Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up backend features and vendor curve25519_dalek_derive #531

Merged
merged 53 commits into from
Jun 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
39c34cb
Vendor import unsafe_target_features as curve25519-dalek-derive
pinkforest Jun 12, 2023
31511c2
Rename vendored unsafe_target_features to curve25519-dalek-derive
pinkforest Jun 13, 2023
4c65862
Plumb curve25519_dalek to curve25519_dalek_derive
pinkforest Jun 13, 2023
6c78bcb
Adjust CI for simd feature-less
pinkforest Jun 13, 2023
96107fc
Remove feature gates from avx2/ifma
pinkforest Jun 13, 2023
1aa5038
De-plub rest of the redundant featuresets
pinkforest Jun 13, 2023
90e92fd
mod vector does not depend on nightly
pinkforest Jun 13, 2023
8dd4800
Found the missing zeroize feature gate
pinkforest Jun 13, 2023
7b68f1e
Fix cfg relay via build.rs
pinkforest Jun 13, 2023
b530a2a
Add buildtime compile diagnostics about backend selection
pinkforest Jun 13, 2023
6af3d03
simd override fix
pinkforest Jun 13, 2023
9923241
fmt
pinkforest Jun 13, 2023
1933ca9
Empty
pinkforest Jun 13, 2023
ead53fb
Fix diagnostics typo
pinkforest Jun 13, 2023
47adb22
Move curve25519-dalek-derive to top level
pinkforest Jun 13, 2023
e367aaa
Remove .gitignore from curve25519-dalek-derive
pinkforest Jun 13, 2023
1ffbb71
Update .gitignore for subdirs
pinkforest Jun 13, 2023
20b9149
Re-label CI desc for simd
pinkforest Jun 13, 2023
ffdefaf
Separate fiat from test default and add serial override test
pinkforest Jun 13, 2023
2b8f0ae
Add build script tests
pinkforest Jun 13, 2023
175bc67
Documentation changes
pinkforest Jun 13, 2023
b4114cc
Fix build test
pinkforest Jun 13, 2023
07d0755
Ensure no incremental compilation for build script tests
pinkforest Jun 13, 2023
a3d3c65
Remove redundant LICENSE from derive crate
pinkforest Jun 13, 2023
eeb4b6c
Clean up backend selection doc further
pinkforest Jun 16, 2023
ee82d44
Better table column format
pinkforest Jun 16, 2023
1275f26
Wording fix
pinkforest Jun 16, 2023
4c9ef40
Relative and wording fix
pinkforest Jun 16, 2023
cf013ec
Relative links are mess
pinkforest Jun 16, 2023
1c34768
Relative link mess
pinkforest Jun 16, 2023
cf9ba9e
Vector needs to be included for docsrs
pinkforest Jun 16, 2023
731ff48
docsrs is assumed AVX2 capable so this is not needed.
pinkforest Jun 16, 2023
c2f219c
Reduce confusing implementation detail from pub doc
pinkforest Jun 16, 2023
4fe1079
Target not platform
pinkforest Jun 16, 2023
35b3cef
Reduce gates in enum and allow dead_code in it
pinkforest Jun 16, 2023
95dfca0
The enum needs to be gated for now.
pinkforest Jun 16, 2023
dc507b7
Default backend is serial not auto
pinkforest Jun 16, 2023
0bc5de2
Adjust build script tests to serial not auto default
pinkforest Jun 16, 2023
b11fa4d
Fix simd default on backend
pinkforest Jun 17, 2023
f41ab86
Fix wording about instruction sets
pinkforest Jun 17, 2023
edeb5c2
Disable simd related features unless simd was determined via build
pinkforest Jun 17, 2023
840f8e5
Play the fmt game
pinkforest Jun 17, 2023
b87ad6b
Simply backend gates via build
pinkforest Jun 17, 2023
2755aa3
Revert "Simply backend gates via build"
pinkforest Jun 17, 2023
9c42cc9
Correct gating for feature enable
pinkforest Jun 17, 2023
91d0d6d
Add note and test about the override warning when unsuccesfull
pinkforest Jun 17, 2023
1ac7b2c
Reduce complexity in build gating via compile_error
pinkforest Jun 17, 2023
770a394
Doc typo
pinkforest Jun 17, 2023
2eb2b17
Cosmetic panic error message fix
pinkforest Jun 17, 2023
8bf3439
Play the fmt game again
pinkforest Jun 17, 2023
1671d80
Refer to issue/532 around intended compile_error
pinkforest Jun 17, 2023
1bcbda5
Squash annoying typo
pinkforest Jun 17, 2023
919c8b4
Some README cleanup
rozbb Jun 20, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
65 changes: 58 additions & 7 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ env:
RUSTFLAGS: '-D warnings'

