Skip to content

Commit

Permalink
[WIP] crypto-intrinsics
Browse files Browse the repository at this point in the history
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
  • Loading branch information
tarcieri committed Feb 6, 2022
1 parent c603767 commit 1b16dc8
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 1 deletion.
69 changes: 69 additions & 0 deletions .github/workflows/crypto-intrinsics.yml
Original file line number Diff line number Diff line change
@@ -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:
- beta # 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: beta # MSRV
deps: sudo apt update && sudo apt install gcc-multilib

# 64-bit Linux
- target: x86_64-unknown-linux-gnu
platform: ubuntu-latest
rust: beta # MSRV

# 64-bit Windows
- target: x86_64-pc-windows-msvc
platform: windows-latest
rust: beta # 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
2 changes: 1 addition & 1 deletion .github/workflows/workspace.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
toolchain: 1.51.0 # Highest MSRV in repo
toolchain: beta # Highest MSRV in repo
components: clippy
override: true
profile: minimal
Expand Down
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"block-padding",
"collectable",
"cpufeatures",
"crypto-intrinsics",
"dbl",
"hex-literal",
"opaque-debug",
Expand Down
14 changes: 14 additions & 0 deletions crypto-intrinsics/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "crypto-intrinsics"
description = "Reserved for future use"
version = "0.0.0"
authors = ["RustCrypto Developers"]
license = "Apache-2.0 OR MIT"
edition = "2018"
repository = "https://github.com/RustCrypto/traits"
keywords = ["crypto"]
categories = ["cryptography"]

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
6 changes: 6 additions & 0 deletions crypto-intrinsics/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]

#[cfg(target_arch = "x86_64")]
#[cfg_attr(docsrs, doc(cfg(target_arch = "x86_64")))]
pub mod x86_64;
62 changes: 62 additions & 0 deletions crypto-intrinsics/src/x86_64.rs
Original file line number Diff line number Diff line change
@@ -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]
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]
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);
}
}

0 comments on commit 1b16dc8

Please sign in to comment.