From e7f46bff4408294051de46b325e0dd96405f3b18 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Thu, 10 Nov 2022 19:46:42 -0700 Subject: [PATCH] Implement simplified backend selection 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. --- .github/workflows/rust.yml | 25 ++++++------ Cargo.toml | 18 +++------ README.md | 22 ++--------- src/backend/mod.rs | 12 ------ src/backend/serial/mod.rs | 22 ++--------- src/constants.rs | 12 +++--- src/field.rs | 81 ++++++++++++++++++++++---------------- src/lib.rs | 22 ++--------- src/scalar.rs | 53 ++++++++++++++----------- 9 files changed, 114 insertions(+), 153 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6e0ecefeb..998c2d075 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -10,21 +10,22 @@ env: CARGO_TERM_COLOR: always jobs: - test-u32: - name: Test u32 backend + test: runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable - - run: cargo test --no-default-features --features "std u32_backend" + strategy: + matrix: + include: + # 32-bit target + - target: i686-unknown-linux-gnu + deps: sudo apt update && sudo apt install gcc-multilib - test-u64: - name: Test u64 backend - runs-on: ubuntu-latest + # 64-bit target + - target: x86_64-unknown-linux-gnu steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable - - run: cargo test --no-default-features --features "std u64_backend" + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + - run: ${{ matrix.deps }} + - run: cargo test build-simd: name: Build simd backend (nightly) diff --git a/Cargo.toml b/Cargo.toml index 9b58da3b8..097d643cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ name = "dalek_benchmarks" harness = false [dependencies] +cfg-if = "1" rand_core = { version = "0.6", default-features = false } digest = { version = "0.10", default-features = false } subtle = { version = "^2.2.1", default-features = false } @@ -55,20 +56,11 @@ fiat-crypto = { version = "0.1.6", optional = true} [features] nightly = ["subtle/nightly"] -default = ["std", "u64_backend"] +default = ["std"] std = ["alloc", "subtle/std", "rand_core/std"] alloc = ["zeroize/alloc"] -# The u32 backend uses u32s with u64 products. -u32_backend = [] -# The u64 backend uses u64s with u128 products. -u64_backend = [] -# fiat-u64 backend (with formally-verified field arith) uses u64s with u128 products. -fiat_u64_backend = ["fiat-crypto"] -# fiat-u32 backend (with formally-verified field arith) uses u32s with u64 products. -fiat_u32_backend = ["fiat-crypto"] +# fiat-crypto backend with formally-verified field arithmetic +fiat_backend = ["fiat-crypto"] # The SIMD backend uses parallel formulas, using either AVX2 or AVX512-IFMA. -simd_backend = ["nightly", "u64_backend", "packed_simd"] -# DEPRECATED: this is now an alias for `simd_backend` and may be removed -# in some future release. -avx2_backend = ["simd_backend"] +simd_backend = ["nightly", "packed_simd"] diff --git a/README.md b/README.md index e9df17ab6..a86cb4573 100644 --- a/README.md +++ b/README.md @@ -99,18 +99,6 @@ Curve arithmetic is implemented using one of the following backends: * an `avx2` backend using [parallel formulas][parallel_doc] and `avx2` instructions (sets speed records); * an `ifma` backend using [parallel formulas][parallel_doc] and `ifma` instructions (sets speed records); -By default the `u64` backend is selected. To select a specific backend, use: -```sh -cargo build --no-default-features --features "std u32_backend" -cargo build --no-default-features --features "std u64_backend" -# Requires nightly, RUSTFLAGS="-C target_feature=+avx2" to use avx2 -cargo build --no-default-features --features "std simd_backend" -# Requires nightly, RUSTFLAGS="-C target_feature=+avx512ifma" to use ifma -cargo build --no-default-features --features "std simd_backend" -``` -Crates using `curve25519-dalek` can either select a backend on behalf of their -users, or expose feature flags that control the `curve25519-dalek` backend. - The `std` feature is enabled by default, but it can be disabled for no-`std` builds using `--no-default-features`. Note that this requires explicitly selecting an arithmetic backend using one of the `_backend` features. @@ -166,8 +154,7 @@ compiled with appropriate `target_feature`s, so this cannot occur. Benchmarks are run using [`criterion.rs`][criterion]: ```sh -cargo bench --no-default-features --features "std u32_backend" -cargo bench --no-default-features --features "std u64_backend" +cargo bench --no-default-features --features std # Uses avx2 or ifma only if compiled for an appropriate target. export RUSTFLAGS="-C target_cpu=native" cargo bench --no-default-features --features "std simd_backend" @@ -227,10 +214,9 @@ optimised batch inversion was contributed by Sean Bowe and Daira Hopwood. The `no_std` and `zeroize` support was contributed by Tony Arcieri. -The formally verified backends, `fiat_u32_backend` and `fiat_u64_backend`, which -integrate with the Rust generated by the -[Fiat Crypto project](https://github.com/mit-plv/fiat-crypto) were contributed -by François Garillot. +The formally verified `fiat_backend` integrates Rust code generated by the +[Fiat Crypto project](https://github.com/mit-plv/fiat-crypto) and was +contributed by François Garillot. Thanks also to Ashley Hauck, Lucas Salibian, Manish Goregaokar, Jack Grigg, Pratyush Mishra, Michael Rosenberg, and countless others for their diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 18f8af797..9da698368 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -34,18 +34,6 @@ //! The [`vector`] backend is selected by the `simd_backend` cargo //! feature; it uses the [`serial`] backend for non-vectorized operations. -#[cfg(not(any( - feature = "u32_backend", - feature = "u64_backend", - feature = "fiat_u32_backend", - feature = "fiat_u64_backend", - feature = "simd_backend", -)))] -compile_error!( - "no curve25519-dalek backend cargo feature enabled! \ - please enable one of: u32_backend, u64_backend, fiat_u32_backend, fiat_u64_backend, simd_backend" -); - pub mod serial; #[cfg(any( diff --git a/src/backend/serial/mod.rs b/src/backend/serial/mod.rs index 971afe97f..a0094e0b0 100644 --- a/src/backend/serial/mod.rs +++ b/src/backend/serial/mod.rs @@ -19,31 +19,17 @@ //! //! When the vector backend is enabled, the field and scalar //! implementations are still used for non-vectorized operations. -//! -//! Note: at this time the `u32` and `u64` backends cannot be built -//! together. - -#[cfg(not(any( - feature = "u32_backend", - feature = "u64_backend", - feature = "fiat_u32_backend", - feature = "fiat_u64_backend" -)))] -compile_error!( - "no curve25519-dalek backend cargo feature enabled! \ - please enable one of: u32_backend, u64_backend, fiat_u32_backend, fiat_u64_backend" -); -#[cfg(feature = "u32_backend")] +#[cfg(not(target_pointer_width = "64"))] pub mod u32; -#[cfg(feature = "u64_backend")] +#[cfg(target_pointer_width = "64")] pub mod u64; -#[cfg(feature = "fiat_u32_backend")] +#[cfg(all(feature = "fiat_backend", not(target_pointer_width = "64")))] pub mod fiat_u32; -#[cfg(feature = "fiat_u64_backend")] +#[cfg(all(feature = "fiat_backend", target_pointer_width = "64"))] pub mod fiat_u64; pub mod curve_models; diff --git a/src/constants.rs b/src/constants.rs index d22a962b5..7cf6be1cb 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -34,13 +34,13 @@ use crate::ristretto::CompressedRistretto; use crate::ristretto::RistrettoPoint; use crate::scalar::Scalar; -#[cfg(feature = "fiat_u32_backend")] +#[cfg(all(feature = "fiat_backend", not(target_pointer_width = "64")))] pub use crate::backend::serial::fiat_u32::constants::*; -#[cfg(feature = "fiat_u64_backend")] +#[cfg(all(feature = "fiat_backend", target_pointer_width = "64"))] pub use crate::backend::serial::fiat_u64::constants::*; -#[cfg(feature = "u32_backend")] +#[cfg(target_pointer_width = "32")] pub use crate::backend::serial::u32::constants::*; -#[cfg(feature = "u64_backend")] +#[cfg(target_pointer_width = "64")] pub use crate::backend::serial::u64::constants::*; /// The Ed25519 basepoint, in `CompressedEdwardsY` format. @@ -142,7 +142,7 @@ mod test { /// Test that d = -121665/121666 #[test] - #[cfg(feature = "u32_backend")] + #[cfg(not(target_pointer_width = "64"))] fn test_d_vs_ratio() { use crate::backend::serial::u32::field::FieldElement2625; let a = -&FieldElement2625([121665, 0, 0, 0, 0, 0, 0, 0, 0, 0]); @@ -155,7 +155,7 @@ mod test { /// Test that d = -121665/121666 #[test] - #[cfg(feature = "u64_backend")] + #[cfg(target_pointer_width = "64")] fn test_d_vs_ratio() { use crate::backend::serial::u64::field::FieldElement51; let a = -&FieldElement51([121665, 0, 0, 0, 0]); diff --git a/src/field.rs b/src/field.rs index 294268bb1..550b7730a 100644 --- a/src/field.rs +++ b/src/field.rs @@ -25,6 +25,8 @@ use core::cmp::{Eq, PartialEq}; +use cfg_if::cfg_if; + use subtle::Choice; use subtle::ConditionallyNegatable; use subtle::ConditionallySelectable; @@ -33,40 +35,51 @@ use subtle::ConstantTimeEq; use crate::backend; use crate::constants; -#[cfg(feature = "fiat_u32_backend")] -pub use backend::serial::fiat_u32::field::*; -#[cfg(feature = "fiat_u64_backend")] -pub use backend::serial::fiat_u64::field::*; -/// A `FieldElement` represents an element of the field -/// \\( \mathbb Z / (2\^{255} - 19)\\). -/// -/// The `FieldElement` type is an alias for one of the platform-specific -/// implementations. -/// Using formally-verified field arithmetic from fiat-crypto -#[cfg(feature = "fiat_u32_backend")] -pub type FieldElement = backend::serial::fiat_u32::field::FieldElement2625; -#[cfg(feature = "fiat_u64_backend")] -pub type FieldElement = backend::serial::fiat_u64::field::FieldElement51; - -#[cfg(feature = "u64_backend")] -pub use crate::backend::serial::u64::field::*; -/// A `FieldElement` represents an element of the field -/// \\( \mathbb Z / (2\^{255} - 19)\\). -/// -/// The `FieldElement` type is an alias for one of the platform-specific -/// implementations. -#[cfg(feature = "u64_backend")] -pub type FieldElement = backend::serial::u64::field::FieldElement51; - -#[cfg(feature = "u32_backend")] -pub use backend::serial::u32::field::*; -/// A `FieldElement` represents an element of the field -/// \\( \mathbb Z / (2\^{255} - 19)\\). -/// -/// The `FieldElement` type is an alias for one of the platform-specific -/// implementations. -#[cfg(feature = "u32_backend")] -pub type FieldElement = backend::serial::u32::field::FieldElement2625; +cfg_if! { + if #[cfg(feature = "fiat_backend")] { + if #[cfg(target_pointer_width = "64")] { + pub use backend::serial::fiat_u64::field::*; + + /// A `FieldElement` represents an element of the field + /// \\( \mathbb Z / (2\^{255} - 19)\\). + /// + /// The `FieldElement` type is an alias for one of the platform-specific + /// implementations. + /// + /// Using formally-verified field arithmetic from fiat-crypto. + pub type FieldElement = backend::serial::fiat_u64::field::FieldElement51; + } else { + pub use backend::serial::fiat_u32::field::*; + + /// A `FieldElement` represents an element of the field + /// \\( \mathbb Z / (2\^{255} - 19)\\). + /// + /// The `FieldElement` type is an alias for one of the platform-specific + /// implementations. + /// + /// Using formally-verified field arithmetic from fiat-crypto. + pub type FieldElement = backend::serial::fiat_u32::field::FieldElement2625; + } + } else if #[cfg(target_pointer_width = "64")] { + pub use crate::backend::serial::u64::field::*; + + /// A `FieldElement` represents an element of the field + /// \\( \mathbb Z / (2\^{255} - 19)\\). + /// + /// The `FieldElement` type is an alias for one of the platform-specific + /// implementations. + pub type FieldElement = backend::serial::u64::field::FieldElement51; + } else { + pub use backend::serial::u32::field::*; + + /// A `FieldElement` represents an element of the field + /// \\( \mathbb Z / (2\^{255} - 19)\\). + /// + /// The `FieldElement` type is an alias for one of the platform-specific + /// implementations. + pub type FieldElement = backend::serial::u32::field::FieldElement2625; + } +} impl Eq for FieldElement {} diff --git a/src/lib.rs b/src/lib.rs index 0e18d5f30..db6f55fb9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -105,18 +105,6 @@ //! * an `avx2` backend using [parallel formulas][parallel_doc] and `avx2` instructions (sets speed records); //! * an `ifma` backend using [parallel formulas][parallel_doc] and `ifma` instructions (sets speed records); //! -//! By default the `u64` backend is selected. To select a specific backend, use: -//! ```sh -//! cargo build --no-default-features --features "std u32_backend" -//! cargo build --no-default-features --features "std u64_backend" -//! # Requires nightly, RUSTFLAGS="-C target_feature=+avx2" to use avx2 -//! cargo build --no-default-features --features "std simd_backend" -//! # Requires nightly, RUSTFLAGS="-C target_feature=+avx512ifma" to use ifma -//! cargo build --no-default-features --features "std simd_backend" -//! ``` -//! Crates using `curve25519-dalek` can either select a backend on behalf of their -//! users, or expose feature flags that control the `curve25519-dalek` backend. -//! //! The `std` feature is enabled by default, but it can be disabled for no-`std` //! builds using `--no-default-features`. Note that this requires explicitly //! selecting an arithmetic backend using one of the `_backend` features. @@ -165,8 +153,7 @@ //! Benchmarks are run using [`criterion.rs`][criterion]: //! //! ```sh -//! cargo bench --no-default-features --features "std u32_backend" -//! cargo bench --no-default-features --features "std u64_backend" +//! cargo bench --no-default-features --features std //! # Uses avx2 or ifma only if compiled for an appropriate target. //! export RUSTFLAGS="-C target_cpu=native" //! cargo bench --no-default-features --features "std simd_backend" @@ -226,10 +213,9 @@ //! //! The `no_std` and `zeroize` support was contributed by Tony Arcieri. //! -//! The formally verified backends, `fiat_u32_backend` and `fiat_u64_backend`, which -//! integrate with the Rust generated by the -//! [Fiat Crypto project](https://github.com/mit-plv/fiat-crypto) were contributed -//! by François Garillot. +//! The formally verified `fiat_backend` integrates Rust code generated by the +//! [Fiat Crypto project](https://github.com/mit-plv/fiat-crypto) and was +//! contributed by François Garillot. //! //! Thanks also to Ashley Hauck, Lucas Salibian, Manish Goregaokar, Jack Grigg, //! Pratyush Mishra, Michael Rosenberg, and countless others for their diff --git a/src/scalar.rs b/src/scalar.rs index 3f3a6b8c7..ef8aa9d6e 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -150,6 +150,8 @@ use core::ops::{Sub, SubAssign}; #[allow(unused_imports)] use crate::prelude::*; +use cfg_if::cfg_if; + use rand_core::{CryptoRng, RngCore}; use digest::generic_array::typenum::U64; @@ -164,28 +166,35 @@ use zeroize::Zeroize; use crate::backend; use crate::constants; -/// An `UnpackedScalar` represents an element of the field GF(l), optimized for speed. -/// -/// This is a type alias for one of the scalar types in the `backend` -/// module. -#[cfg(feature = "fiat_u32_backend")] -type UnpackedScalar = backend::serial::fiat_u32::scalar::Scalar29; -#[cfg(feature = "fiat_u64_backend")] -type UnpackedScalar = backend::serial::fiat_u64::scalar::Scalar52; - -/// An `UnpackedScalar` represents an element of the field GF(l), optimized for speed. -/// -/// This is a type alias for one of the scalar types in the `backend` -/// module. -#[cfg(feature = "u64_backend")] -type UnpackedScalar = backend::serial::u64::scalar::Scalar52; - -/// An `UnpackedScalar` represents an element of the field GF(l), optimized for speed. -/// -/// This is a type alias for one of the scalar types in the `backend` -/// module. -#[cfg(feature = "u32_backend")] -type UnpackedScalar = backend::serial::u32::scalar::Scalar29; +cfg_if! { + if #[cfg(feature = "fiat_backend")] { + if #[cfg(target_pointer_width = "64")] { + /// An `UnpackedScalar` represents an element of the field GF(l), optimized for speed. + /// + /// This is a type alias for one of the scalar types in the `backend` + /// module. + type UnpackedScalar = backend::serial::fiat_u64::scalar::Scalar52; + } else { + /// An `UnpackedScalar` represents an element of the field GF(l), optimized for speed. + /// + /// This is a type alias for one of the scalar types in the `backend` + /// module. + type UnpackedScalar = backend::serial::fiat_u32::scalar::Scalar29; + } + } else if #[cfg(target_pointer_width = "64")] { + /// An `UnpackedScalar` represents an element of the field GF(l), optimized for speed. + /// + /// This is a type alias for one of the scalar types in the `backend` + /// module. + type UnpackedScalar = backend::serial::u64::scalar::Scalar52; + } else { + /// An `UnpackedScalar` represents an element of the field GF(l), optimized for speed. + /// + /// This is a type alias for one of the scalar types in the `backend` + /// module. + type UnpackedScalar = backend::serial::u32::scalar::Scalar29; + } +} /// The `Scalar` struct holds an integer \\(s < 2\^{255} \\) which /// represents an element of \\(\mathbb Z / \ell\\).