jobs:
test:
test-auto:
runs-on: ubuntu-latest
strategy:
matrix:
Expand All @@ -38,10 +38,58 @@ jobs:
- run: cargo test --target ${{ matrix.target }} --features digest
- run: cargo test --target ${{ matrix.target }} --features rand_core
- run: cargo test --target ${{ matrix.target }} --features serde

test-fiat:
runs-on: ubuntu-latest
strategy:
matrix:
include:
# 32-bit target
- target: i686-unknown-linux-gnu
deps: sudo apt update && sudo apt install gcc-multilib

# 64-bit target
- target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- run: rustup target add ${{ matrix.target }}
- run: ${{ matrix.deps }}
- env:
RUSTFLAGS: '--cfg curve25519_dalek_backend="fiat"'
run: cargo test --target ${{ matrix.target }}

test-serial:
runs-on: ubuntu-latest
strategy:
matrix:
include:
# 32-bit target
- target: i686-unknown-linux-gnu
deps: sudo apt update && sudo apt install gcc-multilib

# 64-bit target
- target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- run: rustup target add ${{ matrix.target }}
- run: ${{ matrix.deps }}
- env:
RUSTFLAGS: '--cfg curve25519_dalek_backend="serial"'
run: cargo test --target ${{ matrix.target }}

build-script:
name: Test Build Script
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
targets: wasm32-unknown-unknown,x86_64-unknown-linux-gnu,i686-unknown-linux-gnu
- run: bash tests/build_tests.sh

build-nostd:
name: Build on no_std target (thumbv7em-none-eabi)
runs-on: ubuntu-latest
Expand All @@ -55,8 +103,8 @@ jobs:
- run: cargo build --target thumbv7em-none-eabi --release
- run: cargo build --target thumbv7em-none-eabi --release --features serde

test-simd-native:
name: Test simd backend (native)
test-simd-nightly:
name: Test simd backend (nightly)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand All @@ -66,20 +114,23 @@ jobs:
# 1) build all of the x86_64 SIMD code,
# 2) run all of the SIMD-specific tests that the test runner supports,
# 3) run all of the normal tests using the best available SIMD backend.
# This should automatically pick up the simd backend in a x84_64 runner
RUSTFLAGS: '-C target_cpu=native'
run: cargo test --features simd --target x86_64-unknown-linux-gnu
run: cargo test --target x86_64-unknown-linux-gnu

test-simd-avx2:
name: Test simd backend (avx2)
test-simd-stable:
name: Test simd backend (stable)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- env:
# This will run AVX2-specific tests and run all of the normal tests
# with the AVX2 backend, even if the runner supports AVX512.
# This should automatically pick up the simd backend in a x86_64 runner
# It should pick AVX2 due to stable toolchain used since AVX512 requires nigthly
RUSTFLAGS: '-C target_feature=+avx2'
run: cargo test --no-default-features --features alloc,precomputed-tables,zeroize,simd_avx2 --target x86_64-unknown-linux-gnu
run: cargo test --no-default-features --features alloc,precomputed-tables,zeroize --target x86_64-unknown-linux-gnu

