Skip to content

Commit f6acf38

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 f6acf38

File tree

7 files changed

+155
-1
lines changed

7 files changed

+155
-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

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use core::arch::asm;
2+
3+
/// Move if zero.
4+
///
5+
/// Uses a `test` instruction to check if the given `condition` value is
6+
/// equal to zero, then calls `cmovz` (a.k.a. `cmove`) to conditionally move
7+
/// `src` to `dst` when `condition` is equal to zero.
8+
#[inline]
9+
pub fn cmovz(condition: u64, src: u64, dst: &mut u64) {
10+
unsafe {
11+
asm!(
12+
"test {0}, {0}",
13+
"cmovz {1}, {2}",
14+
in(reg) condition,
15+
inlateout(reg) *dst,
16+
in(reg) src
17+
);
18+
}
19+
}
20+
21+
/// Move if not zero.
22+
///
23+
/// Uses a `test` instruction to check if the given `condition` value is not
24+
/// equal to zero, then calls `cmovnz` (a.k.a. `cmovne`) to conditionally move
25+
/// `src` to `dst` when `condition` is nonzero.
26+
#[inline]
27+
pub fn cmovnz(condition: u64, src: u64, dst: &mut u64) {
28+
unsafe {
29+
asm!(
30+
"test {0}, {0}",
31+
"cmovnz {1}, {2}",
32+
in(reg) condition,
33+
inlateout(reg) *dst,
34+
in(reg) src
35+
);
36+
}
37+
}
38+
39+
#[cfg(test)]
40+
mod tests {
41+
use super::*;
42+
43+
#[test]
44+
fn cmovz_works() {
45+
let mut n = 24;
46+
cmovz(42, 42, &mut n);
47+
assert_eq!(n, 24);
48+
cmovz(0, 42, &mut n);
49+
assert_eq!(n, 42);
50+
}
51+
52+
#[test]
53+
fn cmovnz_works() {
54+
let mut n = 24;
55+
cmovnz(0, 42, &mut n);
56+
assert_eq!(n, 24);
57+
cmovnz(42, 42, &mut n);
58+
assert_eq!(n, 42);
59+
}
60+
}

0 commit comments

Comments
 (0)