Skip to content

Commit 081f632

Browse files
authored
Implement simplified backend selection (#428)
As proposed in #414, this commit changes the backend selection approach, introspecting `target_pointer_width` to select `u32_backend` vs `u64_backend` (or `fiat_u32_backend`/`fiat_u64_backend` if the `fiat_backend` feature is enabled). This helps eliminate the use of non-additive features, and also the rather confusing errors that happen if multiple backends are selected (i.e. thousands of lines of rustc errors). The selection logic checks if `target_pointer_width = "64"` and uses the 64-bit backend, or falls back to the 32-bit backend otherwise. This means the crate will always have a valid backend regardless of the pointer width, although there may be odd edge cases for exotic platforms which would optimally use the 64-bit backend but have a non-"64" target pointer width for whatever reason. We can handle those cases as they come up.
1 parent 977eb0d commit 081f632

File tree

8 files changed

+162
-150
lines changed

8 files changed

+162
-150
lines changed

.github/workflows/rust.yml

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,24 @@ env:
1010
CARGO_TERM_COLOR: always
1111

1212
jobs:
13-
test-u32:
14-
name: Test u32 backend
13+
test:
1514
runs-on: ubuntu-latest
16-
steps:
17-
- uses: actions/checkout@v3
18-
- uses: dtolnay/rust-toolchain@stable
19-
- run: cargo test --no-default-features --features "std u32_backend"
15+
strategy:
16+
matrix:
17+
include:
18+
# 32-bit target
19+
- target: i686-unknown-linux-gnu
20+
deps: sudo apt update && sudo apt install gcc-multilib
2021

21-
test-u64:
22-
name: Test u64 backend
23-
runs-on: ubuntu-latest
22+
# 64-bit target
23+
- target: x86_64-unknown-linux-gnu
2424
steps:
25-
- uses: actions/checkout@v3
26-
- uses: dtolnay/rust-toolchain@stable
27-
- run: cargo test --no-default-features --features "std u64_backend"
25+
- uses: actions/checkout@v3
26+
- uses: dtolnay/rust-toolchain@stable
27+
- run: rustup target add ${{ matrix.target }}
28+
- run: ${{ matrix.deps }}
29+
- run: cargo test --target ${{ matrix.target }}
30+
- run: cargo test --target ${{ matrix.target }} --features fiat_backend
2831

2932
build-simd:
3033
name: Build simd backend (nightly)
@@ -54,7 +57,9 @@ jobs:
5457
steps:
5558
- uses: actions/checkout@v3
5659
- uses: dtolnay/rust-toolchain@stable
57-
- run: cargo test --lib --no-default-features --features "alloc u32_backend"
60+
- run: rustup target add i686-unknown-linux-gnu
61+
- run: sudo apt update && sudo apt install gcc-multilib
62+
- run: cargo test --lib --no-default-features --features alloc --target i686-unknown-linux-gnu
5863

5964
nightly:
6065
name: Test nightly compiler
@@ -72,11 +77,11 @@ jobs:
7277
# First run `cargo +nightly -Z minimal-verisons check` in order to get a
7378
# Cargo.lock with the oldest possible deps
7479
- uses: dtolnay/rust-toolchain@nightly
75-
- run: cargo -Z minimal-versions check --no-default-features --features "fiat_u64_backend serde"
80+
- run: cargo -Z minimal-versions check --no-default-features --features fiat_backend,serde
7681
# Now check that `cargo build` works with respect to the oldest possible
7782
# deps and the stated MSRV
7883
- uses: dtolnay/[email protected]
79-
- run: cargo build --no-default-features --features "fiat_u64_backend serde"
84+
- run: cargo build --no-default-features --features fiat_backend,serde
8085

8186
bench:
8287
name: Check that benchmarks compile

Cargo.toml

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ name = "dalek_benchmarks"
4343
harness = false
4444

4545
[dependencies]
46+
cfg-if = "1"
4647
rand_core = { version = "0.6", default-features = false }
4748
digest = { version = "0.10", default-features = false }
4849
subtle = { version = "^2.2.1", default-features = false }
@@ -55,20 +56,11 @@ fiat-crypto = { version = "0.1.6", optional = true}
5556

5657
[features]
5758
nightly = ["subtle/nightly"]
58-
default = ["std", "u64_backend"]
59+
default = ["std"]
5960
std = ["alloc", "subtle/std", "rand_core/std"]
6061
alloc = ["zeroize/alloc"]
6162

62-
# The u32 backend uses u32s with u64 products.
63-
u32_backend = []
64-
# The u64 backend uses u64s with u128 products.
65-
u64_backend = []
66-
# fiat-u64 backend (with formally-verified field arith) uses u64s with u128 products.
67-
fiat_u64_backend = ["fiat-crypto"]
68-
# fiat-u32 backend (with formally-verified field arith) uses u32s with u64 products.
69-
fiat_u32_backend = ["fiat-crypto"]
63+
# fiat-crypto backend with formally-verified field arithmetic
64+
fiat_backend = ["fiat-crypto"]
7065
# The SIMD backend uses parallel formulas, using either AVX2 or AVX512-IFMA.
71-
simd_backend = ["nightly", "u64_backend", "packed_simd"]
72-
# DEPRECATED: this is now an alias for `simd_backend` and may be removed
73-
# in some future release.
74-
avx2_backend = ["simd_backend"]
66+
simd_backend = ["nightly", "packed_simd"]

README.md

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,13 @@ version, and in terms of non-breaking changes it includes:
8282
### 4.x (current alpha)
8383

8484
The `4.x` series has an API largely unchanged from `3.x`, with a breaking change
85-
to update the `rand` dependency crates. It also requires including a new trait,
86-
`use curve25519_dalek::traits::BasepointTable`, whenever using `EdwardsBasepointTable`
87-
or `RistrettoBasepointTable`.
85+
to update the `rand` dependency crates.
86+
87+
It also requires including a new trait,
88+
`use curve25519_dalek::traits::BasepointTable`, whenever using
89+
`EdwardsBasepointTable` or `RistrettoBasepointTable`.
90+
91+
Backend selection has also been updated to be more automatic. See below.
8892

8993
# Backends and Features
9094

@@ -98,24 +102,26 @@ Curve arithmetic is implemented using one of the following backends:
98102
* a `u64` backend using serial formulas and `u128` products;
99103
* an `avx2` backend using [parallel formulas][parallel_doc] and `avx2` instructions (sets speed records);
100104
* an `ifma` backend using [parallel formulas][parallel_doc] and `ifma` instructions (sets speed records);
101-
102-
By default the `u64` backend is selected. To select a specific backend, use:
103-
```sh
104-
cargo build --no-default-features --features "std u32_backend"
105-
cargo build --no-default-features --features "std u64_backend"
106-
# Requires nightly, RUSTFLAGS="-C target_feature=+avx2" to use avx2
107-
cargo build --no-default-features --features "std simd_backend"
108-
# Requires nightly, RUSTFLAGS="-C target_feature=+avx512ifma" to use ifma
109-
cargo build --no-default-features --features "std simd_backend"
110-
```
111-
Crates using `curve25519-dalek` can either select a backend on behalf of their
112-
users, or expose feature flags that control the `curve25519-dalek` backend.
105+
* a `fiat` backend using formally verified field arithmetic from [fiat-crypto];
113106

114107
The `std` feature is enabled by default, but it can be disabled for no-`std`
115108
builds using `--no-default-features`. Note that this requires explicitly
116109
selecting an arithmetic backend using one of the `_backend` features.
117110
If no backend is selected, compilation will fail.
118111

112+
## Backend selection
113+
114+
Backend selection is done automatically. E.g., if you're compiling on a
115+
64-bit machine, then the `u64` backend is automatically chosen. And
116+
if the `fiat_backend` feature is set, then the fiat `u64` backend is
117+
chosen.
118+
119+
If you need a `u32` backend on a `u64` machine, then simple
120+
cross-compiling will work on an x86-64 Linux machine:
121+
122+
* `sudo apt install gcc-multilib` (or whatever package manager you use)
123+
* `rustup target add i686-unknown-linux-gnu`
124+
* `cargo build --target i686-unknown-linux-gnu`
119125

120126
# Minimum Supported Rust Version
121127

@@ -166,11 +172,10 @@ compiled with appropriate `target_feature`s, so this cannot occur.
166172
Benchmarks are run using [`criterion.rs`][criterion]:
167173

168174
```sh
169-
cargo bench --no-default-features --features "std u32_backend"
170-
cargo bench --no-default-features --features "std u64_backend"
175+
cargo bench --no-default-features
171176
# Uses avx2 or ifma only if compiled for an appropriate target.
172177
export RUSTFLAGS="-C target_cpu=native"
173-
cargo bench --no-default-features --features "std simd_backend"
178+
cargo +nightly bench --no-default-features --features simd_backend
174179
```
175180

176181
Performance is a secondary goal behind correctness, safety, and
@@ -227,10 +232,9 @@ optimised batch inversion was contributed by Sean Bowe and Daira Hopwood.
227232

228233
The `no_std` and `zeroize` support was contributed by Tony Arcieri.
229234

230-
The formally verified backends, `fiat_u32_backend` and `fiat_u64_backend`, which
231-
integrate with the Rust generated by the
232-
[Fiat Crypto project](https://github.com/mit-plv/fiat-crypto) were contributed
233-
by François Garillot.
235+
The formally verified `fiat_backend` integrates Rust code generated by the
236+
[Fiat Crypto project](https://github.com/mit-plv/fiat-crypto) and was
237+
contributed by François Garillot.
234238

235239
Thanks also to Ashley Hauck, Lucas Salibian, Manish Goregaokar, Jack Grigg,
236240
Pratyush Mishra, Michael Rosenberg, and countless others for their
@@ -244,3 +248,4 @@ contributions.
244248
[criterion]: https://github.com/japaric/criterion.rs
245249
[parallel_doc]: https://doc-internal.dalek.rs/curve25519_dalek/backend/vector/avx2/index.html
246250
[subtle_doc]: https://doc.dalek.rs/subtle/
251+
[fiat-crypto]: https://github.com/mit-plv/fiat-crypto

src/backend/mod.rs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,6 @@
3434
//! The [`vector`] backend is selected by the `simd_backend` cargo
3535
//! feature; it uses the [`serial`] backend for non-vectorized operations.
3636
37-
#[cfg(not(any(
38-
feature = "u32_backend",
39-
feature = "u64_backend",
40-
feature = "fiat_u32_backend",
41-
feature = "fiat_u64_backend",
42-
feature = "simd_backend",
43-
)))]
44-
compile_error!(
45-
"no curve25519-dalek backend cargo feature enabled! \
46-
please enable one of: u32_backend, u64_backend, fiat_u32_backend, fiat_u64_backend, simd_backend"
47-
);
48-
4937
pub mod serial;
5038

