diff --git a/.github/workflows/crypto-intrinsics.yml b/.github/workflows/crypto-intrinsics.yml new file mode 100644 index 00000000..8532c048 --- /dev/null +++ b/.github/workflows/crypto-intrinsics.yml @@ -0,0 +1,69 @@ +name: crypto-intrinsics + +on: + pull_request: + paths: + - "crypto-intrinsics/**" + - "Cargo.*" + push: + branches: master + +defaults: + run: + working-directory: crypto-intrinsics + +env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Dwarnings" + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.59.0 # MSRV + target: + - thumbv7em-none-eabi + - wasm32-unknown-unknown + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + - run: cargo build --target ${{ matrix.target }} --release + + test: + strategy: + matrix: + include: + # 32-bit Linux + - target: i686-unknown-linux-gnu + platform: ubuntu-latest + rust: 1.59.0 # MSRV + deps: sudo apt update && sudo apt install gcc-multilib + + # 64-bit Linux + - target: x86_64-unknown-linux-gnu + platform: ubuntu-latest + rust: 1.59.0 # MSRV + + # 64-bit Windows + - target: x86_64-pc-windows-msvc + platform: windows-latest + rust: 1.59.0 # MSRV + + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + profile: minimal + override: true + - run: ${{ matrix.deps }} + - run: cargo test --release diff --git a/.github/workflows/workspace.yml b/.github/workflows/workspace.yml index c778e5c1..2f230a9d 100644 --- a/.github/workflows/workspace.yml +++ b/.github/workflows/workspace.yml @@ -17,7 +17,7 @@ jobs: - uses: RustCrypto/actions/cargo-cache@master - uses: actions-rs/toolchain@v1 with: - toolchain: 1.51.0 # Highest MSRV in repo + toolchain: 1.59.0 # Highest MSRV in repo components: clippy override: true profile: minimal diff --git a/Cargo.lock b/Cargo.lock index d7a9a8c1..f89addb6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,6 +27,10 @@ dependencies = [ "libc", ] +[[package]] +name = "crypto-intrinsics" +version = "0.0.0" + [[package]] name = "dbl" version = "0.3.2" diff --git a/Cargo.toml b/Cargo.toml index 9c2e9066..297cee7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "block-buffer", "collectable", "cpufeatures", + "crypto-intrinsics", "dbl", "hex-literal", "opaque-debug", diff --git a/crypto-intrinsics/Cargo.toml b/crypto-intrinsics/Cargo.toml new file mode 100644 index 00000000..4a2f2ef6 --- /dev/null +++ b/crypto-intrinsics/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "crypto-intrinsics" +description = """ +High-level wrappers for architecture-specific CPU intrinsics which are relevant +to cryptographic use cases, such as the CMOV family, implemented using stable +inline assembly +""" +version = "0.0.0" +authors = ["RustCrypto Developers"] +license = "Apache-2.0 OR MIT" +repository = "https://github.com/RustCrypto/utils/tree/master/crypto-intrinsics" +categories = ["cryptography"] +keywords = ["crypto", "cmov", "intrinsics"] +readme = "README.md" +edition = "2018" # Can't bump to 2021 due to pre-1.56 MSRV crates in the same workspace +rust-version = "1.59" + +[package.metadata.docs.rs] +all-features = true +all-targets = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/crypto-intrinsics/README.md b/crypto-intrinsics/README.md new file mode 100644 index 00000000..29866a37 --- /dev/null +++ b/crypto-intrinsics/README.md @@ -0,0 +1,75 @@ +# [RustCrypto]: CPU Intrinsics + +[![Crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache 2.0/MIT Licensed][license-image] +![MSRV][rustc-image] +[![Build Status][build-image]][build-link] + +High-level wrappers for architecture-specific CPU intrinsics which are not +yet provided via [`core::arch`], implemented using inline assembly. + +[Documentation] + +## About + +Certain CPU instructions which are important for cryptographic applications +are difficult to emit from LLVM due to various complicating factors. + +This crate provides high-level architecture-specific wrappers for these +instructions built on stable inline assembly. No attempts at abstraction +are made, just raw access to specific CPU instructions which are guaranteed +to be emitted verbatim and not optimized away or otherwise rewritten by the +compiler. + +## Supported Instructions + +### `x86` (32-bit) + +- `cmovz` (a.k.a. `cmove`) +- `cmovnz` (a.k.a. `cmovne`) + +### `x86_64` + +- `cmovz` (a.k.a. `cmove`) +- `cmovnz` (a.k.a. `cmovne`) + +## Minimum Supported Rust Version + +Rust **1.59** or newer. + +In the future, we reserve the right to change MSRV (i.e. MSRV is out-of-scope +for this crate's SemVer guarantees), however when we do it will be accompanied by +a minor version bump. + +## License + +Licensed under either of: + +* [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) +* [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/crypto-intrinsics.svg +[crate-link]: https://crates.io/crates/crypto-intrinsics +[docs-image]: https://docs.rs/crypto-intrinsics/badge.svg +[docs-link]: https://docs.rs/crypto-intrinsics/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.51+-blue.svg +[build-image]: https://github.com/RustCrypto/utils/actions/workflows/crypto-intrinsics.yml/badge.svg +[build-link]: https://github.com/RustCrypto/utils/actions/workflows/crypto-intrinsics.yml + +[//]: # (general links) + +[RustCrypto]: https://github.com/RustCrypto +[Documentation]: https://docs.rs/crypto-intrinsics +[`core::arch`]: https://doc.rust-lang.org/core/arch/index.html diff --git a/crypto-intrinsics/src/lib.rs b/crypto-intrinsics/src/lib.rs new file mode 100644 index 00000000..5c302509 --- /dev/null +++ b/crypto-intrinsics/src/lib.rs @@ -0,0 +1,16 @@ +#![no_std] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![doc = include_str!("../README.md")] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg" +)] +#![warn(missing_docs, rust_2018_idioms, unused_qualifications)] + +#[cfg(target_arch = "x86")] +#[cfg_attr(docsrs, doc(cfg(target_arch = "x86")))] +pub mod x86; + +#[cfg(target_arch = "x86_64")] +#[cfg_attr(docsrs, doc(cfg(target_arch = "x86_64")))] +pub mod x86_64; diff --git a/crypto-intrinsics/src/x86.rs b/crypto-intrinsics/src/x86.rs new file mode 100644 index 00000000..4076023d --- /dev/null +++ b/crypto-intrinsics/src/x86.rs @@ -0,0 +1,62 @@ +//! `x86` intrinsics (32-bit) + +use core::arch::asm; + +/// Move if zero. +/// +/// Uses a `test` instruction to check if the given `condition` value is +/// equal to zero, then calls `cmovz` (a.k.a. `cmove`) to conditionally move +/// `src` to `dst` when `condition` is equal to zero. +#[inline(always)] +pub fn cmovz(condition: u32, src: u32, dst: &mut u32) { + unsafe { + asm! { + "test {0}, {0}", + "cmovz {1}, {2}", + in(reg) condition, + inlateout(reg) *dst, + in(reg) src + }; + } +} + +/// Move if not zero. +/// +/// Uses a `test` instruction to check if the given `condition` value is not +/// equal to zero, then calls `cmovnz` (a.k.a. `cmovne`) to conditionally move +/// `src` to `dst` when `condition` is nonzero. +#[inline(always)] +pub fn cmovnz(condition: u32, src: u32, dst: &mut u32) { + unsafe { + asm! { + "test {0}, {0}", + "cmovnz {1}, {2}", + in(reg) condition, + inlateout(reg) *dst, + in(reg) src + }; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn cmovz_works() { + let mut n = 24; + cmovz(42, 42, &mut n); + assert_eq!(n, 24); + cmovz(0, 42, &mut n); + assert_eq!(n, 42); + } + + #[test] + fn cmovnz_works() { + let mut n = 24; + cmovnz(0, 42, &mut n); + assert_eq!(n, 24); + cmovnz(42, 42, &mut n); + assert_eq!(n, 42); + } +} diff --git a/crypto-intrinsics/src/x86_64.rs b/crypto-intrinsics/src/x86_64.rs new file mode 100644 index 00000000..d8ee8508 --- /dev/null +++ b/crypto-intrinsics/src/x86_64.rs @@ -0,0 +1,62 @@ +//! `x86_64` intrinsics + +use core::arch::asm; + +/// Move if zero. +/// +/// Uses a `test` instruction to check if the given `condition` value is +/// equal to zero, then calls `cmovz` (a.k.a. `cmove`) to conditionally move +/// `src` to `dst` when `condition` is equal to zero. +#[inline(always)] +pub fn cmovz(condition: u64, src: u64, dst: &mut u64) { + unsafe { + asm! { + "test {0}, {0}", + "cmovz {1}, {2}", + in(reg) condition, + inlateout(reg) *dst, + in(reg) src + }; + } +} + +/// Move if not zero. +/// +/// Uses a `test` instruction to check if the given `condition` value is not +/// equal to zero, then calls `cmovnz` (a.k.a. `cmovne`) to conditionally move +/// `src` to `dst` when `condition` is nonzero. +#[inline(always)] +pub fn cmovnz(condition: u64, src: u64, dst: &mut u64) { + unsafe { + asm! { + "test {0}, {0}", + "cmovnz {1}, {2}", + in(reg) condition, + inlateout(reg) *dst, + in(reg) src + }; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn cmovz_works() { + let mut n = 24; + cmovz(42, 42, &mut n); + assert_eq!(n, 24); + cmovz(0, 42, &mut n); + assert_eq!(n, 42); + } + + #[test] + fn cmovnz_works() { + let mut n = 24; + cmovnz(0, 42, &mut n); + assert_eq!(n, 24); + cmovnz(42, 42, &mut n); + assert_eq!(n, 42); + } +} diff --git a/dbl/src/lib.rs b/dbl/src/lib.rs index 185a3ffe..fe77c87c 100644 --- a/dbl/src/lib.rs +++ b/dbl/src/lib.rs @@ -29,12 +29,14 @@ pub trait Dbl { /// `block<<1`, otherwise `(block<<1)^C`, where `C` is the non-leading /// coefficients of the lexicographically first irreducible degree-b binary /// polynomial with the minimal number of ones. + #[must_use] fn dbl(self) -> Self; /// Reverse double block. (alternatively: divbide block by x) /// /// If least significant bit of the block equals to zero will return /// `block>>1`, otherwise `(block>>1)^(1<>1)` + #[must_use] fn inv_dbl(self) -> Self; } diff --git a/wycheproof2blb/src/aes_siv.rs b/wycheproof2blb/src/aes_siv.rs index e4d399d5..46b7232b 100644 --- a/wycheproof2blb/src/aes_siv.rs +++ b/wycheproof2blb/src/aes_siv.rs @@ -13,6 +13,7 @@ struct TestSuite { #[derive(Debug, Deserialize)] struct TestGroup { + #[allow(dead_code)] #[serde(flatten)] pub group: wycheproof::Group, #[serde(rename = "keySize")] diff --git a/wycheproof2blb/src/ecdsa.rs b/wycheproof2blb/src/ecdsa.rs index c9877d99..edb69089 100644 --- a/wycheproof2blb/src/ecdsa.rs +++ b/wycheproof2blb/src/ecdsa.rs @@ -13,10 +13,13 @@ struct TestSuite { #[derive(Debug, Deserialize)] struct TestGroup { + #[allow(dead_code)] #[serde(flatten)] pub group: wycheproof::Group, + #[allow(dead_code)] #[serde(rename = "keyDer")] pub key_der: String, + #[allow(dead_code)] #[serde(rename = "keyPem")] pub key_pem: String, pub sha: String, @@ -27,6 +30,7 @@ struct TestGroup { #[derive(Debug, Deserialize)] struct TestKey { curve: String, + #[allow(dead_code)] #[serde(rename = "type")] key_type: String, #[serde(with = "hex_string")] diff --git a/wycheproof2blb/src/ed25519.rs b/wycheproof2blb/src/ed25519.rs index 9ae1e235..89054f33 100644 --- a/wycheproof2blb/src/ed25519.rs +++ b/wycheproof2blb/src/ed25519.rs @@ -13,10 +13,13 @@ struct TestSuite { #[derive(Debug, Deserialize)] struct TestGroup { + #[allow(dead_code)] #[serde(flatten)] pub group: wycheproof::Group, + #[allow(dead_code)] #[serde(rename = "keyDer")] pub key_der: String, + #[allow(dead_code)] #[serde(rename = "keyPem")] pub key_pem: String, pub key: TestKey, diff --git a/wycheproof2blb/src/hkdf.rs b/wycheproof2blb/src/hkdf.rs index 4901d92c..e449b99f 100644 --- a/wycheproof2blb/src/hkdf.rs +++ b/wycheproof2blb/src/hkdf.rs @@ -13,8 +13,10 @@ struct TestSuite { #[derive(Debug, Deserialize)] struct TestGroup { + #[allow(dead_code)] #[serde(flatten)] pub group: wycheproof::Group, + #[allow(dead_code)] #[serde(rename = "keySize")] pub key_size: u32, pub tests: Vec, diff --git a/wycheproof2blb/src/main.rs b/wycheproof2blb/src/main.rs index de1e4c48..11082812 100644 --- a/wycheproof2blb/src/main.rs +++ b/wycheproof2blb/src/main.rs @@ -136,7 +136,7 @@ fn main() { } let mut out_file = std::fs::File::create(out_path).unwrap(); - let blobs: Vec> = infos.into_iter().map(|info| info.data).flatten().collect(); + let blobs: Vec> = infos.into_iter().flat_map(|info| info.data).collect(); let (blb_data, _) = blobby::encode_blobs(&blobs); out_file.write_all(&blb_data).unwrap(); }