From 9b6c932635cd3c85ab499bd5ba21af4b221adcc4 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 14 May 2018 15:41:45 -0700 Subject: [PATCH 1/9] Rename 'precomputed_tables' to the more accurate 'stage2_build' --- Cargo.toml | 7 +++++-- build.rs | 4 ++-- src/backend/avx2/mod.rs | 4 +++- src/backend/avx2/scalar_mul/mod.rs | 2 +- src/constants.rs | 6 +++--- src/edwards.rs | 15 +++------------ src/montgomery.rs | 3 +-- src/ristretto.rs | 6 ++---- src/scalar_mul/mod.rs | 2 +- 9 files changed, 21 insertions(+), 28 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b6c5681..84d326e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,7 +67,10 @@ alloc = [] yolocrypto = ["avx2_backend"] # Radix-51 arithmetic using u128 radix_51 = [] -# Include precomputed basepoint tables. This is off by default so that build.rs can generate the tables, and then re-enabled by build.rs in the main-stage compilation. -precomputed_tables = [] # experimental avx2 support avx2_backend = ["nightly"] +# Signals that we're in the main build stage. This is off by default, +# to signal stage 1 of the build, where build.rs loads the library +# into the build script. Then, the build.rs emits the stage2_build +# feature before the main-stage compilation. +stage2_build = [] diff --git a/build.rs b/build.rs index 389e7bb..52c66bd 100644 --- a/build.rs +++ b/build.rs @@ -62,8 +62,8 @@ use curve_models::AffineNielsPoint; use scalar_mul::window::NafLookupTable8; fn main() { - // Enable the "precomputed_tables" feature in the main build stage - println!("cargo:rustc-cfg=feature=\"precomputed_tables\"\n"); + // Enable the "stage2_build" feature in the main build stage + println!("cargo:rustc-cfg=feature=\"stage2_build\"\n"); let out_dir = env::var("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("basepoint_table.rs"); diff --git a/src/backend/avx2/mod.rs b/src/backend/avx2/mod.rs index 94cf55a..b13ea1d 100644 --- a/src/backend/avx2/mod.rs +++ b/src/backend/avx2/mod.rs @@ -9,7 +9,9 @@ // - Henry de Valence // See the comment above the ristretto::notes module. -#![cfg_attr(all(feature = "nightly", feature="precomputed_tables"), doc(include = "../docs/avx2-notes.md"))] +#![cfg_attr( + all(feature = "nightly", feature = "stage2_build"), doc(include = "../docs/avx2-notes.md") +)] pub(crate) mod field; diff --git a/src/backend/avx2/scalar_mul/mod.rs b/src/backend/avx2/scalar_mul/mod.rs index c4c944b..f59cba2 100644 --- a/src/backend/avx2/scalar_mul/mod.rs +++ b/src/backend/avx2/scalar_mul/mod.rs @@ -10,7 +10,7 @@ pub mod variable_base; -#[cfg(feature="precomputed_tables")] +#[cfg(feature = "stage2_build")] pub mod vartime_double_base; #[cfg(any(feature = "alloc", feature = "std"))] diff --git a/src/constants.rs b/src/constants.rs index c8cf1c1..247f094 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -85,14 +85,14 @@ pub const BASEPOINT_ORDER: Scalar = Scalar{ // Precomputed basepoint table is generated into a file by build.rs -#[cfg(feature="precomputed_tables")] +#[cfg(feature = "stage2_build")] include!(concat!(env!("OUT_DIR"), "/basepoint_table.rs")); -#[cfg(feature="precomputed_tables")] +#[cfg(feature = "stage2_build")] use ristretto::RistrettoBasepointTable; /// The Ristretto basepoint, as a `RistrettoBasepointTable` for scalar multiplication. -#[cfg(feature="precomputed_tables")] +#[cfg(feature = "stage2_build")] pub const RISTRETTO_BASEPOINT_TABLE: RistrettoBasepointTable = RistrettoBasepointTable(ED25519_BASEPOINT_TABLE); diff --git a/src/edwards.rs b/src/edwards.rs index 5ba490c..95a208e 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -858,7 +858,7 @@ pub mod vartime { } /// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint. - #[cfg(feature="precomputed_tables")] + #[cfg(feature="stage2_build")] pub fn double_scalar_mul_basepoint(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint { // If we built with AVX2, use the AVX2 backend. #[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] @@ -879,7 +879,7 @@ pub mod vartime { // Tests // ------------------------------------------------------------------------ -#[cfg(test)] +#[cfg(all(test, feature = "stage2_build"))] mod test { use field::FieldElement; use scalar::Scalar; @@ -971,7 +971,6 @@ mod test { /// Test that computing 1*basepoint gives the correct basepoint. #[test] - #[cfg(feature="precomputed_tables")] fn basepoint_mult_one_vs_basepoint() { let bp = &constants::ED25519_BASEPOINT_TABLE * &Scalar::one(); let compressed = bp.compress(); @@ -980,7 +979,6 @@ mod test { /// Test that `EdwardsBasepointTable::basepoint()` gives the correct basepoint. #[test] - #[cfg(feature="precomputed_tables")] fn basepoint_table_basepoint_function_correct() { let bp = constants::ED25519_BASEPOINT_TABLE.basepoint(); assert_eq!(bp.compress(), constants::ED25519_BASEPOINT_COMPRESSED); @@ -1031,7 +1029,6 @@ mod test { /// Sanity check for conversion to precomputed points #[test] - #[cfg(feature="precomputed_tables")] fn to_affine_niels_clears_denominators() { // construct a point as aB so it has denominators (ie. Z != 1) let aB = &constants::ED25519_BASEPOINT_TABLE * &A_SCALAR; @@ -1043,7 +1040,6 @@ mod test { /// Test basepoint_mult versus a known scalar multiple from ed25519.py #[test] - #[cfg(feature="precomputed_tables")] fn basepoint_mult_vs_ed25519py() { let aB = &constants::ED25519_BASEPOINT_TABLE * &A_SCALAR; assert_eq!(aB.compress(), A_TIMES_BASEPOINT); @@ -1051,7 +1047,6 @@ mod test { /// Test that multiplication by the basepoint order kills the basepoint #[test] - #[cfg(feature="precomputed_tables")] fn basepoint_mult_by_basepoint_order() { let B = &constants::ED25519_BASEPOINT_TABLE; let should_be_id = B * &constants::BASEPOINT_ORDER; @@ -1060,11 +1055,9 @@ mod test { /// Test precomputed basepoint mult #[test] - #[cfg(feature="precomputed_tables")] fn test_precomputed_basepoint_mult() { - let table = EdwardsBasepointTable::create(&constants::ED25519_BASEPOINT_POINT); let aB_1 = &constants::ED25519_BASEPOINT_TABLE * &A_SCALAR; - let aB_2 = &table * &A_SCALAR; + let aB_2 = &constants::ED25519_BASEPOINT_POINT * &A_SCALAR; assert_eq!(aB_1.compress(), aB_2.compress()); } @@ -1084,7 +1077,6 @@ mod test { /// Test that computing 2*basepoint is the same as basepoint.double() #[test] - #[cfg(feature="precomputed_tables")] fn basepoint_mult_two_vs_basepoint2() { let two = Scalar::from_u64(2); let bp2 = &constants::ED25519_BASEPOINT_TABLE * &two; @@ -1210,7 +1202,6 @@ mod test { /// Test double_scalar_mul_vartime vs ed25519.py #[test] - #[cfg(feature="precomputed_tables")] fn double_scalar_mul_basepoint_vs_ed25519py() { let A = A_TIMES_BASEPOINT.decompress().unwrap(); let result = vartime::double_scalar_mul_basepoint(&A_SCALAR, &A, &B_SCALAR); diff --git a/src/montgomery.rs b/src/montgomery.rs index 0ebca9a..ad03c69 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -279,7 +279,7 @@ impl<'a, 'b> Mul<&'b MontgomeryPoint> for &'a Scalar { // Tests // ------------------------------------------------------------------------ -#[cfg(test)] +#[cfg(all(test, feature = "stage2_build"))] mod test { use constants; use super::*; @@ -338,7 +338,6 @@ mod test { } #[test] - #[cfg(feature="precomputed_tables")] fn montgomery_ladder_matches_edwards_scalarmult() { let mut csprng: OsRng = OsRng::new().unwrap(); diff --git a/src/ristretto.rs b/src/ristretto.rs index c5b86f6..b293fb8 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -161,7 +161,7 @@ // missing). // // This hack is also used in the avx2 notes. -#[cfg_attr(all(feature = "nightly", feature="precomputed_tables"), doc(include = "../docs/ristretto-notes.md"))] +#[cfg_attr(all(feature = "nightly", feature = "stage2_build"), doc(include = "../docs/ristretto-notes.md"))] mod notes { } @@ -1014,7 +1014,7 @@ pub mod vartime { // Tests // ------------------------------------------------------------------------ -#[cfg(test)] +#[cfg(all(test, feature = "stage2_build"))] mod test { use rand::OsRng; @@ -1152,7 +1152,6 @@ mod test { } #[test] - #[cfg(feature="precomputed_tables")] fn four_torsion_random() { let mut rng = OsRng::new().unwrap(); let B = &constants::RISTRETTO_BASEPOINT_TABLE; @@ -1215,7 +1214,6 @@ mod test { } #[test] - #[cfg(feature="precomputed_tables")] fn random_roundtrip() { let mut rng = OsRng::new().unwrap(); let B = &constants::RISTRETTO_BASEPOINT_TABLE; diff --git a/src/scalar_mul/mod.rs b/src/scalar_mul/mod.rs index f172788..78ba7cd 100644 --- a/src/scalar_mul/mod.rs +++ b/src/scalar_mul/mod.rs @@ -12,7 +12,7 @@ pub mod window; pub mod variable_base; -#[cfg(feature="precomputed_tables")] +#[cfg(feature = "stage2_build")] pub mod vartime_double_base; #[cfg(any(feature = "alloc", feature = "std"))] From 34c43c20a97c6930c6495a0a72a5682aa836b69f Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 14 May 2018 17:35:34 -0700 Subject: [PATCH 2/9] Rework backend selection code. Each backend can now be selected by an individual feature: - `u32_backend` for `backend::u32`; - `u64_backend` for `backend::u64`; - `avx2_backend` for `backend::avx2`; The `u64_backend` is selected by default, since most people use X64 and we have no way to select based on target (see discussion in #126). However, these changes mean that it is possible to select the backend explicitly, and if we had the ability to select target-default features, we could do so easily. --- .travis.yml | 28 +++++++++++----------------- Cargo.toml | 19 ++++++++++++------- README.md | 29 +++++++++++++++++++++-------- build.rs | 9 ++++----- src/backend/mod.rs | 6 +++--- src/constants.rs | 8 ++++---- src/edwards.rs | 18 +++++++++--------- src/field.rs | 8 ++++---- src/lib.rs | 1 - src/scalar.rs | 4 ++-- 10 files changed, 70 insertions(+), 60 deletions(-) diff --git a/.travis.yml b/.travis.yml index d910557..a38afd5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,34 +2,28 @@ language: rust rust: - stable - - beta - nightly env: - - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='' + # Tests the u32 backend + - TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std u32_backend' + # Tests the u64 backend + - TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std u64_backend' + # Tests the avx2 backend + - TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std avx2_backend yolocrypto' + # Tests serde support and default feature selection - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='serde' - - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='nightly' - - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='yolocrypto nightly' + # Tests building without std - TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES='' matrix: exclude: - # Test nightly features, such as radix_51, only on nightly. + # Test the avx2 backend only on nightly - rust: stable - env: TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='nightly' - - rust: beta - env: TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='nightly' - - rust: stable - env: TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='yolocrypto nightly' - - rust: beta - env: TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='yolocrypto nightly' + env: TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std avx2_backend yolocrypto' # Test no_std only on nightly. - rust: stable - env: TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES='' - - rust: beta - env: TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES='' - - rust: nightly - env: TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES='alloc' + env: TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES='' script: - cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS diff --git a/Cargo.toml b/Cargo.toml index 84d326e..df8c644 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,15 +60,20 @@ serde = { version = "1.0", optional = true } rand = { version = "0.4", optional = false } [features] -nightly = ["radix_51", "subtle/nightly", "clear_on_drop/nightly"] -default = ["std"] +nightly = ["subtle/nightly", "clear_on_drop/nightly"] +default = ["std", "u64_backend"] std = ["rand", "subtle/std"] alloc = [] -yolocrypto = ["avx2_backend"] -# Radix-51 arithmetic using u128 -radix_51 = [] -# experimental avx2 support -avx2_backend = ["nightly"] +yolocrypto = [] + +# The u32 backend uses u32s with u64 products. +u32_backend = [] +# The u64 backend uses u64s with u128 products. +u64_backend = [] +# The AVX2 backend uses u32x8s with u64x4 products. +# It uses the u64 code for serial operations. +avx2_backend = ["nightly", "u64_backend"] + # Signals that we're in the main build stage. This is off by default, # to signal stage 1 of the build, where build.rs loads the library # into the build script. Then, the build.rs emits the stage2_build diff --git a/README.md b/README.md index 53cfd8b..15a926b 100644 --- a/README.md +++ b/README.md @@ -59,25 +59,38 @@ extern crate curve25519_dalek; # Backends and Features +The `yolocrypto` feature enables experimental features. The name `yolocrypto` +is meant to indicate that it is not considered production-ready, and we do not +consider `yolocrypto` features to be covered by semver guarantees. + +The `std` feature is enabled by default, but it can be disabled. + +The `nightly` feature enables nightly-only features. **It is recommended for security**. + Curve arithmetic is implemented using one of the following backends: * a `u32` backend using `u64` products; -* a `u64` backend using `u128` products, available using the `nightly` feature; +* a `u64` backend using `u128` products; * an experimental AVX2 backend, available using the `yolocrypto` feature when compiling for a target with `target_feature=+avx2`. +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" +cargo build --no-default-features --features "std avx2_backend yolocrypto" +``` + Benchmarks are run using [`criterion.rs`][criterion]: ```sh -cargo bench # u32 backend -cargo bench --features="nightly" # u64 backend -cargo bench --features="nightly yolocrypto" # u64 or avx2 if available +# You must set RUSTFLAGS to enable AVX2 support. +export RUSTFLAGS="-C target_cpu=native" +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 avx2_backend yolocrypto" ``` -The `yolocrypto` feature enables experimental features. The name `yolocrypto` -is meant to indicate that it is not considered production-ready, and we do not -consider `yolocrypto` features to be covered by semver guarantees. - # Contributing Please see [CONTRIBUTING.md][contributing]. diff --git a/build.rs b/build.rs index 52c66bd..64aec5d 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "nightly", feature(i128_type))] #![cfg_attr(feature = "nightly", feature(cfg_target_feature))] #![cfg_attr(all(feature = "nightly", feature = "yolocrypto"), feature(stdsimd))] #![allow(unused_variables)] @@ -75,12 +74,12 @@ fn main() { f.write_all( format!( "\n -#[cfg(feature=\"radix_51\")] -use backend::u64::field::FieldElement64; - -#[cfg(not(feature=\"radix_51\"))] +#[cfg(feature = \"u32_backend\")] use backend::u32::field::FieldElement32; +#[cfg(feature = \"u64_backend\")] +use backend::u64::field::FieldElement64; + use edwards::EdwardsBasepointTable; use curve_models::AffineNielsPoint; diff --git a/src/backend/mod.rs b/src/backend/mod.rs index bfa2a66..b095eef 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -21,12 +21,12 @@ //! `32bit` since identifiers can't start with letters, and the backends //! do use `u32`/`u64`, so this seems like a least-bad option. -#[cfg(not(feature="radix_51"))] +#[cfg(feature = "u32_backend")] pub mod u32; -#[cfg(feature="radix_51")] +#[cfg(feature = "u64_backend")] pub mod u64; -#[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] +#[cfg(all(feature = "avx2_backend", feature = "yolocrypto", target_feature = "avx2"))] pub mod avx2; diff --git a/src/constants.rs b/src/constants.rs index 247f094..fd8298c 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -33,9 +33,9 @@ use ristretto::CompressedRistretto; use montgomery::MontgomeryPoint; use scalar::Scalar; -#[cfg(feature="radix_51")] +#[cfg(feature = "u64_backend")] pub use backend::u64::constants::*; -#[cfg(not(feature="radix_51"))] +#[cfg(feature = "u32_backend")] pub use backend::u32::constants::*; /// The Ed25519 basepoint, in `CompressedEdwardsY` format. @@ -149,8 +149,8 @@ mod test { } /// Test that d = -121665/121666 - #[cfg(not(feature="radix_51"))] #[test] + #[cfg(feature = "u32_backend")] fn test_d_vs_ratio() { use backend::u32::field::FieldElement32; let a = -&FieldElement32([121665,0,0,0,0,0,0,0,0,0]); @@ -162,8 +162,8 @@ mod test { } /// Test that d = -121665/121666 - #[cfg(feature="radix_51")] #[test] + #[cfg(feature = "u64_backend")] fn test_d_vs_ratio() { use backend::u64::field::FieldElement64; let a = -&FieldElement64([121665,0,0,0,0]); diff --git a/src/edwards.rs b/src/edwards.rs index 95a208e..8a9f89b 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -490,13 +490,13 @@ impl<'a, 'b> Mul<&'b Scalar> for &'a EdwardsPoint { /// `EdwardsBasepointTable` is approximately 4x faster. fn mul(self, scalar: &'b Scalar) -> EdwardsPoint { // If we built with AVX2, use the AVX2 backend. - #[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] + #[cfg(all(feature="avx2_backend", target_feature="avx2"))] { use backend::avx2::scalar_mul::variable_base::mul; mul(self, scalar) } // Otherwise, use the serial backend: - #[cfg(not(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2"))))] + #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] { use scalar_mul::variable_base::mul; mul(self, scalar) @@ -571,13 +571,13 @@ pub fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint // delegate based on the iter's size hint -- hdevalence // If we built with AVX2, use the AVX2 backend. - #[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] + #[cfg(all(feature="avx2_backend", target_feature="avx2"))] { use backend::avx2::scalar_mul::straus::multiscalar_mul; multiscalar_mul(scalars, points) } // Otherwise, proceed as normal: - #[cfg(not(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2"))))] + #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] { use scalar_mul::straus::multiscalar_mul; multiscalar_mul(scalars, points) @@ -844,13 +844,13 @@ pub mod vartime { // XXX later when we do more fancy multiscalar mults, we can delegate // based on the iter's size hint -- hdevalence // If we built with AVX2, use the AVX2 backend. - #[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] + #[cfg(all(feature="avx2_backend", target_feature="avx2"))] { use backend::avx2::scalar_mul::vartime_straus::multiscalar_mul; multiscalar_mul(scalars, points) } // Otherwise, proceed as normal: - #[cfg(not(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2"))))] + #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] { use scalar_mul::vartime_straus::multiscalar_mul; multiscalar_mul(scalars, points) @@ -861,13 +861,13 @@ pub mod vartime { #[cfg(feature="stage2_build")] pub fn double_scalar_mul_basepoint(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint { // If we built with AVX2, use the AVX2 backend. - #[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] + #[cfg(all(feature="avx2_backend", target_feature="avx2"))] { use backend::avx2::scalar_mul::vartime_double_base::mul; mul(a, A, b) } // Otherwise, proceed as normal: - #[cfg(not(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2"))))] + #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] { use scalar_mul::vartime_double_base::mul; mul(a, A, b) @@ -1169,7 +1169,7 @@ mod test { /// and enable `debug_assert!()`. This performs many scalar /// multiplications to attempt to trigger possible overflows etc. /// - /// For instance, the `radix_51` `Mul` implementation for + /// For instance, the `u64` `Mul` implementation for /// `FieldElements` requires the input `Limb`s to be bounded by /// 2^54, but we cannot enforce this dynamically at runtime, or /// statically at compile time (until Rust gets type-level diff --git a/src/field.rs b/src/field.rs index 81251f9..938fa03 100644 --- a/src/field.rs +++ b/src/field.rs @@ -32,24 +32,24 @@ use subtle::ConstantTimeEq; use constants; use backend; -#[cfg(feature="radix_51")] +#[cfg(feature = "u64_backend")] pub use backend::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="radix_51")] +#[cfg(feature = "u64_backend")] pub type FieldElement = backend::u64::field::FieldElement64; -#[cfg(not(feature="radix_51"))] +#[cfg(feature = "u32_backend")] pub use backend::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(not(feature="radix_51"))] +#[cfg(feature = "u32_backend")] pub type FieldElement = backend::u32::field::FieldElement32; impl Eq for FieldElement {} diff --git a/src/lib.rs b/src/lib.rs index e4b4a13..3761540 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,6 @@ #![cfg_attr(feature = "alloc", feature(alloc))] -#![cfg_attr(feature = "nightly", feature(i128_type))] #![cfg_attr(feature = "nightly", feature(cfg_target_feature))] #![cfg_attr(feature = "nightly", feature(external_doc))] #![cfg_attr(all(feature = "nightly", feature = "yolocrypto"), feature(stdsimd))] diff --git a/src/scalar.rs b/src/scalar.rs index f1b03a7..22e9e9d 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -39,14 +39,14 @@ use constants; /// /// This is a type alias for one of the scalar types in the `backend` /// module. -#[cfg(feature="radix_51")] +#[cfg(feature = "u64_backend")] type UnpackedScalar = backend::u64::scalar::Scalar64; /// 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(not(feature="radix_51"))] +#[cfg(feature = "u32_backend")] type UnpackedScalar = backend::u32::scalar::Scalar32; From 83280ca181b15245937c6760956a80767e329e01 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 15 May 2018 02:31:00 +0000 Subject: [PATCH 3/9] Fix travis no_std builds after merging #143. --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index a38afd5..cf6db51 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,8 +13,9 @@ env: - TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std avx2_backend yolocrypto' # Tests serde support and default feature selection - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='serde' - # Tests building without std - - TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES='' + # Tests building without std. We have to select a backend, so we select the one + # most likely to be useful in an embedded environment. + - TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES='u32_backend' matrix: exclude: @@ -23,7 +24,7 @@ matrix: env: TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std avx2_backend yolocrypto' # Test no_std only on nightly. - rust: stable - env: TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES='' + env: TEST_COMMAND=build EXTRA_FLAGS=--no-default-features FEATURES='u32_backend' script: - cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS From e3bf9b0213983e0f136a5cc8555dd77cd583b6dd Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 2 May 2018 14:54:46 -0700 Subject: [PATCH 4/9] Add `MultiscalarMul` and `VartimeMultiscalarMul` traits. These traits have the same interface, but with different names, so that it's not possible to use them interchangeably. (Constant-time and variable-time routines should not be used interchangeably). This commit changes the external API to use these traits, replacing ``` edwards::multiscalar_mul edwards::vartime::multiscalar_mul ``` with ``` EdwardsPoint::multiscalar_mul (as an impl) EdwardsPoint::vartime_multiscalar_mul (as an impl) ``` and similarly for Ristretto. Refactoring the backend is for a later commit. Multiscalar multiplication with precomputation is for a later commit. The `edwards::vartime` module is retained since it's used for `vartime_double_base_scalar_mul`. It should be subsumed into the precomputation API in a later commit. --- benches/dalek_benchmarks.rs | 9 +- src/edwards.rs | 201 +++++++++++++----------------------- src/ristretto.rs | 157 +++++++--------------------- src/traits.rs | 121 +++++++++++++++++++++- 4 files changed, 236 insertions(+), 252 deletions(-) diff --git a/benches/dalek_benchmarks.rs b/benches/dalek_benchmarks.rs index 353f4bf..35d1a24 100644 --- a/benches/dalek_benchmarks.rs +++ b/benches/dalek_benchmarks.rs @@ -18,7 +18,10 @@ static MULTISCALAR_SIZES: [usize; 13] = [1, 2, 4, 8, 16, 32, 64, 128, 256, 384, mod edwards_benches { use super::*; - use curve25519_dalek::edwards::{self, EdwardsPoint}; + use curve25519_dalek::edwards; + use curve25519_dalek::edwards::EdwardsPoint; + use curve25519_dalek::traits::MultiscalarMul; + use curve25519_dalek::traits::VartimeMultiscalarMul; fn compress(c: &mut Criterion) { let B = &constants::ED25519_BASEPOINT_POINT; @@ -70,7 +73,7 @@ mod edwards_benches { .iter() .map(|s| s * &constants::ED25519_BASEPOINT_TABLE) .collect(); - b.iter(|| edwards::multiscalar_mul(&scalars, &points)); + b.iter(|| EdwardsPoint::multiscalar_mul(&scalars, &points)); }, &MULTISCALAR_SIZES, ); @@ -86,7 +89,7 @@ mod edwards_benches { .iter() .map(|s| s * &constants::ED25519_BASEPOINT_TABLE) .collect(); - b.iter(|| edwards::vartime::multiscalar_mul(&scalars, &points)); + b.iter(|| EdwardsPoint::vartime_multiscalar_mul(&scalars, &points)); }, &MULTISCALAR_SIZES, ); diff --git a/src/edwards.rs b/src/edwards.rs index 8a9f89b..27b6154 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -112,6 +112,7 @@ use field::FieldElement; use scalar::Scalar; use montgomery::MontgomeryPoint; + use curve_models::ProjectivePoint; use curve_models::CompletedPoint; use curve_models::AffineNielsPoint; @@ -121,6 +122,8 @@ use scalar_mul::window::LookupTable; use traits::{Identity, IsIdentity}; use traits::ValidityCheck; +use traits::MultiscalarMul; +use traits::VartimeMultiscalarMul; // ------------------------------------------------------------------------ // Compressed points @@ -516,71 +519,68 @@ impl<'a, 'b> Mul<&'b EdwardsPoint> for &'a Scalar { } } -/// Given an iterator of (possibly secret) scalars and an iterator of -/// (possibly secret) points, compute -/// $$ -/// Q = c\_1 P\_1 + \cdots + c\_n P\_n. -/// $$ -/// -/// This function has the same behaviour as -/// `vartime::multiscalar_mul` but is constant-time. -/// -/// It is an error to call this function with two iterators of different lengths. -/// -/// # Examples -/// -/// The trait bound aims for maximum flexibility: the inputs must be -/// convertable to iterators (`I: IntoIter`), and the iterator's items -/// must be `Borrow` (or `Borrow`), to allow -/// iterators returning either `Scalar`s or `&Scalar`s. -/// -/// ``` -/// use curve25519_dalek::{constants, edwards}; -/// use curve25519_dalek::scalar::Scalar; -/// -/// // Some scalars -/// let a = Scalar::from_u64(87329482); -/// let b = Scalar::from_u64(37264829); -/// let c = Scalar::from_u64(98098098); -/// -/// // Some points -/// let P = constants::ED25519_BASEPOINT_POINT; -/// let Q = P + P; -/// let R = P + Q; -/// -/// // A1 = a*P + b*Q + c*R -/// let abc = [a,b,c]; -/// let A1 = edwards::multiscalar_mul(&abc, &[P,Q,R]); -/// // Note: (&abc).into_iter(): Iterator -/// -/// // A2 = (-a)*P + (-b)*Q + (-c)*R -/// let minus_abc = abc.iter().map(|x| -x); -/// let A2 = edwards::multiscalar_mul(minus_abc, &[P,Q,R]); -/// // Note: minus_abc.into_iter(): Iterator -/// -/// assert_eq!(A1.compress(), (-A2).compress()); -/// ``` -#[cfg(any(feature = "alloc", feature = "std"))] -pub fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint - where I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, -{ - // XXX later when we do more fancy multiscalar mults, we can - // delegate based on the iter's size hint -- hdevalence +// ------------------------------------------------------------------------ +// Multiscalar Multiplication impls +// ------------------------------------------------------------------------ - // If we built with AVX2, use the AVX2 backend. - #[cfg(all(feature="avx2_backend", target_feature="avx2"))] +// These use the iterator's size hint and the target settings to +// forward to a specific backend implementation. + +#[cfg(any(feature = "alloc", feature = "std"))] +impl MultiscalarMul for EdwardsPoint { + type Point = EdwardsPoint; + + fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, { - use backend::avx2::scalar_mul::straus::multiscalar_mul; - multiscalar_mul(scalars, points) + // XXX later when we do more fancy multiscalar mults, we can + // delegate based on the iter's size hint -- hdevalence + + // If we built with AVX2, use the AVX2 backend. + #[cfg(all(feature="avx2_backend", target_feature="avx2"))] + { + use backend::avx2::scalar_mul::straus::multiscalar_mul; + multiscalar_mul(scalars, points) + } + // Otherwise, proceed as normal: + #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] + { + use scalar_mul::straus::multiscalar_mul; + multiscalar_mul(scalars, points) + } } - // Otherwise, proceed as normal: - #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] +} + +#[cfg(any(feature = "alloc", feature = "std"))] +impl VartimeMultiscalarMul for EdwardsPoint { + type Point = EdwardsPoint; + + fn vartime_multiscalar_mul(scalars: I, points: J) -> EdwardsPoint + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, { - use scalar_mul::straus::multiscalar_mul; - multiscalar_mul(scalars, points) + // XXX later when we do more fancy multiscalar mults, we can + // delegate based on the iter's size hint -- hdevalence + + // If we built with AVX2, use the AVX2 backend. + #[cfg(all(feature="avx2_backend", target_feature="avx2"))] + { + use backend::avx2::scalar_mul::vartime_straus::multiscalar_mul; + multiscalar_mul(scalars, points) + } + // Otherwise, proceed as normal: + #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] + { + use scalar_mul::vartime_straus::multiscalar_mul; + multiscalar_mul(scalars, points) + } } } @@ -787,78 +787,17 @@ impl Debug for EdwardsBasepointTable { // Variable-time functions // ------------------------------------------------------------------------ +/// Variable-time operations on curve points, useful for non-secret data. +/// +/// XXX delete this whole module pub mod vartime { //! Variable-time operations on curve points, useful for non-secret data. use super::*; - /// Given an iterator of public scalars and an iterator of public points, compute - /// $$ - /// Q = c\_1 P\_1 + \cdots + c\_n P\_n. - /// $$ - /// - /// This function has the same behaviour as - /// `edwards::multiscalar_mul` but operates on non-secret data. - /// - /// It is an error to call this function with two iterators of different lengths. - /// - /// # Examples - /// - /// The trait bound aims for maximum flexibility: the inputs must be - /// convertable to iterators (`I: IntoIter`), and the iterator's items - /// must be `Borrow` (or `Borrow`), to allow - /// iterators returning either `Scalar`s or `&Scalar`s. - /// - /// ``` - /// use curve25519_dalek::{constants, edwards}; - /// use curve25519_dalek::scalar::Scalar; - /// - /// // Some scalars - /// let a = Scalar::from_u64(87329482); - /// let b = Scalar::from_u64(37264829); - /// let c = Scalar::from_u64(98098098); - /// - /// // Some points - /// let P = constants::ED25519_BASEPOINT_POINT; - /// let Q = P + P; - /// let R = P + Q; - /// - /// // A1 = a*P + b*Q + c*R - /// let abc = [a,b,c]; - /// let A1 = edwards::vartime::multiscalar_mul(&abc, &[P,Q,R]); - /// // Note: (&abc).into_iter(): Iterator - /// - /// // A2 = (-a)*P + (-b)*Q + (-c)*R - /// let minus_abc = abc.iter().map(|x| -x); - /// let A2 = edwards::vartime::multiscalar_mul(minus_abc, &[P,Q,R]); - /// // Note: minus_abc.into_iter(): Iterator - /// - /// assert_eq!(A1.compress(), (-A2).compress()); - /// ``` - #[cfg(any(feature = "alloc", feature = "std"))] - pub fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint - where I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, - { - // XXX later when we do more fancy multiscalar mults, we can delegate - // based on the iter's size hint -- hdevalence - // If we built with AVX2, use the AVX2 backend. - #[cfg(all(feature="avx2_backend", target_feature="avx2"))] - { - use backend::avx2::scalar_mul::vartime_straus::multiscalar_mul; - multiscalar_mul(scalars, points) - } - // Otherwise, proceed as normal: - #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] - { - use scalar_mul::vartime_straus::multiscalar_mul; - multiscalar_mul(scalars, points) - } - } - /// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint. - #[cfg(feature="stage2_build")] + /// + /// XXX eliminate this function when we have the precomputation API + #[cfg(feature = "stage2_build")] pub fn double_scalar_mul_basepoint(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint { // If we built with AVX2, use the AVX2 backend. #[cfg(all(feature="avx2_backend", target_feature="avx2"))] @@ -1211,7 +1150,7 @@ mod test { #[test] fn multiscalar_mul_vs_ed25519py() { let A = A_TIMES_BASEPOINT.decompress().unwrap(); - let result = vartime::multiscalar_mul( + let result = EdwardsPoint::vartime_multiscalar_mul( &[A_SCALAR, B_SCALAR], &[A, constants::ED25519_BASEPOINT_POINT] ); @@ -1221,11 +1160,11 @@ mod test { #[test] fn multiscalar_mul_vartime_vs_consttime() { let A = A_TIMES_BASEPOINT.decompress().unwrap(); - let result_vartime = vartime::multiscalar_mul( + let result_vartime = EdwardsPoint::vartime_multiscalar_mul( &[A_SCALAR, B_SCALAR], &[A, constants::ED25519_BASEPOINT_POINT] ); - let result_consttime = multiscalar_mul( + let result_consttime = EdwardsPoint::multiscalar_mul( &[A_SCALAR, B_SCALAR], &[A, constants::ED25519_BASEPOINT_POINT] ); diff --git a/src/ristretto.rs b/src/ristretto.rs index b293fb8..dca5ed5 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -186,7 +186,6 @@ use subtle::ConditionallyNegatable; use subtle::ConstantTimeEq; use subtle::Choice; -use edwards; use edwards::EdwardsPoint; use edwards::EdwardsBasepointTable; @@ -194,7 +193,7 @@ use scalar::Scalar; use curve_models::CompletedPoint; -use traits::Identity; +use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul}; // ------------------------------------------------------------------------ // Compressed points @@ -790,60 +789,47 @@ define_mul_assign_variants!(LHS = RistrettoPoint, RHS = Scalar); define_mul_variants!(LHS = RistrettoPoint, RHS = Scalar, Output = RistrettoPoint); define_mul_variants!(LHS = Scalar, RHS = RistrettoPoint, Output = RistrettoPoint); +// ------------------------------------------------------------------------ +// Multiscalar Multiplication impls +// ------------------------------------------------------------------------ + +// These use iterator combinators to unwrap the underlying points and +// forward to the EdwardsPoint implementations. -/// Given an iterator of (possibly secret) scalars and an iterator of -/// (possibly secret) points, compute -/// $$ -/// Q = c\_1 P\_1 + \cdots + c\_n P\_n. -/// $$ -/// -/// This function has the same behaviour as -/// `vartime::multiscalar_mul` but is constant-time. -/// -/// It is an error to call this function with two iterators of different lengths. -/// -/// # Examples -/// -/// The trait bound aims for maximum flexibility: the inputs must be -/// convertable to iterators (`I: IntoIter`), and the iterator's items -/// must be `Borrow` (or `Borrow`), to allow -/// iterators returning either `Scalar`s or `&Scalar`s. -/// -/// ``` -/// use curve25519_dalek::{constants, ristretto}; -/// use curve25519_dalek::scalar::Scalar; -/// -/// // Some scalars -/// let a = Scalar::from_u64(87329482); -/// let b = Scalar::from_u64(37264829); -/// let c = Scalar::from_u64(98098098); -/// -/// // Some points -/// let P = constants::RISTRETTO_BASEPOINT_POINT; -/// let Q = P + P; -/// let R = P + Q; -/// -/// // A1 = a*P + b*Q + c*R -/// let abc = [a,b,c]; -/// let A1 = ristretto::multiscalar_mul(&abc, &[P,Q,R]); -/// // Note: (&abc).into_iter(): Iterator -/// -/// // A2 = (-a)*P + (-b)*Q + (-c)*R -/// let minus_abc = abc.iter().map(|x| -x); -/// let A2 = ristretto::multiscalar_mul(minus_abc, &[P,Q,R]); -/// // Note: minus_abc.into_iter(): Iterator -/// -/// assert_eq!(A1.compress(), (-A2).compress()); -/// ``` #[cfg(any(feature = "alloc", feature = "std"))] -pub fn multiscalar_mul(scalars: I, points: J) -> RistrettoPoint - where I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, -{ - let extended_points = points.into_iter().map(|P| P.borrow().0); - RistrettoPoint(edwards::multiscalar_mul(scalars, extended_points)) +impl MultiscalarMul for RistrettoPoint { + type Point = RistrettoPoint; + + fn multiscalar_mul(scalars: I, points: J) -> RistrettoPoint + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, + { + let extended_points = points.into_iter().map(|P| P.borrow().0); + RistrettoPoint( + EdwardsPoint::multiscalar_mul(scalars, extended_points) + ) + } +} + +#[cfg(any(feature = "alloc", feature = "std"))] +impl VartimeMultiscalarMul for RistrettoPoint { + type Point = RistrettoPoint; + + fn vartime_multiscalar_mul(scalars: I, points: J) -> RistrettoPoint + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, + { + let extended_points = points.into_iter().map(|P| P.borrow().0); + RistrettoPoint( + EdwardsPoint::vartime_multiscalar_mul(scalars, extended_points) + ) + } } /// A precomputed table of multiples of a basepoint, used to accelerate @@ -947,69 +933,6 @@ impl Debug for RistrettoPoint { } } -// ------------------------------------------------------------------------ -// Variable-time functions -// ------------------------------------------------------------------------ - -pub mod vartime { - //! Variable-time operations on ristretto points, useful for non-secret data. - use super::*; - - /// Given an iterator of public scalars and an iterator of public points, compute - /// $$ - /// Q = c\_1 P\_1 + \cdots + c\_n P\_n. - /// $$ - /// - /// This function has the same behaviour as - /// `vartime::multiscalar_mul` but is constant-time. - /// - /// It is an error to call this function with two iterators of different lengths. - /// - /// # Examples - /// - /// The trait bound aims for maximum flexibility: the inputs must be - /// convertable to iterators (`I: IntoIter`), and the iterator's items - /// must be `Borrow` (or `Borrow`), to allow - /// iterators returning either `Scalar`s or `&Scalar`s. - /// - /// ``` - /// use curve25519_dalek::{constants, ristretto}; - /// use curve25519_dalek::scalar::Scalar; - /// - /// // Some scalars - /// let a = Scalar::from_u64(87329482); - /// let b = Scalar::from_u64(37264829); - /// let c = Scalar::from_u64(98098098); - /// - /// // Some points - /// let P = constants::RISTRETTO_BASEPOINT_POINT; - /// let Q = P + P; - /// let R = P + Q; - /// - /// // A1 = a*P + b*Q + c*R - /// let abc = [a,b,c]; - /// let A1 = ristretto::vartime::multiscalar_mul(&abc, &[P,Q,R]); - /// // Note: (&abc).into_iter(): Iterator - /// - /// // A2 = (-a)*P + (-b)*Q + (-c)*R - /// let minus_abc = abc.iter().map(|x| -x); - /// let A2 = ristretto::vartime::multiscalar_mul(minus_abc, &[P,Q,R]); - /// // Note: minus_abc.into_iter(): Iterator - /// - /// assert_eq!(A1.compress(), (-A2).compress()); - /// ``` - #[cfg(any(feature = "alloc", feature = "std"))] - pub fn multiscalar_mul(scalars: I, points: J) -> RistrettoPoint - where I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, - { - let extended_points = points.into_iter().map(|P| P.borrow().0); - RistrettoPoint(edwards::vartime::multiscalar_mul(scalars, extended_points)) - } -} - // ------------------------------------------------------------------------ // Tests // ------------------------------------------------------------------------ diff --git a/src/traits.rs b/src/traits.rs index d774833..e706348 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -10,8 +10,12 @@ //! Module for common traits. +use core::borrow::Borrow; + use subtle; +use scalar::Scalar; + // ------------------------------------------------------------------------ // Public Traits // ------------------------------------------------------------------------ @@ -32,12 +36,127 @@ pub trait IsIdentity { /// Implement generic identity equality testing for a point representations /// which have constant-time equality testing and a defined identity /// constructor. -impl IsIdentity for T where T: subtle::ConstantTimeEq + Identity { +impl IsIdentity for T +where + T: subtle::ConstantTimeEq + Identity, +{ fn is_identity(&self) -> bool { self.ct_eq(&T::identity()).unwrap_u8() == 1u8 } } +/// A trait for constant-time multiscalar multiplication without precomputation. +pub trait MultiscalarMul { + /// The type of point being multiplied, e.g., `RistrettoPoint`. + type Point; + + /// Given an iterator of (possibly secret) scalars and an iterator of + /// public points, compute + /// $$ + /// Q = c\_1 P\_1 + \cdots + c\_n P\_n. + /// $$ + /// + /// It is an error to call this function with two iterators of different lengths. + /// + /// # Examples + /// + /// The trait bound aims for maximum flexibility: the inputs must be + /// convertable to iterators (`I: IntoIter`), and the iterator's items + /// must be `Borrow` (or `Borrow`), to allow + /// iterators returning either `Scalar`s or `&Scalar`s. + /// + /// ``` + /// use curve25519_dalek::constants; + /// use curve25519_dalek::traits::MultiscalarMul; + /// use curve25519_dalek::ristretto::RistrettoPoint; + /// use curve25519_dalek::scalar::Scalar; + /// + /// // Some scalars + /// let a = Scalar::from_u64(87329482); + /// let b = Scalar::from_u64(37264829); + /// let c = Scalar::from_u64(98098098); + /// + /// // Some points + /// let P = constants::RISTRETTO_BASEPOINT_POINT; + /// let Q = P + P; + /// let R = P + Q; + /// + /// // A1 = a*P + b*Q + c*R + /// let abc = [a,b,c]; + /// let A1 = RistrettoPoint::multiscalar_mul(&abc, &[P,Q,R]); + /// // Note: (&abc).into_iter(): Iterator + /// + /// // A2 = (-a)*P + (-b)*Q + (-c)*R + /// let minus_abc = abc.iter().map(|x| -x); + /// let A2 = RistrettoPoint::multiscalar_mul(minus_abc, &[P,Q,R]); + /// // Note: minus_abc.into_iter(): Iterator + /// + /// assert_eq!(A1.compress(), (-A2).compress()); + /// ``` + fn multiscalar_mul(scalars: I, points: J) -> Self::Point + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow; +} + +/// A trait for variable-time multiscalar multiplication without precomputation. +pub trait VartimeMultiscalarMul { + /// The type of point being multiplied, e.g., `RistrettoPoint`. + type Point; + + /// Given an iterator of (possibly secret) scalars and an iterator of + /// public points, compute + /// $$ + /// Q = c\_1 P\_1 + \cdots + c\_n P\_n. + /// $$ + /// + /// It is an error to call this function with two iterators of different lengths. + /// + /// # Examples + /// + /// The trait bound aims for maximum flexibility: the inputs must be + /// convertable to iterators (`I: IntoIter`), and the iterator's items + /// must be `Borrow` (or `Borrow`), to allow + /// iterators returning either `Scalar`s or `&Scalar`s. + /// + /// ``` + /// use curve25519_dalek::constants; + /// use curve25519_dalek::traits::MultiscalarMul; + /// use curve25519_dalek::ristretto::RistrettoPoint; + /// use curve25519_dalek::scalar::Scalar; + /// + /// // Some scalars + /// let a = Scalar::from_u64(87329482); + /// let b = Scalar::from_u64(37264829); + /// let c = Scalar::from_u64(98098098); + /// + /// // Some points + /// let P = constants::RISTRETTO_BASEPOINT_POINT; + /// let Q = P + P; + /// let R = P + Q; + /// + /// // A1 = a*P + b*Q + c*R + /// let abc = [a,b,c]; + /// let A1 = RistrettoPoint::multiscalar_mul(&abc, &[P,Q,R]); + /// // Note: (&abc).into_iter(): Iterator + /// + /// // A2 = (-a)*P + (-b)*Q + (-c)*R + /// let minus_abc = abc.iter().map(|x| -x); + /// let A2 = RistrettoPoint::multiscalar_mul(minus_abc, &[P,Q,R]); + /// // Note: minus_abc.into_iter(): Iterator + /// + /// assert_eq!(A1.compress(), (-A2).compress()); + /// ``` + fn vartime_multiscalar_mul(scalars: I, points: J) -> Self::Point + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow; +} + // ------------------------------------------------------------------------ // Private Traits // ------------------------------------------------------------------------ From 149c5004e8bee6d82c8d4d5fc3b42f6daf57c035 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 2 May 2018 17:20:58 -0700 Subject: [PATCH 5/9] Use multiscalar traits for the backend implementations. --- src/backend/avx2/scalar_mul/mod.rs | 4 - src/backend/avx2/scalar_mul/straus.rs | 116 ++++++--- src/backend/avx2/scalar_mul/vartime_straus.rs | 51 ---- src/edwards.rs | 16 +- src/scalar_mul/mod.rs | 4 - src/scalar_mul/straus.rs | 234 +++++++++++++----- src/scalar_mul/vartime_straus.rs | 54 ---- 7 files changed, 262 insertions(+), 217 deletions(-) delete mode 100644 src/backend/avx2/scalar_mul/vartime_straus.rs delete mode 100644 src/scalar_mul/vartime_straus.rs diff --git a/src/backend/avx2/scalar_mul/mod.rs b/src/backend/avx2/scalar_mul/mod.rs index f59cba2..d3d14e3 100644 --- a/src/backend/avx2/scalar_mul/mod.rs +++ b/src/backend/avx2/scalar_mul/mod.rs @@ -13,8 +13,4 @@ pub mod variable_base; #[cfg(feature = "stage2_build")] pub mod vartime_double_base; -#[cfg(any(feature = "alloc", feature = "std"))] pub mod straus; - -#[cfg(any(feature = "alloc", feature = "std"))] -pub mod vartime_straus; diff --git a/src/backend/avx2/scalar_mul/straus.rs b/src/backend/avx2/scalar_mul/straus.rs index 59e123e..52c3079 100644 --- a/src/backend/avx2/scalar_mul/straus.rs +++ b/src/backend/avx2/scalar_mul/straus.rs @@ -7,48 +7,98 @@ // Authors: // - Isis Agora Lovecruft // - Henry de Valence -#![allow(non_snake_case)] use core::borrow::Borrow; use clear_on_drop::ClearOnDrop; -use traits::Identity; -use scalar::Scalar; -use edwards::EdwardsPoint; -use scalar_mul::window::LookupTable; use backend::avx2::edwards::{CachedPoint, ExtendedPoint}; +use edwards::EdwardsPoint; +use scalar::Scalar; +use scalar_mul::window::{LookupTable, NafLookupTable5}; +use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul}; -/// Perform constant-time, variable-base scalar multiplication. -pub(crate) fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint -where - I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, -{ - // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] - // for each input point P - let lookup_tables: Vec<_> = points - .into_iter() - .map(|point| LookupTable::::from(point.borrow())) - .collect(); +/// Multiscalar multiplication using interleaved window / Straus' +/// method. See the `Straus` struct in the serial backend for more +/// details. +/// +/// This exists as a seperate implementation from that one because the +/// AVX2 code uses different curve models (it does not pass between +/// multiple models during scalar mul), and it has to convert the +/// point representation on the fly. +pub struct Straus {} - let scalar_digits_vec: Vec<_> = scalars - .into_iter() - .map(|s| s.borrow().to_radix_16()) - .collect(); - // Pass ownership to a ClearOnDrop wrapper - let scalar_digits = ClearOnDrop::new(scalar_digits_vec); +#[cfg(any(feature = "alloc", feature = "std"))] +impl MultiscalarMul for Straus { + type Point = EdwardsPoint; - let mut Q = ExtendedPoint::identity(); - for j in (0..64).rev() { - Q = Q.mul_by_pow_2(4); - let it = scalar_digits.iter().zip(lookup_tables.iter()); - for (s_i, lookup_table_i) in it { - // Q = Q + s_{i,j} * P_i - Q = &Q + &lookup_table_i.select(s_i[j]); + fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, + { + // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] + // for each input point P + let lookup_tables: Vec<_> = points + .into_iter() + .map(|point| LookupTable::::from(point.borrow())) + .collect(); + + let scalar_digits_vec: Vec<_> = scalars + .into_iter() + .map(|s| s.borrow().to_radix_16()) + .collect(); + // Pass ownership to a ClearOnDrop wrapper + let scalar_digits = ClearOnDrop::new(scalar_digits_vec); + + let mut Q = ExtendedPoint::identity(); + for j in (0..64).rev() { + Q = Q.mul_by_pow_2(4); + let it = scalar_digits.iter().zip(lookup_tables.iter()); + for (s_i, lookup_table_i) in it { + // Q = Q + s_{i,j} * P_i + Q = &Q + &lookup_table_i.select(s_i[j]); + } } + Q.into() + } +} + +#[cfg(any(feature = "alloc", feature = "std"))] +impl VartimeMultiscalarMul for Straus { + type Point = EdwardsPoint; + + fn vartime_multiscalar_mul(scalars: I, points: J) -> EdwardsPoint + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, + { + let nafs: Vec<_> = scalars + .into_iter() + .map(|c| c.borrow().non_adjacent_form(5)) + .collect(); + let lookup_tables: Vec<_> = points + .into_iter() + .map(|point| NafLookupTable5::::from(point.borrow())) + .collect(); + + let mut Q = ExtendedPoint::identity(); + + for i in (0..255).rev() { + Q = Q.double(); + + for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) { + if naf[i] > 0 { + Q = &Q + &lookup_table.select(naf[i] as usize); + } else if naf[i] < 0 { + Q = &Q - &lookup_table.select(-naf[i] as usize); + } + } + } + Q.into() } - Q.into() } diff --git a/src/backend/avx2/scalar_mul/vartime_straus.rs b/src/backend/avx2/scalar_mul/vartime_straus.rs deleted file mode 100644 index 30e68d0..0000000 --- a/src/backend/avx2/scalar_mul/vartime_straus.rs +++ /dev/null @@ -1,51 +0,0 @@ -// -*- mode: rust; -*- -// -// This file is part of curve25519-dalek. -// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence -// See LICENSE for licensing information. -// -// Authors: -// - Isis Agora Lovecruft -// - Henry de Valence -#![allow(non_snake_case)] - -use core::borrow::Borrow; - -use traits::Identity; -use scalar::Scalar; -use edwards::EdwardsPoint; -use scalar_mul::window::NafLookupTable5; -use backend::avx2::edwards::{CachedPoint, ExtendedPoint}; - -/// Perform variable-time, variable-base scalar multiplication. -pub(crate) fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint -where - I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, -{ - let nafs: Vec<_> = scalars - .into_iter() - .map(|c| c.borrow().non_adjacent_form(5)) - .collect(); - let lookup_tables: Vec<_> = points - .into_iter() - .map(|point| NafLookupTable5::::from(point.borrow())) - .collect(); - - let mut Q = ExtendedPoint::identity(); - - for i in (0..255).rev() { - Q = Q.double(); - - for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) { - if naf[i] > 0 { - Q = &Q + &lookup_table.select(naf[i] as usize); - } else if naf[i] < 0 { - Q = &Q - &lookup_table.select(-naf[i] as usize); - } - } - } - Q.into() -} diff --git a/src/edwards.rs b/src/edwards.rs index 27b6154..36076cd 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -543,14 +543,14 @@ impl MultiscalarMul for EdwardsPoint { // If we built with AVX2, use the AVX2 backend. #[cfg(all(feature="avx2_backend", target_feature="avx2"))] { - use backend::avx2::scalar_mul::straus::multiscalar_mul; - multiscalar_mul(scalars, points) + use backend::avx2::scalar_mul::straus::Straus; + Straus::multiscalar_mul(scalars, points) } // Otherwise, proceed as normal: #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] { - use scalar_mul::straus::multiscalar_mul; - multiscalar_mul(scalars, points) + use scalar_mul::straus::Straus; + Straus::multiscalar_mul(scalars, points) } } } @@ -572,14 +572,14 @@ impl VartimeMultiscalarMul for EdwardsPoint { // If we built with AVX2, use the AVX2 backend. #[cfg(all(feature="avx2_backend", target_feature="avx2"))] { - use backend::avx2::scalar_mul::vartime_straus::multiscalar_mul; - multiscalar_mul(scalars, points) + use backend::avx2::scalar_mul::straus::Straus; + Straus::vartime_multiscalar_mul(scalars, points) } // Otherwise, proceed as normal: #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] { - use scalar_mul::vartime_straus::multiscalar_mul; - multiscalar_mul(scalars, points) + use scalar_mul::straus::Straus; + Straus::vartime_multiscalar_mul(scalars, points) } } } diff --git a/src/scalar_mul/mod.rs b/src/scalar_mul/mod.rs index 78ba7cd..1c756ab 100644 --- a/src/scalar_mul/mod.rs +++ b/src/scalar_mul/mod.rs @@ -15,8 +15,4 @@ pub mod variable_base; #[cfg(feature = "stage2_build")] pub mod vartime_double_base; -#[cfg(any(feature = "alloc", feature = "std"))] pub mod straus; - -#[cfg(any(feature = "alloc", feature = "std"))] -pub mod vartime_straus; diff --git a/src/scalar_mul/straus.rs b/src/scalar_mul/straus.rs index e448ee9..a65dce8 100644 --- a/src/scalar_mul/straus.rs +++ b/src/scalar_mul/straus.rs @@ -7,77 +7,185 @@ // Authors: // - Isis Agora Lovecruft // - Henry de Valence + #![allow(non_snake_case)] use core::borrow::Borrow; -use clear_on_drop::ClearOnDrop; - -use traits::Identity; -use scalar::Scalar; use edwards::EdwardsPoint; -use curve_models::ProjectiveNielsPoint; -use scalar_mul::window::LookupTable; +use scalar::Scalar; +use traits::MultiscalarMul; +use traits::VartimeMultiscalarMul; -/// Perform constant-time, variable-base scalar multiplication. -pub(crate) fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint -where - I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, -{ - // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] - // for each input point P - let lookup_tables: Vec<_> = points - .into_iter() - .map(|point| LookupTable::::from(point.borrow())) - .collect(); +/// Perform multiscalar multiplication by the interleaved window +/// method, also known as Straus' method (since it was apparently +/// [first published][solution] by Straus in 1964, as a solution to [a +/// problem][problem] posted in the American Mathematical Monthly in +/// 1963). +/// +/// It is easy enough to reinvent, and has been repeatedly. The basic +/// idea is that when computing +/// \\[ +/// Q = s_1 P_1 + \cdots + s_n P_n +/// \\] +/// by means of additions and doublings, the doublings can be shared +/// across the \\( P_i \\\). +/// +/// We implement two versions, a constant-time algorithm using fixed +/// windows and a variable-time algorithm using sliding windows. They +/// are slight variations on the same idea, and are described in more +/// detail in the respective implementations. +/// +/// [solution]: https://www.jstor.org/stable/2310929 +/// [problem]: https://www.jstor.org/stable/2312273 +pub struct Straus {} - // Setting s_i = i-th scalar, compute - // - // s_i = s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63, - // - // with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`. - // - // This puts the scalar digits into a heap-allocated Vec. - // To ensure that these are erased, pass ownership of the Vec into a - // ClearOnDrop wrapper. - let scalar_digits_vec: Vec<_> = scalars - .into_iter() - .map(|s| s.borrow().to_radix_16()) - .collect(); - let scalar_digits = ClearOnDrop::new(scalar_digits_vec); +#[cfg(any(feature = "alloc", feature = "std"))] +impl MultiscalarMul for Straus { + type Point = EdwardsPoint; - // Compute s_1*P_1 + ... + s_n*P_n: since - // - // s_i*P_i = P_i*(s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63) - // s_i*P_i = P_i*s_{i,0} + P_i*s_{i,1}*16^1 + ... + P_i*s_{i,63}*16^63 - // s_i*P_i = P_i*s_{i,0} + 16*(P_i*s_{i,1} + 16*( ... + 16*P_i*s_{i,63})...) - // - // we have the two-dimensional sum - // - // s_1*P_1 = P_1*s_{1,0} + 16*(P_1*s_{1,1} + 16*( ... + 16*P_1*s_{1,63})...) - // + s_2*P_2 = + P_2*s_{2,0} + 16*(P_2*s_{2,1} + 16*( ... + 16*P_2*s_{2,63})...) - // ... - // + s_n*P_n = + P_n*s_{n,0} + 16*(P_n*s_{n,1} + 16*( ... + 16*P_n*s_{n,63})...) - // - // We sum column-wise top-to-bottom, then right-to-left, - // multiplying by 16 only once per column. - // - // This provides the speedup over doing n independent scalar - // mults: we perform 63 multiplications by 16 instead of 63*n - // multiplications, saving 252*(n-1) doublings. - let mut Q = EdwardsPoint::identity(); - for j in (0..64).rev() { - Q = Q.mul_by_pow_2(4); - let it = scalar_digits.iter().zip(lookup_tables.iter()); - for (s_i, lookup_table_i) in it { - // R_i = s_{i,j} * P_i - let R_i = lookup_table_i.select(s_i[j]); - // Q = Q + R_i - Q = (&Q + &R_i).to_extended(); + /// Constant-time Straus using a fixed window of size \\(4\\). + /// + /// Our goal is to compute + /// \\[ + /// Q = s_1 P_1 + \cdots + s_n P_n. + /// \\] + /// + /// For each point \\( P_i \\), precompute a lookup table of + /// \\[ + /// P_i, 2P_i, 3P_i, 4P_i, 5P_i, 6P_i, 7P_i, 8P_i. + /// \\] + /// + /// For each scalar \\( s_i \\), compute its radix-\\(2^4\\) + /// signed digits \\( s_{i,j} \\), i.e., + /// \\[ + /// s_i = s_{i,0} + s_{i,1} 16^1 + ... + s_{i,63} 16^{63}, + /// \\] + /// with \\( -8 \leq s_{i,j} < 8 \\). Since \\( 0 \leq |s_{i,j}| + /// \leq 8 \\), we can retrieve \\( s_{i,j} P_i \\) from the + /// lookup table with a conditional negation: using signed + /// digits halves the required table size. + /// + /// Then as in the single-base fixed window case, we have + /// \\[ + /// \begin{aligned} + /// s_i P_i &= P_i (s_{i,0} + s_{i,1} 16^1 + \cdots + s_{i,63} 16^{63}) \\\\ + /// s_i P_i &= P_i s_{i,0} + P_i s_{i,1} 16^1 + \cdots + P_i s_{i,63} 16^{63} \\\\ + /// s_i P_i &= P_i s_{i,0} + 16(P_i s_{i,1} + 16( \cdots +16P_i s_{i,63})\cdots ) + /// \end{aligned} + /// \\] + /// so each \\( s_i P_i \\) can be computed by alternately adding + /// a precomputed multiple \\( P_i s_{i,j} \\) of \\( P_i \\) and + /// repeatedly doubling. + /// + /// Now consider the two-dimensional sum + /// \\[ + /// \begin{aligned} + /// s\_1 P\_1 &=& P\_1 s\_{1,0} &+& 16 (P\_1 s\_{1,1} &+& 16 ( \cdots &+& 16 P\_1 s\_{1,63}&) \cdots ) \\\\ + /// + & & + & & + & & & & + & \\\\ + /// s\_2 P\_2 &=& P\_2 s\_{2,0} &+& 16 (P\_2 s\_{2,1} &+& 16 ( \cdots &+& 16 P\_2 s\_{2,63}&) \cdots ) \\\\ + /// + & & + & & + & & & & + & \\\\ + /// \vdots & & \vdots & & \vdots & & & & \vdots & \\\\ + /// + & & + & & + & & & & + & \\\\ + /// s\_n P\_n &=& P\_n s\_{n,0} &+& 16 (P\_n s\_{n,1} &+& 16 ( \cdots &+& 16 P\_n s\_{n,63}&) \cdots ) + /// \end{aligned} + /// \\] + /// The sum of the left-hand column is the result \\( Q \\); by + /// computing the two-dimensional sum on the right column-wise, + /// top-to-bottom, then right-to-left, we need to multiply by \\( + /// 16\\) only once per column, sharing the doublings across all + /// of the input points. + fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, + { + use clear_on_drop::ClearOnDrop; + + use curve_models::ProjectiveNielsPoint; + use scalar_mul::window::LookupTable; + use traits::Identity; + + let lookup_tables: Vec<_> = points + .into_iter() + .map(|point| LookupTable::::from(point.borrow())) + .collect(); + + // This puts the scalar digits into a heap-allocated Vec. + // To ensure that these are erased, pass ownership of the Vec into a + // ClearOnDrop wrapper. + let scalar_digits_vec: Vec<_> = scalars + .into_iter() + .map(|s| s.borrow().to_radix_16()) + .collect(); + let scalar_digits = ClearOnDrop::new(scalar_digits_vec); + + let mut Q = EdwardsPoint::identity(); + for j in (0..64).rev() { + Q = Q.mul_by_pow_2(4); + let it = scalar_digits.iter().zip(lookup_tables.iter()); + for (s_i, lookup_table_i) in it { + // R_i = s_{i,j} * P_i + let R_i = lookup_table_i.select(s_i[j]); + // Q = Q + R_i + Q = (&Q + &R_i).to_extended(); + } } + Q + } +} + +#[cfg(any(feature = "alloc", feature = "std"))] +impl VartimeMultiscalarMul for Straus { + type Point = EdwardsPoint; + + /// Variable-time Straus using a non-adjacent form of width \\(5\\). + /// + /// This is completely similar to the constant-time code, but we + /// use a non-adjacent form for the scalar, and do not do table + /// lookups in constant time. + /// + /// The non-adjacent form has signed, odd digits. Using only odd + /// digits halves the table size (since we only need odd + /// multiples), or gives fewer additions for the same table size. + fn vartime_multiscalar_mul(scalars: I, points: J) -> EdwardsPoint + where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, + { + use curve_models::{CompletedPoint, ProjectiveNielsPoint, ProjectivePoint}; + use scalar_mul::window::NafLookupTable5; + use traits::Identity; + + let nafs: Vec<_> = scalars + .into_iter() + .map(|c| c.borrow().non_adjacent_form(5)) + .collect(); + let lookup_tables: Vec<_> = points + .into_iter() + .map(|P| NafLookupTable5::::from(P.borrow())) + .collect(); + + let mut r = ProjectivePoint::identity(); + + for i in (0..255).rev() { + let mut t: CompletedPoint = r.double(); + + for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) { + if naf[i] > 0 { + t = &t.to_extended() + &lookup_table.select(naf[i] as usize); + } else if naf[i] < 0 { + t = &t.to_extended() - &lookup_table.select(-naf[i] as usize); + } + } + + r = t.to_projective(); + } + + r.to_extended() } - Q } diff --git a/src/scalar_mul/vartime_straus.rs b/src/scalar_mul/vartime_straus.rs deleted file mode 100644 index 03c1f75..0000000 --- a/src/scalar_mul/vartime_straus.rs +++ /dev/null @@ -1,54 +0,0 @@ -// -*- mode: rust; -*- -// -// This file is part of curve25519-dalek. -// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence -// See LICENSE for licensing information. -// -// Authors: -// - Isis Agora Lovecruft -// - Henry de Valence -#![allow(non_snake_case)] - -use core::borrow::Borrow; - -use traits::Identity; -use scalar::Scalar; -use edwards::EdwardsPoint; -use curve_models::{CompletedPoint, ProjectivePoint, ProjectiveNielsPoint}; -use scalar_mul::window::NafLookupTable5; - -/// Perform variable-time, variable-base scalar multiplication. -pub(crate) fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint -where - I: IntoIterator, - I::Item: Borrow, - J: IntoIterator, - J::Item: Borrow, -{ - let nafs: Vec<_> = scalars - .into_iter() - .map(|c| c.borrow().non_adjacent_form(5)) - .collect(); - let lookup_tables: Vec<_> = points - .into_iter() - .map(|P| NafLookupTable5::::from(P.borrow())) - .collect(); - - let mut r = ProjectivePoint::identity(); - - for i in (0..255).rev() { - let mut t: CompletedPoint = r.double(); - - for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) { - if naf[i] > 0 { - t = &t.to_extended() + &lookup_table.select(naf[i] as usize); - } else if naf[i] < 0 { - t = &t.to_extended() - &lookup_table.select(-naf[i] as usize); - } - } - - r = t.to_projective(); - } - - r.to_extended() -} From 2eed24109eff01ee974fbd1397170e13a939e669 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 15 May 2018 11:15:53 -0700 Subject: [PATCH 6/9] Move double-base scmul to the EdwardsPoint type --- benches/dalek_benchmarks.rs | 2 +- src/edwards.rs | 54 +++++++++++++++---------------------- 2 files changed, 23 insertions(+), 33 deletions(-) diff --git a/benches/dalek_benchmarks.rs b/benches/dalek_benchmarks.rs index 35d1a24..0248fdf 100644 --- a/benches/dalek_benchmarks.rs +++ b/benches/dalek_benchmarks.rs @@ -59,7 +59,7 @@ mod edwards_benches { let a = Scalar::from_u64(298374928).invert(); let b = Scalar::from_u64(897987897).invert(); let A = B * (b * a); - bench.iter(|| edwards::vartime::double_scalar_mul_basepoint(&a, &A, &b)); + bench.iter(|| EdwardsPoint::vartime_double_scalar_mul_basepoint(&a, &A, &b)); }); } diff --git a/src/edwards.rs b/src/edwards.rs index 36076cd..5f03929 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -584,6 +584,27 @@ impl VartimeMultiscalarMul for EdwardsPoint { } } +impl EdwardsPoint { + /// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint. + /// + /// XXX eliminate this function when we have the precomputation API + #[cfg(feature = "stage2_build")] + pub fn vartime_double_scalar_mul_basepoint(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint { + // If we built with AVX2, use the AVX2 backend. + #[cfg(all(feature="avx2_backend", target_feature="avx2"))] + { + use backend::avx2::scalar_mul::vartime_double_base::mul; + mul(a, A, b) + } + // Otherwise, proceed as normal: + #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] + { + use scalar_mul::vartime_double_base::mul; + mul(a, A, b) + } + } +} + /// A precomputed table of multiples of a basepoint, for accelerating /// fixed-base scalar multiplication. One table, for the Ed25519 /// basepoint, is provided in the `constants` module. @@ -783,37 +804,6 @@ impl Debug for EdwardsBasepointTable { } } -// ------------------------------------------------------------------------ -// Variable-time functions -// ------------------------------------------------------------------------ - -/// Variable-time operations on curve points, useful for non-secret data. -/// -/// XXX delete this whole module -pub mod vartime { - //! Variable-time operations on curve points, useful for non-secret data. - use super::*; - - /// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint. - /// - /// XXX eliminate this function when we have the precomputation API - #[cfg(feature = "stage2_build")] - pub fn double_scalar_mul_basepoint(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint { - // If we built with AVX2, use the AVX2 backend. - #[cfg(all(feature="avx2_backend", target_feature="avx2"))] - { - use backend::avx2::scalar_mul::vartime_double_base::mul; - mul(a, A, b) - } - // Otherwise, proceed as normal: - #[cfg(not(all(feature="avx2_backend", target_feature="avx2")))] - { - use scalar_mul::vartime_double_base::mul; - mul(a, A, b) - } - } -} - // ------------------------------------------------------------------------ // Tests // ------------------------------------------------------------------------ @@ -1143,7 +1133,7 @@ mod test { #[test] fn double_scalar_mul_basepoint_vs_ed25519py() { let A = A_TIMES_BASEPOINT.decompress().unwrap(); - let result = vartime::double_scalar_mul_basepoint(&A_SCALAR, &A, &B_SCALAR); + let result = EdwardsPoint::vartime_double_scalar_mul_basepoint(&A_SCALAR, &A, &B_SCALAR); assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT); } From bab642c5afcf14bff0317f3d09f5f481aabe0f57 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 15 May 2018 11:22:21 -0700 Subject: [PATCH 7/9] Add doc comments --- src/scalar_mul/mod.rs | 8 ++++++++ src/scalar_mul/straus.rs | 2 ++ 2 files changed, 10 insertions(+) diff --git a/src/scalar_mul/mod.rs b/src/scalar_mul/mod.rs index 1c756ab..3ebe25f 100644 --- a/src/scalar_mul/mod.rs +++ b/src/scalar_mul/mod.rs @@ -8,6 +8,14 @@ // - Isis Agora Lovecruft // - Henry de Valence +//! Implementations of various scalar multiplication algorithms. +//! +//! Note that all of these implementations use serial code for field +//! arithmetic with the multi-model strategy described in the +//! `curve_models` module. The vectorized AVX2 backend has its own +//! scalar multiplication implementations, since it only uses one +//! curve model. + pub mod window; pub mod variable_base; diff --git a/src/scalar_mul/straus.rs b/src/scalar_mul/straus.rs index a65dce8..21bf29e 100644 --- a/src/scalar_mul/straus.rs +++ b/src/scalar_mul/straus.rs @@ -8,6 +8,8 @@ // - Isis Agora Lovecruft // - Henry de Valence +//! Implementation of the interleaved window method, also known as Straus' method. + #![allow(non_snake_case)] use core::borrow::Borrow; From bbb64312f7bbde054b83c8995961c7642d95c292 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 14 May 2018 13:24:23 -0700 Subject: [PATCH 8/9] Use rand 0.5 Requires `0.5.0-pre.2`, which adds `impl CryptoRng for OsRng`. --- Cargo.toml | 11 +++++------ benches/dalek_benchmarks.rs | 2 +- src/lib.rs | 6 +----- src/montgomery.rs | 2 +- src/ristretto.rs | 14 ++++++-------- src/scalar.rs | 9 ++++----- 6 files changed, 18 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index df8c644..db4db71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,28 +41,27 @@ harness = false # match exactly, since the build.rs uses the crate itself as a library. [dependencies] -byteorder = {version = "1", default-features = false } +rand = { version = "0.5.0-pre.2", default-features = false } +byteorder = { version = "1", default-features = false } digest = "0.7" generic-array = "0.9" clear_on_drop = "=0.2.3" subtle = { version = "0.6", features = ["generic-impls"], default-features = false } serde = { version = "1.0", optional = true } -rand = { version = "0.4", optional = true } [build-dependencies] -byteorder = "1" +rand = { version = "0.5.0-pre.2", default-features = false } +byteorder = { version = "1", default-features = false } digest = "0.7" generic-array = "0.9" clear_on_drop = "=0.2.3" subtle = { version = "0.6", features = ["generic-impls"], default-features = false } serde = { version = "1.0", optional = true } -# Allowing rand to be optional during builds causes a build failure when compiling for no_std targets -rand = { version = "0.4", optional = false } [features] nightly = ["subtle/nightly", "clear_on_drop/nightly"] default = ["std", "u64_backend"] -std = ["rand", "subtle/std"] +std = ["subtle/std", "rand/std"] alloc = [] yolocrypto = [] diff --git a/benches/dalek_benchmarks.rs b/benches/dalek_benchmarks.rs index 353f4bf..0e935d7 100644 --- a/benches/dalek_benchmarks.rs +++ b/benches/dalek_benchmarks.rs @@ -1,7 +1,7 @@ #![allow(non_snake_case)] extern crate rand; -use rand::OsRng; +use rand::rngs::OsRng; #[macro_use] extern crate criterion; diff --git a/src/lib.rs b/src/lib.rs index 3761540..4ebffe7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,15 +34,11 @@ #[cfg(feature = "std")] extern crate core; - -#[cfg(feature = "std")] -extern crate rand; - #[cfg(feature = "alloc")] extern crate alloc; +extern crate rand; extern crate clear_on_drop; - extern crate byteorder; // The `Digest` trait is implemented using `generic_array`, so we need it diff --git a/src/montgomery.rs b/src/montgomery.rs index ad03c69..0b8450d 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -284,7 +284,7 @@ mod test { use constants; use super::*; - use rand::OsRng; + use rand::rngs::OsRng; /// Test Montgomery -> Edwards on the X/Ed25519 basepoint #[test] diff --git a/src/ristretto.rs b/src/ristretto.rs index b293fb8..9c0e8af 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -172,8 +172,7 @@ use core::ops::{Mul, MulAssign}; use core::iter::Sum; use core::borrow::Borrow; -#[cfg(feature = "std")] -use rand::Rng; +use rand::{Rng, CryptoRng}; use digest::Digest; use generic_array::typenum::U64; @@ -406,7 +405,7 @@ impl RistrettoPoint { /// # extern crate curve25519_dalek; /// # use curve25519_dalek::ristretto::RistrettoPoint; /// extern crate rand; - /// use rand::OsRng; + /// use rand::rngs::OsRng; /// /// # // Need fn main() here in comment so the doctest compiles /// # // See https://doc.rust-lang.org/book/documentation.html#documentation-as-tests @@ -576,15 +575,14 @@ impl RistrettoPoint { /// discrete log of the output point with respect to any other /// point should be unknown. The map is applied twice and the /// results are added, to ensure a uniform distribution. - #[cfg(feature = "std")] - pub fn random(rng: &mut T) -> Self { + pub fn random(rng: &mut T) -> Self { let mut field_bytes = [0u8; 32]; - rng.fill_bytes(&mut field_bytes); + rng.fill(&mut field_bytes); let r_1 = FieldElement::from_bytes(&field_bytes); let R_1 = RistrettoPoint::elligator_ristretto_flavor(&r_1); - rng.fill_bytes(&mut field_bytes); + rng.fill(&mut field_bytes); let r_2 = FieldElement::from_bytes(&field_bytes); let R_2 = RistrettoPoint::elligator_ristretto_flavor(&r_2); @@ -1016,7 +1014,7 @@ pub mod vartime { #[cfg(all(test, feature = "stage2_build"))] mod test { - use rand::OsRng; + use rand::rngs::OsRng; use scalar::Scalar; use constants; diff --git a/src/scalar.rs b/src/scalar.rs index 22e9e9d..5b67732 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -22,8 +22,7 @@ use core::cmp::{Eq, PartialEq}; use core::iter::{Product, Sum}; use core::borrow::Borrow; -#[cfg(feature = "std")] -use rand::Rng; +use rand::{Rng, CryptoRng}; use digest::Digest; use generic_array::typenum::U64; @@ -324,15 +323,15 @@ impl Scalar { /// /// # Inputs /// - /// * `rng`: any RNG which implements the `rand::Rng` interface. + /// * `rng`: any RNG which implements the `rand::CryptoRng` interface. /// /// # Returns /// /// A random scalar within ℤ/lℤ. #[cfg(feature = "std")] - pub fn random(rng: &mut T) -> Self { + pub fn random(rng: &mut T) -> Self { let mut scalar_bytes = [0u8; 64]; - rng.fill_bytes(&mut scalar_bytes); + rng.fill(&mut scalar_bytes); Scalar::from_bytes_mod_order_wide(&scalar_bytes) } From 337de2a204e76c179c756eb60bb90252cf0e368e Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 15 May 2018 20:34:30 +0000 Subject: [PATCH 9/9] Bump curve25519-dalek version to 0.17.0. --- Cargo.toml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index db4db71..dca1a7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "curve25519-dalek" -version = "0.16.4" +version = "0.17.0" authors = ["Isis Lovecruft ", "Henry de Valence "] readme = "README.md" diff --git a/README.md b/README.md index 15a926b..7dba401 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ make doc-internal To import `curve25519-dalek`, add the following to the dependencies section of your project's `Cargo.toml`: ```toml -curve25519-dalek = "^0.16" +curve25519-dalek = "^0.17" ``` Then import the crate as: ```rust,no_run