5139
#[cfg(any(

src/backend/serial/mod.rs

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,32 +19,24 @@
1919
//!
2020
//! When the vector backend is enabled, the field and scalar
2121
//! implementations are still used for non-vectorized operations.
22-
//!
23-
//! Note: at this time the `u32` and `u64` backends cannot be built
24-
//! together.
25-
26-
#[cfg(not(any(
27-
feature = "u32_backend",
28-
feature = "u64_backend",
29-
feature = "fiat_u32_backend",
30-
feature = "fiat_u64_backend"
31-
)))]
32-
compile_error!(
33-
"no curve25519-dalek backend cargo feature enabled! \
34-
please enable one of: u32_backend, u64_backend, fiat_u32_backend, fiat_u64_backend"
35-
);
3622
37-
#[cfg(feature = "u32_backend")]
38-
pub mod u32;
23+
use cfg_if::cfg_if;
3924

40-
#[cfg(feature = "u64_backend")]
41-
pub mod u64;
25+
cfg_if! {
26+
if #[cfg(feature = "fiat_backend")] {
27+
#[cfg(not(target_pointer_width = "64"))]
28+
pub mod fiat_u32;
4229

43-
#[cfg(feature = "fiat_u32_backend")]
44-
pub mod fiat_u32;
30+
#[cfg(target_pointer_width = "64")]
31+
pub mod fiat_u64;
32+
} else {
33+
#[cfg(not(target_pointer_width = "64"))]
34+
pub mod u32;
4535

46-
#[cfg(feature = "fiat_u64_backend")]
47-
pub mod fiat_u64;
36+
#[cfg(target_pointer_width = "64")]
37+
pub mod u64;
38+
}
39+
}
4840

