Skip to content

Commit

Permalink
Implement simplified backend selection
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tarcieri committed Nov 11, 2022
1 parent d687cc8 commit 40bac3c
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 160 deletions.
34 changes: 19 additions & 15 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,23 @@ 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: rustup target add ${{ matrix.target }}
- run: ${{ matrix.deps }}
- run: cargo test --target ${{ matrix.target }}

build-simd:
name: Build simd backend (nightly)
Expand Down Expand Up @@ -54,7 +56,9 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- run: cargo test --lib --no-default-features --features "alloc u32_backend"
- run: rustup target add i686-unknown-linux-gnu
- run: sudo apt update && sudo apt install gcc-multilib
- run: cargo test --lib --no-default-features --features alloc --target i686-unknown-linux-gnu

nightly:
name: Test nightly compiler
Expand All @@ -72,11 +76,11 @@ jobs:
# First run `cargo +nightly -Z minimal-verisons check` in order to get a
# Cargo.lock with the oldest possible deps
- uses: dtolnay/rust-toolchain@nightly
- run: cargo -Z minimal-versions check --no-default-features --features "fiat_u64_backend serde"
- run: cargo -Z minimal-versions check --no-default-features --features fiat_backend,serde
# Now check that `cargo build` works with respect to the oldest possible
# deps and the stated MSRV
- uses: dtolnay/[email protected]
- run: cargo build --no-default-features --features "fiat_u64_backend serde"
- run: cargo build --no-default-features --features fiat_backend,serde

bench:
name: Check that benchmarks compile
Expand Down
18 changes: 5 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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"]
22 changes: 4 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
12 changes: 0 additions & 12 deletions src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
22 changes: 4 additions & 18 deletions src/backend/serial/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
27 changes: 17 additions & 10 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,27 @@

#![allow(non_snake_case)]

use cfg_if::cfg_if;

use crate::edwards::CompressedEdwardsY;
use crate::montgomery::MontgomeryPoint;
use crate::ristretto::CompressedRistretto;
use crate::ristretto::RistrettoPoint;
use crate::scalar::Scalar;

#[cfg(feature = "fiat_u32_backend")]
pub use crate::backend::serial::fiat_u32::constants::*;
#[cfg(feature = "fiat_u64_backend")]
pub use crate::backend::serial::fiat_u64::constants::*;
#[cfg(feature = "u32_backend")]
pub use crate::backend::serial::u32::constants::*;
#[cfg(feature = "u64_backend")]
pub use crate::backend::serial::u64::constants::*;
cfg_if! {
if #[cfg(feature = "fiat_backend")] {
#[cfg(not(target_pointer_width = "64"))]
pub use crate::backend::serial::fiat_u32::constants::*;
#[cfg(target_pointer_width = "64")]
pub use crate::backend::serial::fiat_u64::constants::*;
} else {
#[cfg(not(target_pointer_width = "64"))]
pub use crate::backend::serial::u32::constants::*;
#[cfg(target_pointer_width = "64")]
pub use crate::backend::serial::u64::constants::*;
}
}

/// The Ed25519 basepoint, in `CompressedEdwardsY` format.
///
Expand Down Expand Up @@ -142,7 +149,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]);
Expand All @@ -155,7 +162,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]);
Expand Down
82 changes: 48 additions & 34 deletions src/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

use core::cmp::{Eq, PartialEq};

use cfg_if::cfg_if;

use subtle::Choice;
use subtle::ConditionallyNegatable;
use subtle::ConditionallySelectable;
Expand All @@ -33,40 +35,52 @@ 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")] {
#[cfg(not(target_pointer_width = "64"))]
pub use backend::serial::fiat_u32::field::*;
#[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.
#[cfg(not(target_pointer_width = "64"))]
pub type FieldElement = backend::serial::fiat_u32::field::FieldElement2625;

/// 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(target_pointer_width = "64")]
pub type FieldElement = backend::serial::fiat_u64::field::FieldElement51;
} 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 {}

Expand Down
22 changes: 4 additions & 18 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit 40bac3c

Please sign in to comment.