Skip to content

Commit 2811bcd

Browse files
committed
[WIP] crypto-intrinsics
With the forthcoming stabilization of inline assembly, I thought we could start putting together a crate which provides wrappers for assembly instructions which are useful for cryptography but lack proper `core::arch` wrappers. More importantly, using inline assembly allows us to provide a sort of black box which LLVM will not interfere with, which is problematic using anything else besides ASM. For example, it's otherwise not possible to correctly emit CMOV instructions on x86 platforms with LLVM because the `x86-cmov-conversion` pass which will rewrite them as branches. For more details, see: https://dsprenkels.com/cmov-conversion.html
1 parent c603767 commit 2811bcd

File tree

8 files changed

+230
-1
lines changed

8 files changed

+230
-1
lines changed
+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
name: crypto-intrinsics
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- "crypto-intrinsics/**"
7+
- "Cargo.*"
8+
push:
9+
branches: master
10+
11+
defaults:
12+
run:
13+
working-directory: crypto-intrinsics
14+
15+
env:
16+
CARGO_INCREMENTAL: 0
17+
RUSTFLAGS: "-Dwarnings"
18+
19+
jobs:
20+
build:
21+
runs-on: ubuntu-latest
22+
strategy:
23+
matrix:
24+
rust:
25+
- beta # MSRV
26+
target:
27+
- thumbv7em-none-eabi
28+
- wasm32-unknown-unknown
29+
steps:
30+
- uses: actions/checkout@v1
31+
- uses: actions-rs/toolchain@v1
32+
with:
33+
profile: minimal
34+
toolchain: ${{ matrix.rust }}
35+
target: ${{ matrix.target }}
36+
override: true
37+
- run: cargo build --target ${{ matrix.target }} --release
38+
39+
test:
40+
strategy:
41+
matrix:
42+
include:
43+
# 32-bit Linux
44+
- target: i686-unknown-linux-gnu
45+
platform: ubuntu-latest
46+
rust: beta # MSRV
47+
deps: sudo apt update && sudo apt install gcc-multilib
48+
49+
# 64-bit Linux
50+
- target: x86_64-unknown-linux-gnu
51+
platform: ubuntu-latest
52+
rust: beta # MSRV
53+
54+
# 64-bit Windows
55+
- target: x86_64-pc-windows-msvc
56+
platform: windows-latest
57+
rust: beta # MSRV
58+
59+
runs-on: ${{ matrix.platform }}
60+
steps:
61+
- uses: actions/checkout@v1
62+
- uses: actions-rs/toolchain@v1
63+
with:
64+
toolchain: ${{ matrix.rust }}
65+
target: ${{ matrix.target }}
66+
profile: minimal
67+
override: true
68+
- run: ${{ matrix.deps }}
69+
- run: cargo test --release

.github/workflows/workspace.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
- uses: actions/checkout@v1
1717
- uses: actions-rs/toolchain@v1
1818
with:
19-
toolchain: 1.51.0 # Highest MSRV in repo
19+
toolchain: beta # Highest MSRV in repo
2020
components: clippy
2121
override: true
2222
profile: minimal