4941
pub mod curve_models;
5042

src/constants.rs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,27 @@
2828
2929
#![allow(non_snake_case)]
3030

31+
use cfg_if::cfg_if;
32+
3133
use crate::edwards::CompressedEdwardsY;
3234
use crate::montgomery::MontgomeryPoint;
3335
use crate::ristretto::CompressedRistretto;
3436
use crate::ristretto::RistrettoPoint;
3537
use crate::scalar::Scalar;
3638

37-
#[cfg(feature = "fiat_u32_backend")]
38-
pub use crate::backend::serial::fiat_u32::constants::*;
39-
#[cfg(feature = "fiat_u64_backend")]
40-
pub use crate::backend::serial::fiat_u64::constants::*;
41-
#[cfg(feature = "u32_backend")]
42-
pub use crate::backend::serial::u32::constants::*;
43-
#[cfg(feature = "u64_backend")]
44-
pub use crate::backend::serial::u64::constants::*;
39+
cfg_if! {
40+
if #[cfg(feature = "fiat_backend")] {
41+
#[cfg(not(target_pointer_width = "64"))]
42+
pub use crate::backend::serial::fiat_u32::constants::*;
43+
#[cfg(target_pointer_width = "64")]
44+
pub use crate::backend::serial::fiat_u64::constants::*;
45+
} else {
46+
#[cfg(not(target_pointer_width = "64"))]
47+
pub use crate::backend::serial::u32::constants::*;
48+
#[cfg(target_pointer_width = "64")]
49+
pub use crate::backend::serial::u64::constants::*;
50+
}
51+
}
4552