build-docs:
name: Build docs
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
*/target/*
target
Cargo.lock

*/Cargo.lock
build*.txt
*~
\#*
.\#*
Expand Down
14 changes: 3 additions & 11 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ digest = { version = "0.10", default-features = false, optional = true }
subtle = { version = "2.3.0", default-features = false }
serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] }
zeroize = { version = "1", default-features = false, optional = true }
unsafe_target_feature = { version = "= 0.1.1", optional = true }

[target.'cfg(target_arch = "x86_64")'.dependencies]
cpufeatures = "0.2.6"
Expand All @@ -62,20 +61,13 @@ cpufeatures = "0.2.6"
fiat-crypto = "0.1.19"

[features]
default = ["alloc", "precomputed-tables", "zeroize", "simd"]
default = ["alloc", "precomputed-tables", "zeroize"]
alloc = ["zeroize?/alloc"]
precomputed-tables = []
legacy_compatibility = []

# Whether to allow the use of the AVX2 SIMD backend.
simd_avx2 = ["unsafe_target_feature"]

# Whether to allow the use of the AVX512 SIMD backend.
# (Note: This requires Rust nightly; on Rust stable this feature will be ignored.)
simd_avx512 = ["unsafe_target_feature"]

# A meta-feature to allow all SIMD backends to be used.
simd = ["simd_avx2", "simd_avx512"]
[target.'cfg(all(not(curve25519_dalek_backend = "fiat"), not(curve25519_dalek_backend = "serial"), target_arch = "x86_64"))'.dependencies]
curve25519-dalek-derive = { version = "0.1", path = "curve25519-dalek-derive" }

