Skip to content

Commit 6f23b0a

Browse files
committed
crypto-intrinsics
Now that inline assembly has been stabilized in Rust 1.59, 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 b437b43 commit 6f23b0a

File tree

10 files changed

+313
-1
lines changed

10 files changed

+313
-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+
- 1.59.0 # 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: 1.59.0 # 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: 1.59.0 # MSRV
53+
54+
# 64-bit Windows
55+
- target: x86_64-pc-windows-msvc
56+
platform: windows-latest
57+
rust: 1.59.0 # 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
@@ -17,7 +17,7 @@ jobs:
1717
- uses: RustCrypto/actions/cargo-cache@master
1818
- uses: actions-rs/toolchain@v1
1919
with:
20-
toolchain: 1.51.0 # Highest MSRV in repo
20+
toolchain: 1.59.0 # Highest MSRV in repo
2121
components: clippy
2222
override: true
2323
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
@@ -4,6 +4,7 @@ members = [
44
"block-buffer",
55
"collectable",
66
"cpufeatures",
7+
"crypto-intrinsics",
78
"dbl",
89
"hex-literal",
910
"opaque-debug",

crypto-intrinsics/Cargo.toml

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[package]
2+
name = "crypto-intrinsics"
3+
description = """
4+
High-level wrappers for architecture-specific CPU intrinsics which are relevant
5+
to cryptographic use cases, such as the CMOV family, implemented using stable
6+
inline assembly
7+
"""
8+
version = "0.0.0"
9+
authors = ["RustCrypto Developers"]
10+
license = "Apache-2.0 OR MIT"
11+
repository = "https://github.com/RustCrypto/utils/tree/master/crypto-intrinsics"
12+
categories = ["cryptography"]
13+
keywords = ["crypto", "cmov", "intrinsics"]
14+
readme = "README.md"
15+
edition = "2018" # Can't bump to 2021 due to pre-1.56 MSRV crates in the same workspace
16+
rust-version = "1.59"
17+
18+
[package.metadata.docs.rs]
19+
all-features = true
20+
all-targets = true
21+
rustdoc-args = ["--cfg", "docsrs"]

crypto-intrinsics/README.md

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# [RustCrypto]: CPU Intrinsics
2+
3+
[![Crate][crate-image]][crate-link]
4+
[![Docs][docs-image]][docs-link]
5+
![Apache 2.0/MIT Licensed][license-image]
6+
![MSRV][rustc-image]
7+
[![Build Status][build-image]][build-link]
8+
9+
High-level wrappers for architecture-specific CPU intrinsics which are not
10+
yet provided via [`core::arch`], implemented using inline assembly.
11+
12+
[Documentation]
13+
14+
## About
15+
16+
Certain CPU instructions which are important for cryptographic applications
17+
are difficult to emit from LLVM due to various complicating factors.
18+
19+
This crate provides high-level architecture-specific wrappers for these
20+
instructions built on stable inline assembly. No attempts at abstraction
21+
are made, just raw access to specific CPU instructions which are guaranteed
22+
to be emitted verbatim and not optimized away or otherwise rewritten by the
23+
compiler.
24+
25+
## Supported Instructions
26+
27+
### `x86` (32-bit)
28+
29+
- `cmovz` (a.k.a. `cmove`)
30+
- `cmovnz` (a.k.a. `cmovne`)
31+
32+
### `x86_64`
33+
34+
- `cmovz` (a.k.a. `cmove`)
35+
- `cmovnz` (a.k.a. `cmovne`)
36+
37+
## Minimum Supported Rust Version
38+
39+
Rust **1.59** or newer.
40+
41+
In the future, we reserve the right to change MSRV (i.e. MSRV is out-of-scope
42+
for this crate's SemVer guarantees), however when we do it will be accompanied by
43+
a minor version bump.
44+
45+
## License
46+
47+
Licensed under either of:
48+
49+
* [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
50+
* [MIT license](http://opensource.org/licenses/MIT)
51+
52+
at your option.
53+
54+
### Contribution
55+
56+
Unless you explicitly state otherwise, any contribution intentionally submitted
57+
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
58+
dual licensed as above, without any additional terms or conditions.
59+
60+
[//]: # (badges)
61+
62+
[crate-image]: https://img.shields.io/crates/v/crypto-intrinsics.svg
63+
[crate-link]: https://crates.io/crates/crypto-intrinsics
64+
[docs-image]: https://docs.rs/crypto-intrinsics/badge.svg
65+
[docs-link]: https://docs.rs/crypto-intrinsics/
66+
[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
67+
[rustc-image]: https://img.shields.io/badge/rustc-1.51+-blue.svg
68+
[build-image]: https://github.com/RustCrypto/utils/actions/workflows/crypto-intrinsics.yml/badge.svg
69+
[build-link]: https://github.com/RustCrypto/utils/actions/workflows/crypto-intrinsics.yml
70+
71+
[//]: # (general links)
72+
73+
[RustCrypto]: https://github.com/RustCrypto
74+
[Documentation]: https://docs.rs/crypto-intrinsics
75+
[`core::arch`]: https://doc.rust-lang.org/core/arch/index.html

crypto-intrinsics/src/lib.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#![no_std]
2+
#![cfg_attr(docsrs, feature(doc_cfg))]
3+
#![doc = include_str!("../README.md")]
4+
#![doc(
5+
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
6+
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
7+
)]
8+
#![warn(missing_docs, rust_2018_idioms, unused_qualifications)]
9+
10+
#[cfg(target_arch = "x86")]
11+
#[cfg_attr(docsrs, doc(cfg(target_arch = "x86")))]
12+
pub mod x86;
13+
14+
#[cfg(target_arch = "x86_64")]
15+
#[cfg_attr(docsrs, doc(cfg(target_arch = "x86_64")))]
16+
pub mod x86_64;

crypto-intrinsics/src/x86.rs

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

crypto-intrinsics/src/x86_64.rs

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

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)