4653
/// The Ed25519 basepoint, in `CompressedEdwardsY` format.
4754
///
@@ -142,7 +149,7 @@ mod test {
142149

143150
/// Test that d = -121665/121666
144151
#[test]
145-
#[cfg(feature = "u32_backend")]
152+
#[cfg(all(not(target_pointer_width = "64"), not(feature = "fiat_backend")))]
146153
fn test_d_vs_ratio() {
147154
use crate::backend::serial::u32::field::FieldElement2625;
148155
let a = -&FieldElement2625([121665, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
@@ -155,7 +162,7 @@ mod test {
155162

156163
/// Test that d = -121665/121666
157164
#[test]
158-
#[cfg(feature = "u64_backend")]
165+
#[cfg(all(target_pointer_width = "64", not(feature = "fiat_backend")))]
159166
fn test_d_vs_ratio() {
160167
use crate::backend::serial::u64::field::FieldElement51;
161168
let a = -&FieldElement51([121665, 0, 0, 0, 0]);

src/field.rs

Lines changed: 48 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
2626
use core::cmp::{Eq, PartialEq};
2727

28+
use cfg_if::cfg_if;
29+
2830
use subtle::Choice;
2931
use subtle::ConditionallyNegatable;
3032
use subtle::ConditionallySelectable;
@@ -33,40 +35,52 @@ use subtle::ConstantTimeEq;
3335
use crate::backend;
3436
use crate::constants;
3537

36-
#[cfg(feature = "fiat_u32_backend")]
37-
pub use backend::serial::fiat_u32::field::*;
38-
#[cfg(feature = "fiat_u64_backend")]
39-
pub use backend::serial::fiat_u64::field::*;
40-
/// A `FieldElement` represents an element of the field
41-
/// \\( \mathbb Z / (2\^{255} - 19)\\).
42-
///
43-
/// The `FieldElement` type is an alias for one of the platform-specific
44-
/// implementations.
45-
/// Using formally-verified field arithmetic from fiat-crypto
46-
#[cfg(feature = "fiat_u32_backend")]
47-
pub type FieldElement = backend::serial::fiat_u32::field::FieldElement2625;
48-
#[cfg(feature = "fiat_u64_backend")]
49-
pub type FieldElement = backend::serial::fiat_u64::field::FieldElement51;
50-
51-
#[cfg(feature = "u64_backend")]
52-
pub use crate::backend::serial::u64::field::*;
53-
/// A `FieldElement` represents an element of the field
54-
/// \\( \mathbb Z / (2\^{255} - 19)\\).
55-
///
56-
/// The `FieldElement` type is an alias for one of the platform-specific
57-
/// implementations.
58-
#[cfg(feature = "u64_backend")]
59-
pub type FieldElement = backend::serial::u64::field::FieldElement51;
60-
61-
#[cfg(feature = "u32_backend")]
62-
pub use backend::serial::u32::field::*;
63-
/// A `FieldElement` represents an element of the field
64-
/// \\( \mathbb Z / (2\^{255} - 19)\\).
65-
///
66-
/// The `FieldElement` type is an alias for one of the platform-specific
67-
/// implementations.
68-
#[cfg(feature = "u32_backend")]
69-
pub type FieldElement = backend::serial::u32::field::FieldElement2625;
38+
cfg_if! {
39+
if #[cfg(feature = "fiat_backend")] {
40+
#[cfg(not(target_pointer_width = "64"))]
41+
pub use backend::serial::fiat_u32::field::*;
42+
#[cfg(target_pointer_width = "64")]
43+
pub use backend::serial::fiat_u64::field::*;
44+
45+
/// A `FieldElement` represents an element of the field
46+
/// \\( \mathbb Z / (2\^{255} - 19)\\).
47+
///
48+
/// The `FieldElement` type is an alias for one of the platform-specific
49+
/// implementations.
50+
///
51+
/// Using formally-verified field arithmetic from fiat-crypto.
52+
#[cfg(not(target_pointer_width = "64"))]
53+
pub type FieldElement = backend::serial::fiat_u32::field::FieldElement2625;
54+
55+
/// A `FieldElement` represents an element of the field
56+
/// \\( \mathbb Z / (2\^{255} - 19)\\).
57+
///
58+
/// The `FieldElement` type is an alias for one of the platform-specific
59+
/// implementations.
60+
///
61+
/// Using formally-verified field arithmetic from fiat-crypto.
62+
#[cfg(target_pointer_width = "64")]
63+
pub type FieldElement = backend::serial::fiat_u64::field::FieldElement51;
64+
} else if #[cfg(target_pointer_width = "64")] {
65+
pub use crate::backend::serial::u64::field::*;
66+
67+
/// A `FieldElement` represents an element of the field
68+
/// \\( \mathbb Z / (2\^{255} - 19)\\).
69+
///
70+
/// The `FieldElement` type is an alias for one of the platform-specific
71+
/// implementations.
72+
pub type FieldElement = backend::serial::u64::field::FieldElement51;
73+
} else {
74+
pub use backend::serial::u32::field::*;
75+
76+
/// A `FieldElement` represents an element of the field
77+
/// \\( \mathbb Z / (2\^{255} - 19)\\).
78+
///
79+
/// The `FieldElement` type is an alias for one of the platform-specific
80+
/// implementations.
81+
pub type FieldElement = backend::serial::u32::field::FieldElement2625;
82+
}
83+
}
7084

7185
impl Eq for FieldElement {}
7286

0 commit comments

Comments
 (0)