[profile.dev]
opt-level = 2
72 changes: 34 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,6 @@ curve25519-dalek = "4.0.0-rc.2"
| `alloc` | ✓ | Enables Edwards and Ristretto multiscalar multiplication, batch scalar inversion, and batch Ristretto double-and-compress. Also enables `zeroize`. |
| `zeroize` | ✓ | Enables [`Zeroize`][zeroize-trait] for all scalar and curve point types. |
| `precomputed-tables` | ✓ | Includes precomputed basepoint multiplication tables. This speeds up `EdwardsPoint::mul_base` and `RistrettoPoint::mul_base` by ~4x, at the cost of ~30KB added to the code size. |
| `simd_avx2` | ✓ | Allows the AVX2 SIMD backend to be used, if available. |
| `simd_avx512` | ✓ | Allows the AVX512 SIMD backend to be used, if available. |
| `simd` | ✓ | Allows every SIMD backend to be used, if available. |
| `rand_core` | | Enables `Scalar::random` and `RistrettoPoint::random`. This is an optional dependency whose version is not subject to SemVer. See [below](#public-api-semver-exemptions) for more details. |
| `digest` | | Enables `RistrettoPoint::{from_hash, hash_from_bytes}` and `Scalar::{from_hash, hash_from_bytes}`. This is an optional dependency whose version is not subject to SemVer. See [below](#public-api-semver-exemptions) for more details. |
| `serde` | | Enables `serde` serialization/deserialization for all the point and scalar types. |
Expand Down Expand Up @@ -90,25 +87,27 @@ latest breaking changes in high level are below:

This release also does a lot of dependency updates and relaxations to unblock upstream build issues.

### 4.0.0 - Open Breaking Changes
# Backends

See tracking issue: [curve25519-dalek/issues/521](https://github.com/dalek-cryptography/curve25519-dalek/issues/521)
Curve arithmetic is implemented and used by one of the following backends:

# Backends
| Backend | Selection | Implementation | Bits / Word sizes |
| :--- | :--- | :--- | :--- |
| `serial` | Automatic | An optimized, non-parllel implementation | `32` and `64` |
| `fiat` | Manual | Formally verified field arithmetic from [fiat-crypto] | `32` and `64` |
| `simd` | Automatic | Intel AVX2 / AVX512 IFMA accelerated backend | `64` only |

Curve arithmetic is implemented and used by selecting one of the following backends:
At runtime, `curve25519-dalek` selects an arithmetic backend from the set of backends it was compiled to support. For Intel x86-64 targets, unless otherwise specified, it will build itself with `simd` support, and default to `serial` at runtime if the appropriate CPU features aren't detected. See [SIMD backend] for more details.

| Backend | Implementation | Target backends |
| :--- | :--- | :--- |
| `[default]` | Automatic runtime backend selection (either serial or SIMD) | `u32` <br/> `u64` <br/> `avx2` <br/> `avx512` |
| `fiat` | Formally verified field arithmetic from [fiat-crypto] | `fiat_u32` <br/> `fiat_u64` |
In the future, `simd` backend may be extended to cover more instruction sets. This change will be non-breaking as this is considered as implementation detail.

To choose a backend other than the `[default]` backend, set the
environment variable:
## Manual Backend Override

You can force the crate to compile with specific backend support, e.g., `serial` for x86-64 targets to save code size, or `fiat` to force the runtime to use verified code. To do this, set the environment variable:
```sh
RUSTFLAGS='--cfg curve25519_dalek_backend="BACKEND"'
```
where `BACKEND` is `fiat`. Equivalently, you can write to
Equivalently, you can write to
`~/.cargo/config`:
```toml
[build]
Expand All @@ -117,53 +116,49 @@ rustflags = ['--cfg=curve25519_dalek_backend="BACKEND"']
More info [here](https://doc.rust-lang.org/cargo/reference/config.html#buildrustflags).

Note for contributors: The target backends are not entirely independent of each
other. The SIMD backend directly depends on parts of the the `u64` backend to
other. The [SIMD backend] directly depends on parts of the serial backend to
function.

## Word size for serial backends
## Bits / Word size

`curve25519-dalek` will automatically choose the word size for the `[default]`
and `fiat` serial backends, based on the build target. For example, building
for a 64-bit machine, the default `u64` target backend is automatically chosen
when the `[default]` backend is selected, and `fiat_u64` is chosen when the
`fiat backend is selected.
`curve25519-dalek` will automatically choose the word size for the `fiat` and
`serial` backends, based on the build target.
For example, building for a 64-bit machine, the default 64 bit word size is
automatically chosen when either the `serial` or `fiat` backend is selected.

Backend word size can be overridden for `[default]` and `fiat` by setting the
In some targets it might be required to override the word size for better
performance.
Backend word size can be overridden for `serial` and `fiat` by setting the
environment variable:
```sh
RUSTFLAGS='--cfg curve25519_dalek_bits="SIZE"'
```
where `SIZE` is `32` or `64`. As in the above section, this can also be placed
`SIZE` is `32` or `64`. As in the above section, this can also be placed
in `~/.cargo/config`.

**NOTE:** Using a word size of 32 will automatically disable SIMD support.
Note: The [SIMD backend] requires a word size of 64 bits. Attempting to set bits=32 and backend=`simd` will yield a compile error.

### Cross-compilation

Because backend selection is done by target, cross-compiling will select the
correct word size automatically. For example, on an x86-64 Linux machine,
`curve25519-dalek` will use the `u32` target backend if the following is run:
Because backend selection is done by target, cross-compiling will select the correct word size automatically. For example, if a x86-64 Linux machine runs the following commands, `curve25519-dalek` will be compiled with the 32-bit `serial` backend.
```console
$ sudo apt install gcc-multilib # (or whatever package manager you use)
$ rustup target add i686-unknown-linux-gnu
$ cargo build --target i686-unknown-linux-gnu
```

## SIMD target backends
## SIMD backend

The SIMD target backend selection is done automatically at runtime depending
on the available CPU features, provided the appropriate feature flag is enabled.
The specific SIMD backend (AVX512 / AVX2 / `serial` default) is selected automatically at runtime, depending on the currently available CPU features, and whether Rust nightly is being used for compilation. The precise conditions are specified below.

You can also specify an appropriate `-C target_feature` to build a binary
which assumes the required SIMD instructions are always available.
For a given CPU feature, you can also specify an appropriate `-C target_feature` to build a binary which assumes the required SIMD instructions are always available. Don't do this if you don't have a good reason.

| Backend | Feature flag | `RUSTFLAGS` | Requires nightly? |
| :--- | :--- | :--- | :--- |
| avx2 | `simd_avx2` | `-C target_feature=+avx2` | no |
| avx512 | `simd_avx512` | `-C target_feature=+avx512ifma,+avx512vl` | yes |
| Backend | `RUSTFLAGS` | Requires nightly? |
| :--- | :--- | :--- |
| avx2 | `-C target_feature=+avx2` | no |
| avx512 | `-C target_feature=+avx512ifma,+avx512vl` | yes |

The AVX512 backend requires Rust nightly. When compiled on a non-nightly
compiler it will always be disabled.
If compiled on a non-nightly compiler, `curve25519-dalek` will not include AVX512 code, and therefore will never select it at runtime.

# Documentation

Expand Down Expand Up @@ -326,3 +321,4 @@ contributions.
[semver]: https://semver.org/spec/v2.0.0.html
[rngcorestd]: https://github.com/rust-random/rand/tree/7aa25d577e2df84a5156f824077bb7f6bdf28d97/rand_core#crate-features
[zeroize-trait]: https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html
[SIMD backend]: #simd-backend
33 changes: 33 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![deny(clippy::unwrap_used, dead_code)]

#[allow(non_camel_case_types)]
#[derive(PartialEq, Debug)]
enum DalekBits {
Dalek32,
Dalek64,
Expand Down Expand Up @@ -34,6 +35,38 @@ fn main() {
// so for those we want to apply the `#[allow(unused_unsafe)]` attribute to get rid of that warning.
println!("cargo:rustc-cfg=allow_unused_unsafe");
}

let target_arch = match std::env::var("CARGO_CFG_TARGET_ARCH") {
Ok(arch) => arch,
_ => "".to_string(),
};

// Backend overrides / defaults
let curve25519_dalek_backend =
match std::env::var("CARGO_CFG_CURVE25519_DALEK_BACKEND").as_deref() {
Ok("fiat") => "fiat",
Ok("serial") => "serial",
Ok("simd") => {
// simd can only be enabled on x86_64 & 64bit target_pointer_width
match is_capable_simd(&target_arch, curve25519_dalek_bits) {
true => "simd",
// If override is not possible this must result to compile error
// See: issues/532
false => panic!("Could not override curve25519_dalek_backend to simd"),
}
}
// default between serial / simd (if potentially capable)
_ => match is_capable_simd(&target_arch, curve25519_dalek_bits) {
true => "simd",
false => "serial",
},
};
println!("cargo:rustc-cfg=curve25519_dalek_backend=\"{curve25519_dalek_backend}\"");
}

// Is the target arch & curve25519_dalek_bits potentially simd capable ?
fn is_capable_simd(arch: &str, bits: DalekBits) -> bool {
arch == "x86_64" && bits == DalekBits::Dalek64
}

// Deterministic cfg(curve25519_dalek_bits) when this is not explicitly set.
Expand Down
19 changes: 19 additions & 0 deletions curve25519-dalek-derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "curve25519-dalek-derive"
version = "0.1.0"
edition = "2021"

repository = "https://github.com/dalek-cryptography/curve25519-dalek"
homepage = "https://github.com/dalek-cryptography/curve25519-dalek"
documentation = "https://docs.rs/curve25519-dalek-derive"
license = "MIT/Apache-2.0"
readme = "README.md"
description = "curve25519-dalek Derives"

[lib]
proc-macro = true

[dependencies]
proc-macro2 = "1.0.53"
quote = "1.0.26"
syn = { version = "2.0.8", features = ["full"] }