Cargo.lock

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ members = [
55
"block-padding",
66
"collectable",
77
"cpufeatures",
8+
"crypto-intrinsics",
89
"dbl",
910
"hex-literal",
1011
"opaque-debug",

crypto-intrinsics/Cargo.toml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "crypto-intrinsics"
3+
description = "Reserved for future use"
4+
version = "0.0.0"
5+
authors = ["RustCrypto Developers"]
6+
license = "Apache-2.0 OR MIT"
7+
edition = "2018"
8+
repository = "https://github.com/RustCrypto/traits"
9+
keywords = ["crypto"]
10+
categories = ["cryptography"]
11+
12+
[package.metadata.docs.rs]
13+
all-features = true
14+
rustdoc-args = ["--cfg", "docsrs"]

crypto-intrinsics/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#![no_std]
2+
#![cfg_attr(docsrs, feature(doc_cfg))]
3+
4+
#[cfg(target_arch = "x86_64")]
5+
#[cfg_attr(docsrs, doc(cfg(target_arch = "x86_64")))]
6+
pub mod x86_64;

crypto-intrinsics/src/x86_64.rs

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
//! `x86_64` intrinsics
2+
3+
use core::arch::asm;
4+
5+
/// Unsigned integer addition of two operands with carry flag.
6+
///
7+
/// Performs an unsigned addition of the destination operand (first operand),
8+
/// the source operand (second operand) and the carry-flag (CF) and stores the
9+
/// result in the destination operand. The destination operand is a
10+
/// general-purpose register, whereas the source operand can be a
11+
/// general-purpose register or memory location. The state of CF can represent
12+
/// a carry from a previous addition. The instruction sets the CF flag with the
13+
/// carry generated by the unsigned addition of the operands.
14+
///
15+
/// The ADCX instruction is executed in the context of multi-precision addition
16+
/// where we add a series of operands with a carry-chain. At the beginning of a
17+
/// chain of additions, we need to make sure the CF is in a desired initial
18+
/// state. Often, this initial state needs to be 0, which can be achieved with
19+
/// an instruction to zero the CF (e.g. XOR).
20+
#[inline(always)]
21+
pub unsafe fn adcx(a: &mut u64, b: u64) {
22+
asm! {
23+
"adcx {0}, {1}",
24+
inout(reg) *a,
25+
in(reg) b
26+
};
27+
}
28+
29+
/// Unsigned integer addition of two operations with overflow flag.
30+
///
31+
/// Performs an unsigned addition of the destination operand (first operand),
32+
/// the source operand (second operand) and the overflow-flag (OF) and stores
33+
/// the result in the destination operand. The destination operand is a
34+
/// general-purpose register, whereas the source operand can be a
35+
/// general-purpose register or memory location. The state of OF represents a
36+
/// carry from a previous addition. The instruction sets the OF flag with the
37+
/// carry generated by the unsigned addition of the operands.
38+
///
39+
/// The ADOX instruction is executed in the context of multi-precision
40+
/// addition, where we add a series of operands with a carry-chain. At the
41+
/// beginning of a chain of additions, we execute an instruction to zero the OF
42+
/// (e.g. XOR).
43+
#[inline(always)]
44+
pub unsafe fn adox(a: &mut u64, b: u64) {
45+
asm! {
46+
"adox {0}, {1}",
47+
inout(reg) *a,
48+
in(reg) b
49+
};
50+
}
51+
52+
/// Move if zero.
53+
///
54+
/// Uses a `test` instruction to check if the given `condition` value is
55+
/// equal to zero, then calls `cmovz` (a.k.a. `cmove`) to conditionally move
56+
/// `src` to `dst` when `condition` is equal to zero.
57+
#[inline]
58+
pub fn cmovz(condition: u64, src: u64, dst: &mut u64) {
59+
unsafe {
60+
asm! {
61+
"test {0}, {0}",
62+
"cmovz {1}, {2}",
63+
in(reg) condition,
64+
inlateout(reg) *dst,
65+
in(reg) src
66+
};
67+
}
68+
}
69+
70+
/// Move if not zero.
71+
///
72+
/// Uses a `test` instruction to check if the given `condition` value is not
73+
/// equal to zero, then calls `cmovnz` (a.k.a. `cmovne`) to conditionally move
74+
/// `src` to `dst` when `condition` is nonzero.
75+
#[inline]
76+
pub fn cmovnz(condition: u64, src: u64, dst: &mut u64) {
77+
unsafe {
78+
asm! {
79+
"test {0}, {0}",
80+
"cmovnz {1}, {2}",
81+
in(reg) condition,
82+
inlateout(reg) *dst,
83+
in(reg) src
84+
};
85+
}
86+
}
87+
88+
/// Unsigned multiply without affecting flags.
89+
///
90+
/// Performs an unsigned multiplication of the implicit source operand and the
91+
/// specified source operand (the third operand) and stores the low half of the
92+
/// result in the second destination (second operand), the high half of the
93+
/// result in the first destination operand (first operand), without reading or
94+
/// writing the arithmetic flags.
95+
///
96+
/// This enables efficient programming where the software can interleave add
97+
/// with carry operations and multiplications.
98+
///
99+
/// If the first and second operand are identical, it will contain the high
100+
/// half of the multiplication result.
101+
#[inline(always)]
102+
pub unsafe fn mulx(a: u64, b: u64, lo: &mut u64, hi: &mut u64) {
103+
asm! {
104+
"mulx {2}, {1}, {0}",
105+
in(reg) b,
106+
lateout(reg) *lo,
107+
lateout(reg) *hi,
108+
in("rdx") a
109+
};
110+
}
111+
112+
#[cfg(test)]
113+
mod tests {
114+
use super::*;
115+
116+
#[test]
117+
fn cmovz_works() {
118+
let mut n = 24;
119+
cmovz(42, 42, &mut n);
120+
assert_eq!(n, 24);
121+
cmovz(0, 42, &mut n);
122+
assert_eq!(n, 42);
123+
}
124+
125+
#[test]
126+
fn cmovnz_works() {
127+
let mut n = 24;
128+
cmovnz(0, 42, &mut n);
129+
assert_eq!(n, 24);
130+
cmovnz(42, 42, &mut n);
131+
assert_eq!(n, 42);
132+
}
133+
}

dbl/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@ pub trait Dbl {
2929
/// `block<<1`, otherwise `(block<<1)^C`, where `C` is the non-leading
3030
/// coefficients of the lexicographically first irreducible degree-b binary
3131
/// polynomial with the minimal number of ones.
32+
#[must_use]
3233
fn dbl(self) -> Self;
3334

3435
/// Reverse double block. (alternatively: divbide block by x)
3536
///
3637
/// If least significant bit of the block equals to zero will return
3738
/// `block>>1`, otherwise `(block>>1)^(1<<n)^(C>>1)`
39+
#[must_use]
3840
fn inv_dbl(self) -> Self;
3941
}
4042

0 commit comments

Comments
 (0)