From 4a4ec74100975f38c3776f50f94fa6e32e676d88 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 20 Feb 2018 17:32:50 -0800 Subject: [PATCH 01/23] Add example to edwards::multiscalar_mult --- src/edwards.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/edwards.rs b/src/edwards.rs index 4a4914e..8d242ab 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -537,6 +537,25 @@ impl<'a, 'b> Mul<&'b EdwardsPoint> for &'a Scalar { /// A iterable of `Scalar`s and a iterable of `EdwardsPoints`. It is an /// error to call this function with two iterators of different lengths. /// +/// # Examples +/// ``` +/// 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 A1 = edwards::multiscalar_mult(&[a,b,c], &[P,Q,R]); +/// ``` +/// // XXX later when we do more fancy multiscalar mults, we can delegate // based on the iter's size hint -- hdevalence #[cfg(any(feature = "alloc", feature = "std"))] From 0f185d3e289bbd540672062c287a7811edd62af1 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 20 Feb 2018 18:10:40 -0800 Subject: [PATCH 02/23] Generalize trait bounds on multiscalar multiplication. This allows iterators returning either &Scalars or Scalars, so that it's possible to use map() and friends to adjust scalars as they're being fed into the multiscalar multiplication. --- src/backend/avx2/edwards.rs | 51 ++++++----------- src/edwards.rs | 93 ++++++++++++++++++++++++------- src/ristretto.rs | 108 +++++++++++++++++++++++++++++------- 3 files changed, 176 insertions(+), 76 deletions(-) diff --git a/src/backend/avx2/edwards.rs b/src/backend/avx2/edwards.rs index 1a58133..b5f3245 100644 --- a/src/backend/avx2/edwards.rs +++ b/src/backend/avx2/edwards.rs @@ -453,32 +453,19 @@ impl EdwardsBasepointTable { } } -/// Given a vector of (possibly secret) scalars and a vector of -/// (possibly secret) points, compute `c_1 P_1 + ... + c_n P_n`. -/// -/// This function has the same behaviour as -/// `vartime::multiscalar_mult` but is constant-time. -/// -/// # Input -/// -/// A vector of `Scalar`s and a vector of `EdwardsPoints`. It is an -/// error to call this function with two vectors of different lengths. -/// -/// XXX this takes `edwards::EdwardsPoints` because we have to alloc scratch space here anyways, -/// and we need some space to store the converted points, so we may as well do the conversion here. -/// maybe there's a better way to avoid code duplication... however we can't quite just write a -/// generic `multiscalar_mult` because the non-vectorized code passes between models and this code -/// doesn't. +/// Internal multiscalar code. #[cfg(any(feature = "alloc", feature = "std"))] -pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> edwards::EdwardsPoint - where I: IntoIterator, - J: IntoIterator +pub fn multiscalar_mult(scalars: I, points: J) -> edwards::EdwardsPoint + where I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, { //assert_eq!(scalars.len(), points.len()); use clear_on_drop::ClearOnDrop; let lookup_tables_vec: Vec<_> = points.into_iter() - .map(|P| LookupTable::from(ExtendedPoint::from(*P)) ) + .map(|P| LookupTable::from(ExtendedPoint::from(*P.borrow())) ) .collect(); let lookup_tables = ClearOnDrop::new(lookup_tables_vec); @@ -489,7 +476,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> edwards::Edwards // // with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`. let scalar_digits_vec: Vec<_> = scalars.into_iter() - .map(|c| c.to_radix_16()) + .map(|c| c.borrow().to_radix_16()) .collect(); // The above puts the scalar digits into a heap-allocated Vec. @@ -606,27 +593,21 @@ pub mod vartime { Q.into() } - /// Given a vector of public scalars and a vector of public points, compute - /// $$ - /// Q = c\_1 P\_1 + \cdots + c\_n P\_n. - /// $$ - /// - /// # Input - /// - /// A vector of `Scalar`s and a vector of `EdwardsPoints`. It is an - /// error to call this function with two vectors of different lengths. + /// Internal multiscalar function #[cfg(any(feature = "alloc", feature = "std"))] - pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> edwards::EdwardsPoint - where I: IntoIterator, - J: IntoIterator + pub fn multiscalar_mult(scalars: I, points: J) -> edwards::EdwardsPoint + where I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, { //assert_eq!(scalars.len(), points.len()); let nafs: Vec<_> = scalars.into_iter() - .map(|c| c.non_adjacent_form()).collect(); + .map(|c| c.borrow().non_adjacent_form()).collect(); let odd_multiples: Vec<_> = points.into_iter() - .map(|P| OddMultiples::create((*P).into()) ).collect(); + .map(|P| OddMultiples::create((*P.borrow()).into()) ).collect(); let mut Q = ExtendedPoint::identity(); diff --git a/src/edwards.rs b/src/edwards.rs index 8d242ab..2fd7c1e 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -96,6 +96,7 @@ use core::ops::{Add, Sub, Neg}; use core::ops::{AddAssign, SubAssign}; use core::ops::{Mul, MulAssign}; use core::ops::Index; +use core::borrow::Borrow; use subtle::slices_equal; use subtle::ConditionallyAssignable; @@ -532,12 +533,15 @@ impl<'a, 'b> Mul<&'b EdwardsPoint> for &'a Scalar { /// This function has the same behaviour as /// `vartime::multiscalar_mult` but is constant-time. /// -/// # Input -/// -/// A iterable of `Scalar`s and a iterable of `EdwardsPoints`. It is an -/// error to call this function with two iterators of different lengths. +/// 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; @@ -553,15 +557,25 @@ impl<'a, 'b> Mul<&'b EdwardsPoint> for &'a Scalar { /// let R = P + Q; /// /// // A1 = a*P + b*Q + c*R -/// let A1 = edwards::multiscalar_mult(&[a,b,c], &[P,Q,R]); -/// ``` +/// let abc = [a,b,c]; +/// let A1 = edwards::multiscalar_mult(&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_mult(minus_abc, &[P,Q,R]); +/// // Note: minus_abc.into_iter(): Iterator +/// +/// assert_eq!(A1.compress(), (-A2).compress()); +/// ``` // XXX later when we do more fancy multiscalar mults, we can delegate // based on the iter's size hint -- hdevalence #[cfg(any(feature = "alloc", feature = "std"))] -pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> EdwardsPoint - where I: IntoIterator, - J: IntoIterator +pub fn multiscalar_mult(scalars: I, points: J) -> EdwardsPoint + where I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, { // If we built with AVX2, use the AVX2 backend. #[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] { @@ -576,7 +590,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> EdwardsPoint use clear_on_drop::ClearOnDrop; let lookup_tables_vec: Vec<_> = points.into_iter() - .map(|P| LookupTable::::from(P) ) + .map(|P| LookupTable::::from(P.borrow()) ) .collect(); let lookup_tables = ClearOnDrop::new(lookup_tables_vec); @@ -587,7 +601,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> EdwardsPoint // // with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`. let scalar_digits_vec: Vec<_> = scalars.into_iter() - .map(|c| c.to_radix_16()) + .map(|c| c.borrow().to_radix_16()) .collect(); // This above puts the scalar digits into a heap-allocated Vec. @@ -886,20 +900,57 @@ pub mod vartime { } } - /// Given an iterable of public scalars and an iterable of public - /// points, compute + /// Given an iterator of public scalars and an iterator of public points, compute /// $$ /// Q = c\_1 P\_1 + \cdots + c\_n P\_n. /// $$ /// - /// # Input + /// This function has the same behaviour as + /// `edwards::multiscalar_mult` but operates on non-secret data. /// - /// A iterable of `Scalar`s and a iterable of `EdwardsPoints`. It is an - /// error to call this function with two iterators of different lengths. + /// 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_mult(&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_mult(minus_abc, &[P,Q,R]); + /// // Note: minus_abc.into_iter(): Iterator + /// + /// assert_eq!(A1.compress(), (-A2).compress()); + /// ``` + // XXX later when we do more fancy multiscalar mults, we can delegate + // based on the iter's size hint -- hdevalence #[cfg(any(feature = "alloc", feature = "std"))] - pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> EdwardsPoint - where I: IntoIterator, - J: IntoIterator + pub fn multiscalar_mult(scalars: I, points: J) -> EdwardsPoint + where I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, { // If we built with AVX2, use the AVX2 backend. #[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] { @@ -912,9 +963,9 @@ pub mod vartime { //assert_eq!(scalars.len(), points.len()); let nafs: Vec<_> = scalars.into_iter() - .map(|c| c.non_adjacent_form()).collect(); + .map(|c| c.borrow().non_adjacent_form()).collect(); let odd_multiples: Vec<_> = points.into_iter() - .map(|P| OddMultiples::create(P)).collect(); + .map(|P| OddMultiples::create(P.borrow())).collect(); let mut r = ProjectivePoint::identity(); diff --git a/src/ristretto.rs b/src/ristretto.rs index 6c30121..c809dbf 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -463,6 +463,10 @@ mod notes { } use core::fmt::Debug; +use core::ops::{Add, Sub, Neg}; +use core::ops::{AddAssign, SubAssign}; +use core::ops::{Mul, MulAssign}; +use core::borrow::Borrow; #[cfg(feature = "std")] use rand::Rng; @@ -473,10 +477,6 @@ use generic_array::typenum::U32; use constants; use field::FieldElement; -use core::ops::{Add, Sub, Neg}; -use core::ops::{AddAssign, SubAssign}; -use core::ops::{Mul, MulAssign}; - use subtle; use subtle::ConditionallyAssignable; use subtle::ConditionallyNegatable; @@ -1059,16 +1059,49 @@ define_mul_variants!(LHS = Scalar, RHS = RistrettoPoint, Output = RistrettoPoint /// This function has the same behaviour as /// `vartime::multiscalar_mult` but is constant-time. /// -/// # Input +/// It is an error to call this function with two iterators of different lengths. /// -/// An iterable of `Scalar`s and a iterable of `RistrettoPoints`. 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_mult(&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_mult(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_mult<'a, 'b, I, J>(scalars: I, points: J) -> RistrettoPoint - where I: IntoIterator, - J: IntoIterator, +pub fn multiscalar_mult(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.0); + let extended_points = points.into_iter().map(|P| P.borrow().0); RistrettoPoint(edwards::multiscalar_mult(scalars, extended_points)) } @@ -1169,22 +1202,57 @@ pub mod vartime { //! Variable-time operations on ristretto points, useful for non-secret data. use super::*; - /// Given an iterable of public scalars and an iterable of public - /// points, compute + /// Given an iterator of public scalars and an iterator of public points, compute /// $$ /// Q = c\_1 P\_1 + \cdots + c\_n P\_n. /// $$ /// - /// # Input + /// This function has the same behaviour as + /// `vartime::multiscalar_mult` but is constant-time. /// - /// A iterable of `Scalar`s and a iterable of `RistrettoPoints`. It is an - /// error to call this function with two iterators of different lengths. + /// 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_mult(&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_mult(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_mult<'a, 'b, I, J>(scalars: I, points: J) -> RistrettoPoint - where I: IntoIterator, - J: IntoIterator + pub fn multiscalar_mult(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.0); + let extended_points = points.into_iter().map(|P| P.borrow().0); RistrettoPoint(edwards::vartime::multiscalar_mult(scalars, extended_points)) } } From 70f06338f95056b6d14fcd2ef0f1f82dc21b54bd Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 21 Feb 2018 11:01:59 -0800 Subject: [PATCH 03/23] Remove docs.rs docs.rs won't be the authoritative source for docs any more. Also, it won't build our docs properly, because the nightly Rust it uses is too old to support stuff like markdown-included-in-docs :( --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f7ad37d..66c61ba 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# curve25519-dalek [![](https://img.shields.io/crates/v/curve25519-dalek.svg)](https://crates.io/crates/curve25519-dalek) [![](https://docs.rs/curve25519-dalek/badge.svg)](https://docs.rs/curve25519-dalek) [![](https://travis-ci.org/dalek-cryptography/curve25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/curve25519-dalek) +# curve25519-dalek [![](https://img.shields.io/crates/v/curve25519-dalek.svg)](https://crates.io/crates/curve25519-dalek) [![](https://travis-ci.org/dalek-cryptography/curve25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/curve25519-dalek) Date: Wed, 21 Feb 2018 11:03:18 -0800 Subject: [PATCH 04/23] Load resources from our own domain --- README.md | 2 +- rustdoc-include-katex-header.html | 6 +++--- src/lib.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 66c61ba..62afe30 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ + src="https://doc.dalek.rs/assets/dalek-logo-clear.png"/> **A pure-Rust implementation of group operations on Ristretto and Curve25519.** diff --git a/rustdoc-include-katex-header.html b/rustdoc-include-katex-header.html index 455a58e..bc4e3d8 100644 --- a/rustdoc-include-katex-header.html +++ b/rustdoc-include-katex-header.html @@ -1,6 +1,6 @@ - - - + + + diff --git a/src/lib.rs b/src/lib.rs index e06c1c6..5e9c854 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,7 +24,7 @@ #![cfg_attr(feature = "nightly", deny(missing_docs))] #![cfg_attr(feature = "nightly", doc(include = "../README.md"))] -#![doc(html_logo_url = "https://github.com/dalek-cryptography/curve25519-dalek/blob/develop/dalek-logo-clear.png?raw=true")] +#![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")] //------------------------------------------------------------------------ // External dependencies: From 3969d80c89e5a0fa387570c0c92aef61215b565c Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Wed, 21 Feb 2018 11:11:17 -0800 Subject: [PATCH 05/23] Add links to dalek.rs --- README.md | 2 ++ src/ristretto.rs | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 62afe30..f0d940d 100644 --- a/README.md +++ b/README.md @@ -118,3 +118,5 @@ contributions. [ed25519-dalek]: https://github.com/dalek-cryptography/ed25519-dalek [x25519-dalek]: https://github.com/dalek-cryptography/x25519-dalek [contributing]: https://github.com/dalek-cryptography/curve25519-dalek/blob/master/CONTRIBUTING.md +[docs-external]: https://doc.dalek.rs/curve25519_dalek/ +[docs-internal]: https://doc-internal.dalek.rs/curve25519_dalek/ diff --git a/src/ristretto.rs b/src/ristretto.rs index 6c30121..d92bbdd 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -129,7 +129,7 @@ //! gives the _Ristretto_ encoding. //! //! Notes on the details of the encoding can be found in the -//! `ristretto::notes` submodule of the internal `curve25519-dalek` +//! [`ristretto::notes`][ristretto_notes] submodule of the internal `curve25519-dalek` //! documentation. //! //! [cryptonote]: @@ -138,6 +138,8 @@ //! https://moderncrypto.org/mail-archive/curves/2017/000858.html //! [ristretto_coffee]: //! https://en.wikipedia.org/wiki/Ristretto +//! [ristretto_notes]: +//! https://doc-internal.dalek.rs/curve25519_dalek/ristretto/notes/index.html mod notes { From a271ff907de2c0219e43ddafa70a537833c6366f Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 22 Feb 2018 15:37:16 -0800 Subject: [PATCH 06/23] add link to curve models docs --- src/edwards.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/edwards.rs b/src/edwards.rs index 4a4914e..9f28eb6 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -68,7 +68,8 @@ //! The Edwards arithmetic is implemented using the “extended twisted //! coordinates” of Hisil, Wong, Carter, and Dawson, and the //! corresponding complete formulas. For more details, -//! see the `curve_models` submodule of the internal documentation. +//! see the [`curve_models` submodule][curve_models] +//! of the internal documentation. //! //! ## Validity Checking //! @@ -80,6 +81,8 @@ //! unrepresentable: `EdwardsPoint` objects can only be created via //! successful decompression of a compressed point, or else by //! operations on other (valid) `EdwardsPoint`s. +//! +//! [curve_models]: https://doc-internal.dalek.rs/curve25519_dalek/curve_models/index.html // We allow non snake_case names because coordinates in projective space are // traditionally denoted by the capitalisation of their respective From c2be44749ff8e4cc98dbc4411ff1a70689fa414d Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Fri, 9 Mar 2018 23:59:20 +0000 Subject: [PATCH 07/23] Add a custom docs badge. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f0d940d..1ac09aa 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# curve25519-dalek [![](https://img.shields.io/crates/v/curve25519-dalek.svg)](https://crates.io/crates/curve25519-dalek) [![](https://travis-ci.org/dalek-cryptography/curve25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/curve25519-dalek) +# curve25519-dalek [![](https://img.shields.io/crates/v/curve25519-dalek.svg)](https://crates.io/crates/curve25519-dalek) [![](https://img.shields.io/badge/dynamic/json.svg?label=docs&uri=https%3A%2F%2Fcrates.io%2Fapi%2Fv1%2Fcrates%2Fcurve25519-dalek%2Fversions&query=%24.versions%5B0%5D.num&colorB=4F74A6)](https://doc.dalek.rs) [![](https://travis-ci.org/dalek-cryptography/curve25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/curve25519-dalek) Date: Mon, 19 Mar 2018 11:15:52 -0700 Subject: [PATCH 08/23] Fix build for AVX2 backend. A missing import of the Borrow trait caused the build to break with the "yolocrypto" feature enabled; this was't caught by CI because the CI machine that Travis used didn't have AVX2, so the code was never built. This commit adds the missing import and changes `std` to `core` so that the AVX2 backend builds with no_std, but this isn't tested and is, actually, "yolocrypto". --- src/backend/avx2/edwards.rs | 5 +++-- src/backend/avx2/field.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/backend/avx2/edwards.rs b/src/backend/avx2/edwards.rs index b5f3245..fa83c72 100644 --- a/src/backend/avx2/edwards.rs +++ b/src/backend/avx2/edwards.rs @@ -13,8 +13,9 @@ // just going to own it #![allow(bad_style)] -use std::convert::From; -use std::ops::{Index, Add, Sub, Mul, Neg}; +use core::convert::From; +use core::ops::{Index, Add, Sub, Mul, Neg}; +use core::borrow::Borrow; use stdsimd::simd::{u32x8, i32x8}; diff --git a/src/backend/avx2/field.rs b/src/backend/avx2/field.rs index 3f69aad..8d3af8b 100644 --- a/src/backend/avx2/field.rs +++ b/src/backend/avx2/field.rs @@ -25,7 +25,7 @@ pub const D_LANES64: u8 = 0b11_00_00_00; pub const ALL_LANES: u8 = A_LANES | B_LANES | C_LANES | D_LANES; -use std::ops::Mul; +use core::ops::Mul; use stdsimd::simd::{u32x8, i32x8, u64x4}; From c20e09f6cce9949379ad20acf70e7ff862f6229c Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 19 Mar 2018 14:28:21 -0700 Subject: [PATCH 09/23] Rename `multiscalar_mult`->`multiscalar_mul` to match `Mul` traits --- src/backend/avx2/edwards.rs | 16 ++++++++-------- src/edwards.rs | 38 ++++++++++++++++++------------------- src/ristretto.rs | 24 +++++++++++------------ 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/backend/avx2/edwards.rs b/src/backend/avx2/edwards.rs index fa83c72..423465f 100644 --- a/src/backend/avx2/edwards.rs +++ b/src/backend/avx2/edwards.rs @@ -456,7 +456,7 @@ impl EdwardsBasepointTable { /// Internal multiscalar code. #[cfg(any(feature = "alloc", feature = "std"))] -pub fn multiscalar_mult(scalars: I, points: J) -> edwards::EdwardsPoint +pub fn multiscalar_mul(scalars: I, points: J) -> edwards::EdwardsPoint where I: IntoIterator, I::Item: Borrow, J: IntoIterator, @@ -596,7 +596,7 @@ pub mod vartime { /// Internal multiscalar function #[cfg(any(feature = "alloc", feature = "std"))] - pub fn multiscalar_mult(scalars: I, points: J) -> edwards::EdwardsPoint + pub fn multiscalar_mul(scalars: I, points: J) -> edwards::EdwardsPoint where I: IntoIterator, I::Item: Borrow, J: IntoIterator, @@ -881,7 +881,7 @@ mod test { } #[test] - fn multiscalar_mult_vs_adding_scalar_mults() { + fn multiscalar_mul_vs_adding_scalar_mults() { let B: ExtendedPoint = constants::ED25519_BASEPOINT_POINT.into(); let s1 = Scalar::from_bits([233, 1, 233, 147, 113, 78, 244, 120, 40, 45, 103, 51, 224, 199, 189, 218, 96, 140, 211, 112, 39, 194, 73, 216, 173, 33, 102, 93, 76, 200, 84, 12]); let s2 = Scalar::from_bits([165, 30, 79, 89, 58, 24, 195, 245, 248, 146, 203, 236, 119, 43, 64, 119, 196, 111, 188, 251, 248, 53, 234, 59, 215, 28, 218, 13, 59, 120, 14, 4]); @@ -891,7 +891,7 @@ mod test { let R = &(&P1 * &s1) + &(&P2 * &s2); - let R_multiscalar = multiscalar_mult(&[s1, s2], &[P1.into(), P2.into()]); + let R_multiscalar = multiscalar_mul(&[s1, s2], &[P1.into(), P2.into()]); assert_eq!(edwards::EdwardsPoint::from(R).compress(), R_multiscalar.compress()); @@ -901,7 +901,7 @@ mod test { use super::*; #[test] - fn multiscalar_mult_vs_adding_scalar_mults() { + fn multiscalar_mul_vs_adding_scalar_mults() { let B: ExtendedPoint = constants::ED25519_BASEPOINT_POINT.into(); let s1 = Scalar::from_bits([233, 1, 233, 147, 113, 78, 244, 120, 40, 45, 103, 51, 224, 199, 189, 218, 96, 140, 211, 112, 39, 194, 73, 216, 173, 33, 102, 93, 76, 200, 84, 12]); let s2 = Scalar::from_bits([165, 30, 79, 89, 58, 24, 195, 245, 248, 146, 203, 236, 119, 43, 64, 119, 196, 111, 188, 251, 248, 53, 234, 59, 215, 28, 218, 13, 59, 120, 14, 4]); @@ -911,7 +911,7 @@ mod test { let R = &(&P1 * &s1) + &(&P2 * &s2); - let R_multiscalar = vartime::multiscalar_mult(&[s1, s2], &[P1.into(), P2.into()]); + let R_multiscalar = vartime::multiscalar_mul(&[s1, s2], &[P1.into(), P2.into()]); assert_eq!(edwards::EdwardsPoint::from(R).compress(), R_multiscalar.compress()); @@ -1004,7 +1004,7 @@ mod bench { let B = &constants::ED25519_BASEPOINT_TABLE; let points: Vec<_> = scalars.iter().map(|s| B * s).collect(); - b.iter(|| multiscalar_mult(&scalars, &points)); + b.iter(|| multiscalar_mul(&scalars, &points)); } mod vartime { @@ -1031,7 +1031,7 @@ mod bench { let B = &constants::ED25519_BASEPOINT_TABLE; let points: Vec<_> = scalars.iter().map(|s| B * s).collect(); - b.iter(|| vartime::multiscalar_mult(&scalars, &points)); + b.iter(|| vartime::multiscalar_mul(&scalars, &points)); } } } diff --git a/src/edwards.rs b/src/edwards.rs index 952399d..2235528 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -57,10 +57,10 @@ //! `EdwardsBasepointTable`, which performs constant-time fixed-base //! scalar multiplication; //! -//! * the `edwards::multiscalar_mult` function, which performs +//! * the `edwards::multiscalar_mul` function, which performs //! constant-time variable-base multiscalar multiplication; //! -//! * the `edwards::vartime::multiscalar_mult` function, which +//! * the `edwards::vartime::multiscalar_mul` function, which //! performs variable-time variable-base multiscalar multiplication. //! //! ## Implementation @@ -534,7 +534,7 @@ impl<'a, 'b> Mul<&'b EdwardsPoint> for &'a Scalar { /// $$ /// /// This function has the same behaviour as -/// `vartime::multiscalar_mult` but is constant-time. +/// `vartime::multiscalar_mul` but is constant-time. /// /// It is an error to call this function with two iterators of different lengths. /// @@ -561,12 +561,12 @@ impl<'a, 'b> Mul<&'b EdwardsPoint> for &'a Scalar { /// /// // A1 = a*P + b*Q + c*R /// let abc = [a,b,c]; -/// let A1 = edwards::multiscalar_mult(&abc, &[P,Q,R]); +/// 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_mult(minus_abc, &[P,Q,R]); +/// let A2 = edwards::multiscalar_mul(minus_abc, &[P,Q,R]); /// // Note: minus_abc.into_iter(): Iterator /// /// assert_eq!(A1.compress(), (-A2).compress()); @@ -574,7 +574,7 @@ impl<'a, 'b> Mul<&'b EdwardsPoint> for &'a Scalar { // XXX later when we do more fancy multiscalar mults, we can delegate // based on the iter's size hint -- hdevalence #[cfg(any(feature = "alloc", feature = "std"))] -pub fn multiscalar_mult(scalars: I, points: J) -> EdwardsPoint +pub fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint where I: IntoIterator, I::Item: Borrow, J: IntoIterator, @@ -584,7 +584,7 @@ pub fn multiscalar_mult(scalars: I, points: J) -> EdwardsPoint #[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] { use backend::avx2::edwards as edwards_avx2; - edwards_avx2::multiscalar_mult(scalars, points) + edwards_avx2::multiscalar_mul(scalars, points) } // Otherwise, proceed as normal: #[cfg(not(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2"))))] { @@ -909,7 +909,7 @@ pub mod vartime { /// $$ /// /// This function has the same behaviour as - /// `edwards::multiscalar_mult` but operates on non-secret data. + /// `edwards::multiscalar_mul` but operates on non-secret data. /// /// It is an error to call this function with two iterators of different lengths. /// @@ -936,12 +936,12 @@ pub mod vartime { /// /// // A1 = a*P + b*Q + c*R /// let abc = [a,b,c]; - /// let A1 = edwards::vartime::multiscalar_mult(&abc, &[P,Q,R]); + /// 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_mult(minus_abc, &[P,Q,R]); + /// let A2 = edwards::vartime::multiscalar_mul(minus_abc, &[P,Q,R]); /// // Note: minus_abc.into_iter(): Iterator /// /// assert_eq!(A1.compress(), (-A2).compress()); @@ -949,7 +949,7 @@ pub mod vartime { // XXX later when we do more fancy multiscalar mults, we can delegate // based on the iter's size hint -- hdevalence #[cfg(any(feature = "alloc", feature = "std"))] - pub fn multiscalar_mult(scalars: I, points: J) -> EdwardsPoint + pub fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint where I: IntoIterator, I::Item: Borrow, J: IntoIterator, @@ -959,7 +959,7 @@ pub mod vartime { #[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] { use backend::avx2::edwards as edwards_avx2; - edwards_avx2::vartime::multiscalar_mult(scalars, points) + edwards_avx2::vartime::multiscalar_mul(scalars, points) } // Otherwise, proceed as normal: #[cfg(not(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2"))))] { @@ -1363,9 +1363,9 @@ mod test { } #[test] - fn multiscalar_mult_vs_ed25519py() { + fn multiscalar_mul_vs_ed25519py() { let A = A_TIMES_BASEPOINT.decompress().unwrap(); - let result = vartime::multiscalar_mult( + let result = vartime::multiscalar_mul( &[A_SCALAR, B_SCALAR], &[A, constants::ED25519_BASEPOINT_POINT] ); @@ -1373,13 +1373,13 @@ mod test { } #[test] - fn multiscalar_mult_vartime_vs_consttime() { + fn multiscalar_mul_vartime_vs_consttime() { let A = A_TIMES_BASEPOINT.decompress().unwrap(); - let result_vartime = vartime::multiscalar_mult( + let result_vartime = vartime::multiscalar_mul( &[A_SCALAR, B_SCALAR], &[A, constants::ED25519_BASEPOINT_POINT] ); - let result_consttime = multiscalar_mult( + let result_consttime = multiscalar_mul( &[A_SCALAR, B_SCALAR], &[A, constants::ED25519_BASEPOINT_POINT] ); @@ -1526,7 +1526,7 @@ mod bench { let B = &constants::ED25519_BASEPOINT_TABLE; let points: Vec<_> = scalars.iter().map(|s| B * &s).collect(); - b.iter(|| multiscalar_mult(&scalars, &points)); + b.iter(|| multiscalar_mul(&scalars, &points)); } mod vartime { @@ -1556,7 +1556,7 @@ mod bench { // // Since this is a variable-time function, this means the // benchmark is only useful as a ballpark measurement. - b.iter(|| vartime::multiscalar_mult(&scalars, &points)); + b.iter(|| vartime::multiscalar_mul(&scalars, &points)); } } } diff --git a/src/ristretto.rs b/src/ristretto.rs index fa9ef68..ccc7007 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -78,10 +78,10 @@ //! `RistrettoBasepointTable`, which performs constant-time fixed-base //! scalar multiplication; //! -//! * the `ristretto::multiscalar_mult` function, which performs +//! * the `ristretto::multiscalar_mul` function, which performs //! constant-time variable-base multiscalar multiplication; //! -//! * the `ristretto::vartime::multiscalar_mult` function, which +//! * the `ristretto::vartime::multiscalar_mul` function, which //! performs variable-time variable-base multiscalar multiplication. //! //! ## Random Points and Hashing to Ristretto @@ -1059,7 +1059,7 @@ define_mul_variants!(LHS = Scalar, RHS = RistrettoPoint, Output = RistrettoPoint /// $$ /// /// This function has the same behaviour as -/// `vartime::multiscalar_mult` but is constant-time. +/// `vartime::multiscalar_mul` but is constant-time. /// /// It is an error to call this function with two iterators of different lengths. /// @@ -1086,25 +1086,25 @@ define_mul_variants!(LHS = Scalar, RHS = RistrettoPoint, Output = RistrettoPoint /// /// // A1 = a*P + b*Q + c*R /// let abc = [a,b,c]; -/// let A1 = ristretto::multiscalar_mult(&abc, &[P,Q,R]); +/// 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_mult(minus_abc, &[P,Q,R]); +/// 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_mult(scalars: I, points: J) -> RistrettoPoint +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_mult(scalars, extended_points)) + RistrettoPoint(edwards::multiscalar_mul(scalars, extended_points)) } /// A precomputed table of multiples of a basepoint, used to accelerate @@ -1210,7 +1210,7 @@ pub mod vartime { /// $$ /// /// This function has the same behaviour as - /// `vartime::multiscalar_mult` but is constant-time. + /// `vartime::multiscalar_mul` but is constant-time. /// /// It is an error to call this function with two iterators of different lengths. /// @@ -1237,25 +1237,25 @@ pub mod vartime { /// /// // A1 = a*P + b*Q + c*R /// let abc = [a,b,c]; - /// let A1 = ristretto::vartime::multiscalar_mult(&abc, &[P,Q,R]); + /// 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_mult(minus_abc, &[P,Q,R]); + /// 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_mult(scalars: I, points: J) -> RistrettoPoint + 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_mult(scalars, extended_points)) + RistrettoPoint(edwards::vartime::multiscalar_mul(scalars, extended_points)) } } From 7f39656b4a70ddba49af87cc68930cf3dbc82a6d Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 19 Mar 2018 15:37:38 -0700 Subject: [PATCH 10/23] Apply the Elligator map twice to ensure a uniform distribution As noted in the Decaf paper, mapping twice and adding the results ensures a uniform distribution over the group. This changes our random point and hash-to-point functions to do this, matching the Sage script. --- src/ristretto.rs | 64 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/src/ristretto.rs b/src/ristretto.rs index fa9ef68..b986458 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -474,7 +474,7 @@ use core::borrow::Borrow; use rand::Rng; use digest::Digest; -use generic_array::typenum::U32; +use generic_array::typenum::U64; use constants; use field::FieldElement; @@ -815,7 +815,7 @@ impl RistrettoPoint { /// /// This method is not public because it's just used for hashing /// to a point -- proper elligator support is deferred for now. - pub(crate) fn elligator_ristretto_flavour(r_0: &FieldElement) -> RistrettoPoint { + pub(crate) fn elligator_ristretto_flavor(r_0: &FieldElement) -> RistrettoPoint { let (i, d) = (&constants::SQRT_M1, &constants::EDWARDS_D); let one = FieldElement::one(); @@ -870,27 +870,40 @@ impl RistrettoPoint { /// /// # Implementation /// - /// Uses the Ristretto-flavoured Elligator 2 map, so that the discrete log of the - /// output point with respect to any other point should be unknown. + /// Uses the Ristretto-flavoured Elligator 2 map, so that the + /// 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 { let mut field_bytes = [0u8; 32]; + rng.fill_bytes(&mut field_bytes); - let r_0 = FieldElement::from_bytes(&field_bytes); - RistrettoPoint::elligator_ristretto_flavour(&r_0) + let r_1 = FieldElement::from_bytes(&field_bytes); + let R_1 = RistrettoPoint::elligator_ristretto_flavor(&r_1); + + rng.fill_bytes(&mut field_bytes); + let r_2 = FieldElement::from_bytes(&field_bytes); + let R_2 = RistrettoPoint::elligator_ristretto_flavor(&r_2); + + // Applying Elligator twice and adding the results ensures a + // uniform distribution. + &R_1 + &R_2 } /// Hash a slice of bytes into a `RistrettoPoint`. /// - /// Takes a type parameter `D`, which is any `Digest` producing 32 - /// bytes (256 bits) of output. + /// Takes a type parameter `D`, which is any `Digest` producing 64 + /// bytes of output. /// /// Convenience wrapper around `from_hash`. /// /// # Implementation /// - /// Uses the Ristretto-flavoured Elligator 2 map, so that the discrete log of the - /// output point with respect to any other point should be unknown. + /// Uses the Ristretto-flavoured Elligator 2 map, so that the + /// 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. /// /// # Example /// @@ -898,18 +911,18 @@ impl RistrettoPoint { /// # extern crate curve25519_dalek; /// # use curve25519_dalek::ristretto::RistrettoPoint; /// extern crate sha2; - /// use sha2::Sha256; + /// use sha2::Sha512; /// /// # // Need fn main() here in comment so the doctest compiles /// # // See https://doc.rust-lang.org/book/documentation.html#documentation-as-tests /// # fn main() { /// let msg = "To really appreciate architecture, you may even need to commit a murder"; - /// let P = RistrettoPoint::hash_from_bytes::(msg.as_bytes()); + /// let P = RistrettoPoint::hash_from_bytes::(msg.as_bytes()); /// # } /// ``` /// pub fn hash_from_bytes(input: &[u8]) -> RistrettoPoint - where D: Digest + Default + where D: Digest + Default { let mut hash = D::default(); hash.input(input); @@ -922,13 +935,24 @@ impl RistrettoPoint { /// to stream data into the `Digest` than to pass a single byte /// slice. pub fn from_hash(hash: D) -> RistrettoPoint - where D: Digest + Default + where D: Digest + Default { - // XXX this seems clumsy - let mut output = [0u8; 32]; - output.copy_from_slice(hash.result().as_slice()); - let r_0 = FieldElement::from_bytes(&output); - RistrettoPoint::elligator_ristretto_flavour(&r_0) + // dealing with generic arrays is clumsy, until const generics land + let output = hash.result(); + + let mut r_1_bytes = [0u8; 32]; + r_1_bytes.copy_from_slice(&output.as_slice()[0..32]); + let r_1 = FieldElement::from_bytes(&r_1_bytes); + let R_1 = RistrettoPoint::elligator_ristretto_flavor(&r_1); + + let mut r_2_bytes = [0u8; 32]; + r_2_bytes.copy_from_slice(&output.as_slice()[0..32]); + let r_2 = FieldElement::from_bytes(&r_2_bytes); + let R_2 = RistrettoPoint::elligator_ristretto_flavor(&r_2); + + // Applying Elligator twice and adding the results ensures a + // uniform distribution. + &R_1 + &R_2 } } @@ -1427,7 +1451,7 @@ mod test { ]; for i in 0..16 { let r_0 = FieldElement::from_bytes(&bytes[i]); - let Q = RistrettoPoint::elligator_ristretto_flavour(&r_0); + let Q = RistrettoPoint::elligator_ristretto_flavor(&r_0); assert_eq!(Q.compress(), encoded_images[i]); } } From 792ac0775e4b1af55348765a03878e6d0c7db2d6 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 6 Feb 2018 16:47:30 -0800 Subject: [PATCH 11/23] Change to the updated `subtle` API. --- Cargo.toml | 4 +- src/backend/avx2/edwards.rs | 5 ++- src/backend/avx2/field.rs | 5 ++- src/backend/u32/field.rs | 6 +-- src/backend/u64/field.rs | 6 +-- src/constants.rs | 2 +- src/curve_models/mod.rs | 8 ++-- src/curve_models/window.rs | 9 ++-- src/edwards.rs | 37 ++++++++-------- src/field.rs | 88 ++++++++++++++++--------------------- src/montgomery.rs | 20 ++++----- src/ristretto.rs | 44 ++++++++++--------- src/scalar.rs | 56 ++++------------------- src/traits.rs | 4 +- 14 files changed, 125 insertions(+), 169 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e5570e9..ebcdf71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ serde_cbor = "0.6" digest = "0.7" generic-array = "0.9" clear_on_drop = "=0.2.3" -subtle = { version = "0.5", features = ["generic-impls"], default-features = false } +subtle = { version = "0.6", features = ["generic-impls"], default-features = false } stdsimd = { version = "0.0.4", optional = true } serde = { version = "1.0", optional = true } rand = { version = "0.4", optional = true } @@ -48,7 +48,7 @@ rand = { version = "0.4", optional = true } digest = "0.7" generic-array = "0.9" clear_on_drop = "=0.2.3" -subtle = { version = "0.5", features = ["generic-impls"], default-features = false } +subtle = { version = "0.6", features = ["generic-impls"], default-features = false } stdsimd = { version = "0.0.4", optional = true } serde = { version = "1.0", optional = true } # Allowing rand to be optional during builds causes a build failure when compiling for no_std targets diff --git a/src/backend/avx2/edwards.rs b/src/backend/avx2/edwards.rs index fa83c72..3ef8535 100644 --- a/src/backend/avx2/edwards.rs +++ b/src/backend/avx2/edwards.rs @@ -20,6 +20,7 @@ use core::borrow::Borrow; use stdsimd::simd::{u32x8, i32x8}; use subtle::ConditionallyAssignable; +use subtle::Choice; use edwards; use scalar::Scalar; @@ -52,7 +53,7 @@ impl From for edwards::EdwardsPoint { } impl ConditionallyAssignable for ExtendedPoint { - fn conditional_assign(&mut self, other: &ExtendedPoint, choice: u8) { + fn conditional_assign(&mut self, other: &ExtendedPoint, choice: Choice) { self.0.conditional_assign(&other.0, choice); } } @@ -115,7 +116,7 @@ impl Identity for CachedPoint { } impl ConditionallyAssignable for CachedPoint { - fn conditional_assign(&mut self, other: &CachedPoint, choice: u8) { + fn conditional_assign(&mut self, other: &CachedPoint, choice: Choice) { self.0.conditional_assign(&other.0, choice); } } diff --git a/src/backend/avx2/field.rs b/src/backend/avx2/field.rs index 8d3af8b..d8b7883 100644 --- a/src/backend/avx2/field.rs +++ b/src/backend/avx2/field.rs @@ -38,10 +38,11 @@ use backend::avx2::constants::{P_TIMES_2_LO, P_TIMES_2_HI, P_TIMES_16_LO, P_TIME pub(crate) struct FieldElement32x4(pub(crate) [u32x8; 5]); use subtle::ConditionallyAssignable; +use subtle::Choice; impl ConditionallyAssignable for FieldElement32x4 { - fn conditional_assign(&mut self, other: &FieldElement32x4, choice: u8) { - let mask = (-(choice as i32)) as u32; + fn conditional_assign(&mut self, other: &FieldElement32x4, choice: Choice) { + let mask = (-(choice.unwrap_u8() as i32)) as u32; let mask_vec = u32x8::splat(mask); for i in 0..5 { self.0[i] = self.0[i] ^ (mask_vec & (self.0[i] ^ other.0[i])); diff --git a/src/backend/u32/field.rs b/src/backend/u32/field.rs index fef4615..8c65a46 100644 --- a/src/backend/u32/field.rs +++ b/src/backend/u32/field.rs @@ -22,6 +22,7 @@ use core::ops::{Mul, MulAssign}; use core::ops::Neg; use subtle::ConditionallyAssignable; +use subtle::Choice; /// A `FieldElement32` represents an element of the field /// \\( \mathbb Z / (2\^{255} - 19)\\). @@ -219,10 +220,9 @@ impl<'a> Neg for &'a FieldElement32 { } impl ConditionallyAssignable for FieldElement32 { - fn conditional_assign(&mut self, f: &FieldElement32, choice: u8) { - let mask = (-(choice as i32)) as u32; + fn conditional_assign(&mut self, other: &FieldElement32, choice: Choice) { for i in 0..10 { - self.0[i] ^= mask & (self.0[i] ^ f.0[i]); + self.0[i].conditional_assign(&other.0[i], choice); } } } diff --git a/src/backend/u64/field.rs b/src/backend/u64/field.rs index 0859991..d685ff3 100644 --- a/src/backend/u64/field.rs +++ b/src/backend/u64/field.rs @@ -18,6 +18,7 @@ use core::ops::{Mul, MulAssign}; use core::ops::Neg; use subtle::ConditionallyAssignable; +use subtle::Choice; /// A `FieldElement64` represents an element of the field /// \\( \mathbb Z / (2\^{255} - 19)\\). @@ -209,10 +210,9 @@ impl<'a> Neg for &'a FieldElement64 { } impl ConditionallyAssignable for FieldElement64 { - fn conditional_assign(&mut self, f: &FieldElement64, choice: u8) { - let mask = (-(choice as i64)) as u64; + fn conditional_assign(&mut self, other: &FieldElement64, choice: Choice) { for i in 0..5 { - self.0[i] ^= mask & (self.0[i] ^ f.0[i]); + self.0[i].conditional_assign(&other.0[i], choice); } } } diff --git a/src/constants.rs b/src/constants.rs index f353d8d..b1207ea 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -165,7 +165,7 @@ mod test { fn test_sqrt_constants_sign() { let minus_one = FieldElement::minus_one(); let (was_nonzero_square, invsqrt_m1) = minus_one.invsqrt(); - assert_eq!(was_nonzero_square, 1u8); + assert_eq!(was_nonzero_square.unwrap_u8(), 1u8); let sign_test_sqrt = &invsqrt_m1 * &constants::SQRT_M1; // XXX it seems we have flipped the sign relative to // the invsqrt function? diff --git a/src/curve_models/mod.rs b/src/curve_models/mod.rs index f9b29d8..59426fa 100644 --- a/src/curve_models/mod.rs +++ b/src/curve_models/mod.rs @@ -126,11 +126,13 @@ use core::fmt::Debug; use core::ops::{Add, Sub, Neg}; +use subtle::ConditionallyAssignable; +use subtle::Choice; + use constants; use field::FieldElement; use edwards::EdwardsPoint; -use subtle::ConditionallyAssignable; use traits::ValidityCheck; pub mod window; @@ -275,7 +277,7 @@ impl ValidityCheck for ProjectivePoint { // ------------------------------------------------------------------------ impl ConditionallyAssignable for ProjectiveNielsPoint { - fn conditional_assign(&mut self, other: &ProjectiveNielsPoint, choice: u8) { + fn conditional_assign(&mut self, other: &ProjectiveNielsPoint, choice: Choice) { self.Y_plus_X.conditional_assign(&other.Y_plus_X, choice); self.Y_minus_X.conditional_assign(&other.Y_minus_X, choice); self.Z.conditional_assign(&other.Z, choice); @@ -284,7 +286,7 @@ impl ConditionallyAssignable for ProjectiveNielsPoint { } impl ConditionallyAssignable for AffineNielsPoint { - fn conditional_assign(&mut self, other: &AffineNielsPoint, choice: u8) { + fn conditional_assign(&mut self, other: &AffineNielsPoint, choice: Choice) { // PreComputedGroupElementCMove() self.y_plus_x.conditional_assign(&other.y_plus_x, choice); self.y_minus_x.conditional_assign(&other.y_minus_x, choice); diff --git a/src/curve_models/window.rs b/src/curve_models/window.rs index c247459..ada3706 100644 --- a/src/curve_models/window.rs +++ b/src/curve_models/window.rs @@ -14,9 +14,10 @@ use core::fmt::Debug; -use subtle; use subtle::ConditionallyNegatable; use subtle::ConditionallyAssignable; +use subtle::ConstantTimeEq; +use subtle::Choice; use traits::Identity; @@ -67,12 +68,12 @@ where T: Identity + ConditionallyAssignable + ConditionallyNegatable let mut t = T::identity(); for j in 1..9 { // Copy `points[j-1] == j*P` onto `t` in constant time if `|x| == j`. - t.conditional_assign(&self.0[j-1], - subtle::bytes_equal(xabs as u8, j as u8)); + let c = (xabs as u8).ct_eq(&(j as u8)); + t.conditional_assign(&self.0[j-1], c); } // Now t == |x| * P. - let neg_mask = (xmask & 1) as u8; + let neg_mask = Choice::from((xmask & 1) as u8); t.conditional_negate(neg_mask); // Now t == x * P. diff --git a/src/edwards.rs b/src/edwards.rs index 952399d..68e02e4 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -17,9 +17,9 @@ //! //! ## Equality Testing //! -//! The `EdwardsPoint` struct implements the `subtle::Equal` trait for -//! constant-time equality checking, and the Rust `Eq` trait for -//! variable-time equality checking. +//! The `EdwardsPoint` struct implements the `subtle::ConstantTimeEq` +//! trait for constant-time equality checking, and the Rust `Eq` trait +//! for variable-time equality checking. //! //! ## Cofactor-related functions //! @@ -101,11 +101,10 @@ use core::ops::{Mul, MulAssign}; use core::ops::Index; use core::borrow::Borrow; -use subtle::slices_equal; use subtle::ConditionallyAssignable; use subtle::ConditionallyNegatable; -// XXX subtle::Equal -use subtle::Equal; +use subtle::Choice; +use subtle::ConstantTimeEq; use constants; @@ -164,11 +163,12 @@ impl CompressedEdwardsY { let v = &(&YY * &constants::EDWARDS_D) + &Z; // v = dy²+1 let (is_nonzero_square, mut X) = FieldElement::sqrt_ratio(&u, &v); - if is_nonzero_square != 1u8 { return None; } + if is_nonzero_square.unwrap_u8() != 1u8 { return None; } // Flip the sign of X if it's not correct - let compressed_sign_bit = self.as_bytes()[31] >> 7; + let compressed_sign_bit = Choice::from(self.as_bytes()[31] >> 7); let current_sign_bit = X.is_negative(); + X.conditional_negate(current_sign_bit ^ compressed_sign_bit); Some(EdwardsPoint{ X: X, Y: Y, Z: Z, T: &X * &Y }) @@ -282,7 +282,7 @@ impl ValidityCheck for EdwardsPoint { // ------------------------------------------------------------------------ impl ConditionallyAssignable for EdwardsPoint { - fn conditional_assign(&mut self, other: &EdwardsPoint, choice: u8) { + fn conditional_assign(&mut self, other: &EdwardsPoint, choice: Choice) { self.X.conditional_assign(&other.X, choice); self.Y.conditional_assign(&other.Y, choice); self.Z.conditional_assign(&other.Z, choice); @@ -294,16 +294,15 @@ impl ConditionallyAssignable for EdwardsPoint { // Equality // ------------------------------------------------------------------------ -impl Equal for EdwardsPoint { - fn ct_eq(&self, other: &EdwardsPoint) -> u8 { - slices_equal(self.compress().as_bytes(), - other.compress().as_bytes()) +impl ConstantTimeEq for EdwardsPoint { + fn ct_eq(&self, other: &EdwardsPoint) -> Choice { + self.compress().as_bytes().ct_eq(other.compress().as_bytes()) } } impl PartialEq for EdwardsPoint { fn eq(&self, other: &EdwardsPoint) -> bool { - self.ct_eq(other) == 1u8 + self.ct_eq(other).unwrap_u8() == 1u8 } } @@ -374,8 +373,8 @@ impl EdwardsPoint { let y = &self.Y * &recip; let mut s: [u8; 32]; - s = y.to_bytes(); - s[31] ^= (x.is_negative() << 7) as u8; + s = y.to_bytes(); + s[31] ^= x.is_negative().unwrap_u8() << 7; CompressedEdwardsY(s) } } @@ -1203,7 +1202,7 @@ mod test { Z: FieldElement::from_bytes(&two_bytes), T: FieldElement::zero() }; - assert!(id1.ct_eq(&id2) == 1u8); + assert_eq!(id1.ct_eq(&id2).unwrap_u8(), 1u8); } /// Sanity check for conversion to precomputed points @@ -1290,9 +1289,9 @@ mod test { let mut p1 = AffineNielsPoint::identity(); let bp = constants::ED25519_BASEPOINT_POINT.to_affine_niels(); - p1.conditional_assign(&bp, 0); + p1.conditional_assign(&bp, Choice::from(0)); assert_eq!(p1, id); - p1.conditional_assign(&bp, 1); + p1.conditional_assign(&bp, Choice::from(1)); assert_eq!(p1, bp); } diff --git a/src/field.rs b/src/field.rs index 756423e..6b599f4 100644 --- a/src/field.rs +++ b/src/field.rs @@ -24,11 +24,10 @@ use core::cmp::{Eq, PartialEq}; -use subtle::slices_equal; -use subtle::byte_is_nonzero; use subtle::ConditionallyAssignable; use subtle::ConditionallyNegatable; -use subtle::Equal; +use subtle::Choice; +use subtle::ConstantTimeEq; use constants; use backend; @@ -54,37 +53,19 @@ pub use backend::u32::field::*; pub type FieldElement = backend::u32::field::FieldElement32; impl Eq for FieldElement {} + impl PartialEq for FieldElement { - /// Test equality between two `FieldElement`s. Since the - /// internal representation is not canonical, the field elements - /// are normalized to wire format before comparison. - /// - /// # Warning - /// - /// This comparison is *not* constant time. It could easily be - /// made to be, but the main use of an `Eq` implementation is for - /// branching, so it seems pointless to do so. fn eq(&self, other: &FieldElement) -> bool { - let self_bytes = self.to_bytes(); - let other_bytes = other.to_bytes(); - let mut are_equal: bool = true; - for i in 0..32 { - are_equal &= self_bytes[i] == other_bytes[i]; - } - are_equal + self.ct_eq(other).unwrap_u8() == 1u8 } } -impl Equal for FieldElement { +impl ConstantTimeEq for FieldElement { /// Test equality between two `FieldElement`s. Since the /// internal representation is not canonical, the field elements /// are normalized to wire format before comparison. - /// - /// # Returns - /// - /// `1u8` if the two `FieldElement`s are equal, and `0u8` otherwise. - fn ct_eq(&self, other: &FieldElement) -> u8 { - slices_equal(&self.to_bytes(), &other.to_bytes()) + fn ct_eq(&self, other: &FieldElement) -> Choice { + self.to_bytes().ct_eq(&other.to_bytes()) } } @@ -95,33 +76,22 @@ impl FieldElement { /// /// # Return /// - /// If negative, return `1u8`. Otherwise, return `0u8`. - pub fn is_negative(&self) -> u8 { + /// If negative, return `Choice(1)`. Otherwise, return `Choice(0)`. + pub fn is_negative(&self) -> Choice { let bytes = self.to_bytes(); - (bytes[0] & 1) as u8 + (bytes[0] & 1).into() } /// Determine if this `FieldElement` is zero. /// /// # Return /// - /// If zero, return `1u8`. Otherwise, return `0u8`. - pub fn is_zero(&self) -> u8 { - 1u8 & (!self.is_nonzero()) - } - - /// Determine if this `FieldElement` is non-zero. - /// - /// # Return - /// - /// If non-zero, return `1u8`. Otherwise, return `0u8`. - pub fn is_nonzero(&self) -> u8 { //FeIsNonZero + /// If zero, return `Choice(1)`. Otherwise, return `Choice(0)`. + pub fn is_zero(&self) -> Choice { + let zero = [0u8; 32]; let bytes = self.to_bytes(); - let mut x = 0u8; - for b in &bytes { - x |= *b; - } - byte_is_nonzero(x) + + bytes.ct_eq(&zero) } /// Compute (self^(2^250-1), self^11), used as a helper function @@ -275,7 +245,25 @@ impl FieldElement { /// - `(0u8, zero)` if `v` is zero; /// - `(0u8, garbage)` if `u/v` is nonsquare. /// - pub fn sqrt_ratio(u: &FieldElement, v: &FieldElement) -> (u8, FieldElement) { + /// # Example + /// + /// ```ignore + /// let one = FieldElement::one(); + /// let two = &one + &one; + /// let four = &two * &two; + /// + /// // two is nonsquare mod p + /// let (two_is_square, two_sqrt) = FieldElement::sqrt_ratio(&two, &one); + /// assert_eq!(two_is_square.unwrap_u8(), 0u8); + /// + /// // four is square mod p + /// let (four_is_square, four_sqrt) = FieldElement::sqrt_ratio(&four, &one); + /// + /// assert_eq!(four_is_square.unwrap_u8(), 1u8); + /// assert_eq!(four_sqrt.is_negative().unwrap_u8 + /// ``` + /// + pub fn sqrt_ratio(u: &FieldElement, v: &FieldElement) -> (Choice, FieldElement) { // Using the same trick as in ed25519 decoding, we merge the // inversion, the square root, and the square test as follows. // @@ -333,7 +321,7 @@ impl FieldElement { /// - `(0u8, zero)` if `self` is zero; /// - `(0u8, garbage)` if `self` is nonsquare. /// - pub fn invsqrt(&self) -> (u8, FieldElement) { + pub fn invsqrt(&self) -> (Choice, FieldElement) { FieldElement::sqrt_ratio(&FieldElement::one(), self) } @@ -487,11 +475,11 @@ mod test { let one = FieldElement::one(); let minus_one = FieldElement::minus_one(); let mut x = one; - x.conditional_negate(1u8); + x.conditional_negate(Choice::from(1)); assert_eq!(x, minus_one); - x.conditional_negate(0u8); + x.conditional_negate(Choice::from(0)); assert_eq!(x, minus_one); - x.conditional_negate(1u8); + x.conditional_negate(Choice::from(1)); assert_eq!(x, one); } diff --git a/src/montgomery.rs b/src/montgomery.rs index 0c953bc..8ba7670 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -60,8 +60,8 @@ use traits::{Identity, ValidityCheck}; use subtle::ConditionallyAssignable; use subtle::ConditionallySwappable; -use subtle::Equal; -use subtle::Mask; +use subtle::ConstantTimeEq; +use subtle::Choice; /// Holds the \\(u\\)-coordinate of a point on the Montgomery form of /// Curve25519 or its twist. @@ -69,8 +69,8 @@ use subtle::Mask; pub struct MontgomeryPoint(pub [u8; 32]); /// Equality of `MontgomeryPoint`s is defined mod p. -impl Equal for MontgomeryPoint { - fn ct_eq(&self, other: &MontgomeryPoint) -> u8 { +impl ConstantTimeEq for MontgomeryPoint { + fn ct_eq(&self, other: &MontgomeryPoint) -> Choice { let self_fe = FieldElement::from_bytes(&self.0); let other_fe = FieldElement::from_bytes(&other.0); @@ -80,7 +80,7 @@ impl Equal for MontgomeryPoint { impl PartialEq for MontgomeryPoint { fn eq(&self, other: &MontgomeryPoint) -> bool { - self.ct_eq(other) == 1u8 + self.ct_eq(other).unwrap_u8() == 1u8 } } @@ -157,7 +157,7 @@ impl Identity for ProjectivePoint { } impl ConditionallyAssignable for ProjectivePoint { - fn conditional_assign(&mut self, that: &ProjectivePoint, choice: Mask) { + fn conditional_assign(&mut self, that: &ProjectivePoint, choice: Choice) { self.U.conditional_assign(&that.U, choice); self.W.conditional_assign(&that.W, choice); } @@ -244,14 +244,14 @@ impl Mul for MontgomeryPoint { let bits: [i8; 256] = scalar.bits(); for i in (0..255).rev() { - let mask: u8 = (bits[i+1] ^ bits[i]) as u8; + let choice: u8 = (bits[i+1] ^ bits[i]) as u8; - debug_assert!(mask == 0 || mask == 1); + debug_assert!(choice == 0 || choice == 1); - x0.conditional_swap(&mut x1, mask); + x0.conditional_swap(&mut x1, choice.into()); differential_add_and_double(&mut x0, &mut x1, &affine_u); } - x0.conditional_swap(&mut x1, bits[0] as u8); + x0.conditional_swap(&mut x1, Choice::from(bits[0] as u8)); x0.to_affine() } diff --git a/src/ristretto.rs b/src/ristretto.rs index fa9ef68..3b75855 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -57,9 +57,10 @@ //! checking in the Ristretto group can be done in projective //! coordinates without requiring an inversion, so it is much faster. //! -//! The `RistrettoPoint` struct implements the `subtle::Equal` trait for -//! constant-time equality checking, and the Rust `Eq` trait for -//! variable-time equality checking. +//! The `RistrettoPoint` struct implements the +//! `subtle::ConstantTimeEq` trait for constant-time equality +//! checking, and the Rust `Eq` trait for variable-time equality +//! checking. //! //! ## Scalars //! @@ -479,10 +480,10 @@ use generic_array::typenum::U32; use constants; use field::FieldElement; -use subtle; use subtle::ConditionallyAssignable; use subtle::ConditionallyNegatable; -use subtle::Equal; +use subtle::ConstantTimeEq; +use subtle::Choice; use edwards; use edwards::EdwardsPoint; @@ -538,10 +539,10 @@ impl CompressedRistretto { let s = FieldElement::from_bytes(self.as_bytes()); let s_bytes_check = s.to_bytes(); let s_encoding_is_canonical = - subtle::slices_equal(&s_bytes_check[..], self.as_bytes()); + &s_bytes_check[..].ct_eq(self.as_bytes()); let s_is_negative = s.is_negative(); - if s_encoding_is_canonical == 0u8 || s_is_negative == 1u8 { + if s_encoding_is_canonical.unwrap_u8() == 0u8 || s_is_negative.unwrap_u8() == 1u8 { return None; } @@ -565,7 +566,7 @@ impl CompressedRistretto { let t = &x * &y; - if ok == 0u8 || t.is_negative() == 1u8 || y.is_zero() == 1u8 { + if ok.unwrap_u8() == 0u8 || t.is_negative().unwrap_u8() == 1u8 || y.is_zero().unwrap_u8() == 1u8 { return None; } else { return Some(RistrettoPoint(EdwardsPoint{X: x, Y: y, Z: one, T: t})); @@ -839,7 +840,7 @@ impl RistrettoPoint { maybe_s.negate(); // s = -sqrt(rN/D) if rN/D is square (should happen exactly when N/D is nonsquare) - debug_assert_eq!(N_over_D_is_square ^ rN_over_D_is_square, 1u8); + debug_assert_eq!((N_over_D_is_square ^ rN_over_D_is_square).unwrap_u8(), 1u8); s.conditional_assign(&maybe_s, rN_over_D_is_square); c.conditional_assign(&r, rN_over_D_is_square); @@ -944,17 +945,18 @@ impl Identity for RistrettoPoint { impl PartialEq for RistrettoPoint { fn eq(&self, other: &RistrettoPoint) -> bool { - self.ct_eq(other) == 1u8 + self.ct_eq(other).unwrap_u8() == 1u8 } } -impl Equal for RistrettoPoint { +impl ConstantTimeEq for RistrettoPoint { /// Test equality between two `RistrettoPoint`s. /// /// # Returns /// - /// `1u8` if the two `RistrettoPoint`s are equal, and `0u8` otherwise. - fn ct_eq(&self, other: &RistrettoPoint) -> u8 { + /// * `Choice(1)` if the two `RistrettoPoint`s are equal; + /// * `Choice(0)` otherwise. + fn ct_eq(&self, other: &RistrettoPoint) -> Choice { let X1Y2 = &self.0.X * &other.0.Y; let Y1X2 = &self.0.Y * &other.0.X; let X1X2 = &self.0.X * &other.0.X; @@ -1145,7 +1147,7 @@ impl RistrettoBasepointTable { // ------------------------------------------------------------------------ impl ConditionallyAssignable for RistrettoPoint { - /// Conditionally assign `other` to `self`, if `choice == 1u8`. + /// Conditionally assign `other` to `self`, if `choice == Choice(1)`. /// /// # Example /// @@ -1153,24 +1155,26 @@ impl ConditionallyAssignable for RistrettoPoint { /// # extern crate subtle; /// # extern crate curve25519_dalek; /// # - /// # use subtle::ConditionallyAssignable; + /// use subtle::ConditionallyAssignable; + /// use subtle::Choice; /// # /// # use curve25519_dalek::traits::Identity; /// # use curve25519_dalek::ristretto::RistrettoPoint; /// # use curve25519_dalek::constants; /// # fn main() { + /// /// let A = RistrettoPoint::identity(); /// let B = constants::RISTRETTO_BASEPOINT_POINT; /// /// let mut P = A; /// - /// P.conditional_assign(&B, 0u8); - /// assert!(P == A); - /// P.conditional_assign(&B, 1u8); - /// assert!(P == B); + /// P.conditional_assign(&B, Choice::from(0)); + /// assert_eq!(P, A); + /// P.conditional_assign(&B, Choice::from(1)); + /// assert_eq!(P, B); /// # } /// ``` - fn conditional_assign(&mut self, other: &RistrettoPoint, choice: u8) { + fn conditional_assign(&mut self, other: &RistrettoPoint, choice: Choice) { self.0.X.conditional_assign(&other.0.X, choice); self.0.Y.conditional_assign(&other.0.Y, choice); self.0.Z.conditional_assign(&other.0.Z, choice); diff --git a/src/scalar.rs b/src/scalar.rs index ae17169..4e7d7a7 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -26,9 +26,9 @@ use rand::Rng; use digest::Digest; use generic_array::typenum::U64; -use subtle::slices_equal; +use subtle::Choice; use subtle::ConditionallyAssignable; -use subtle::Equal; +use subtle::ConstantTimeEq; use backend; use constants; @@ -145,29 +145,14 @@ impl Debug for Scalar { impl Eq for Scalar {} impl PartialEq for Scalar { - /// Test equality between two `Scalar`s. - /// - /// # Warning - /// - /// This function is *not* guaranteed to be constant time and should only be - /// used for debugging purposes. - /// - /// # Returns - /// - /// True if they are equal, and false otherwise. fn eq(&self, other: &Self) -> bool { - slices_equal(&self.bytes, &other.bytes) == 1u8 + self.ct_eq(other).unwrap_u8() == 1u8 } } -impl Equal for Scalar { - /// Test equality between two `Scalar`s in constant time. - /// - /// # Returns - /// - /// `1u8` if they are equal, and `0u8` otherwise. - fn ct_eq(&self, other: &Self) -> u8 { - slices_equal(&self.bytes, &other.bytes) +impl ConstantTimeEq for Scalar { + fn ct_eq(&self, other: &Self) -> Choice { + self.bytes.ct_eq(&other.bytes) } } @@ -246,34 +231,9 @@ impl<'a> Neg for Scalar { } impl ConditionallyAssignable for Scalar { - /// Conditionally assign another Scalar to this one. - /// - /// ``` - /// # extern crate curve25519_dalek; - /// # extern crate subtle; - /// # use curve25519_dalek::scalar::Scalar; - /// # use subtle::ConditionallyAssignable; - /// # fn main() { - /// let a = Scalar::from_bits([0u8;32]); - /// let b = Scalar::from_bits([1u8;32]); - /// let mut t = a; - /// t.conditional_assign(&b, 0u8); - /// assert!(t[0] == a[0]); - /// t.conditional_assign(&b, 1u8); - /// assert!(t[0] == b[0]); - /// # } - /// ``` - /// - /// # Preconditions - /// - /// * `choice` in {0,1} - // XXX above test checks first byte because Scalar does not impl Eq - fn conditional_assign(&mut self, other: &Scalar, choice: u8) { - // if choice = 0u8, mask = (-0i8) as u8 = 00000000 - // if choice = 1u8, mask = (-1i8) as u8 = 11111111 - let mask = -(choice as i8) as u8; + fn conditional_assign(&mut self, other: &Scalar, choice: Choice) { for i in 0..32 { - self.bytes[i] ^= mask & (self.bytes[i] ^ other.bytes[i]); + self.bytes[i].conditional_assign(&other.bytes[i], choice); } } } diff --git a/src/traits.rs b/src/traits.rs index b0beeb9..d774833 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -32,9 +32,9 @@ 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::Equal + Identity { +impl IsIdentity for T where T: subtle::ConstantTimeEq + Identity { fn is_identity(&self) -> bool { - self.ct_eq(&T::identity()) == 1u8 + self.ct_eq(&T::identity()).unwrap_u8() == 1u8 } } From 0e7d872ad00c869e9b4c433b8b3ce903ae412c2d Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 19 Mar 2018 14:03:27 -0700 Subject: [PATCH 12/23] Add batch inversion for Scalars --- src/scalar.rs | 104 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 97 insertions(+), 7 deletions(-) diff --git a/src/scalar.rs b/src/scalar.rs index 4e7d7a7..3f8295d 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -393,6 +393,92 @@ impl Scalar { self.unpack().invert().pack() } + /// Given a slice of nonzero (possibly secret) `Scalar`s, + /// compute their inverses in a batch. + /// + /// # Return + /// + /// Each element of `inputs` is replaced by its inverse. + /// + /// The product of all inverses is returned. + /// + /// # Warning + /// + /// All input `Scalars` **MUST** be nonzero. If you cannot + /// *prove* that this is the case, you **SHOULD NOT USE THIS + /// FUNCTION**. + /// + /// This function is most efficient when the batch size (slice + /// length) is a power of 2. + /// + /// # Example + /// + /// ``` + /// # extern crate curve25519_dalek; + /// # use curve25519_dalek::scalar::Scalar; + /// # fn main() { + /// + /// let mut scalars = [ + /// Scalar::from_u64(3), + /// Scalar::from_u64(5), + /// Scalar::from_u64(7), + /// Scalar::from_u64(11), + /// ]; + /// + /// let allinv = Scalar::batch_invert(&mut scalars); + /// + /// assert_eq!(allinv, Scalar::from_u64(3*5*7*11).invert()); + /// assert_eq!(scalars[0], Scalar::from_u64(3).invert()); + /// assert_eq!(scalars[1], Scalar::from_u64(5).invert()); + /// assert_eq!(scalars[2], Scalar::from_u64(7).invert()); + /// assert_eq!(scalars[3], Scalar::from_u64(11).invert()); + /// # } + /// ``` + #[cfg(any(feature = "alloc", feature = "std"))] + pub fn batch_invert(inputs: &mut [Scalar]) -> Scalar { + // This code is essentially identical to the FieldElement + // implementation, and is documented there. Unfortunately, + // it's not easy to write it generically, since here we want + // to use `UnpackedScalar`s internally, and `Scalar`s + // externally, but there's no corresponding distinction for + // field elements. + + use clear_on_drop::ClearOnDrop; + use clear_on_drop::clear::ZeroSafe; + // Mark UnpackedScalars as zeroable. + unsafe impl ZeroSafe for UnpackedScalar {} + + let n = inputs.len().next_power_of_two(); + let one: UnpackedScalar = Scalar::one().unpack().to_montgomery(); + + // Wrap the tree storage in a ClearOnDrop to wipe it when we + // pass out of scope. + let mut tree_vec = vec![one; 2*n]; + let mut tree = ClearOnDrop::new(tree_vec); + + for i in 0..inputs.len() { + tree[n+i] = inputs[i].unpack().to_montgomery(); + } + + for i in (1..n).rev() { + tree[i] = UnpackedScalar::montgomery_mul(&tree[2*i], &tree[2*i+1]); + } + + let allinv = tree[1].montgomery_invert(); + + for i in 0..inputs.len() { + let mut inv = allinv; + let mut node = n + i; + while node > 1 { + inv = UnpackedScalar::montgomery_mul(&inv, &tree[node ^1]); + node = node >> 1; + } + inputs[i] = inv.from_montgomery().pack(); + } + + allinv.from_montgomery().pack() + } + /// Get the bits of the scalar. pub(crate) fn bits(&self) -> [i8; 256] { let mut bits = [0i8; 256]; @@ -495,6 +581,7 @@ impl Scalar { } /// Reduce this `Scalar` modulo \\(\ell\\). + #[allow(non_snake_case)] pub fn reduce(&self) -> Scalar { let x = self.unpack(); let xR = UnpackedScalar::mul_internal(&x, &constants::R); @@ -531,13 +618,11 @@ impl UnpackedScalar { Scalar{ bytes: self.to_bytes() } } - /// Compute the multiplicative inverse of this scalar. - pub fn invert(&self) -> UnpackedScalar { - // This is a direct transliteration of the addition chain from + /// Inverts an UnpackedScalar in Montgomery form. + pub fn montgomery_invert(&self) -> UnpackedScalar { + // Uses the addition chain from // https://briansmith.org/ecc-inversion-addition-chains-01#curve25519_scalar_inversion - // as it was published on 2017-09-03. - - let _1 = self.to_montgomery(); + let _1 = self; let _10 = _1.montgomery_square(); let _100 = _10.montgomery_square(); let _11 = UnpackedScalar::montgomery_mul(&_10, &_1); @@ -586,7 +671,12 @@ impl UnpackedScalar { square_multiply(&mut y, 3, &_101); square_multiply(&mut y, 1 + 2, &_11); - y.from_montgomery() + y + } + + /// Inverts an UnpackedScalar not in Montgomery form. + pub fn invert(&self) -> UnpackedScalar { + self.to_montgomery().montgomery_invert().from_montgomery() } } From b48d568f472de3295e0d886ff57be2cb7a249b05 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 22 Mar 2018 11:31:14 -0700 Subject: [PATCH 13/23] Add debug_assert that Scalar::batch_invert inputs are nonzero --- src/scalar.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/scalar.rs b/src/scalar.rs index 3f8295d..931bdc7 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -464,6 +464,9 @@ impl Scalar { tree[i] = UnpackedScalar::montgomery_mul(&tree[2*i], &tree[2*i+1]); } + // tree[1] is zero iff any of the inputs are zero. + debug_assert!(tree[1].from_montgomery().pack() != Scalar::zero()); + let allinv = tree[1].montgomery_invert(); for i in 0..inputs.len() { @@ -977,6 +980,15 @@ mod test { let parsed: Scalar = serde_cbor::from_slice(&output).unwrap(); assert_eq!(parsed, X); } + + #[test] + #[should_panic] + fn batch_invert_with_a_zero_input_panics() { + let mut xs = vec![Scalar::one(); 16]; + xs[3] = Scalar::zero(); + // This should panic in debug mode. + Scalar::batch_invert(&mut xs); + } } #[cfg(all(test, feature = "bench"))] From 8de3d7576adf9b6feb79afc0435a0865bdcdcf12 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 22 Mar 2018 11:35:42 -0700 Subject: [PATCH 14/23] Rename mult_by_pow_2 to mul_by_pow_2 for consistency --- src/backend/avx2/edwards.rs | 10 +++++----- src/constants.rs | 6 +++--- src/edwards.rs | 18 +++++++++--------- src/ristretto.rs | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/backend/avx2/edwards.rs b/src/backend/avx2/edwards.rs index 423465f..fc083e7 100644 --- a/src/backend/avx2/edwards.rs +++ b/src/backend/avx2/edwards.rs @@ -237,7 +237,7 @@ impl ExtendedPoint { } } - pub fn mult_by_pow_2(&self, k: u32) -> ExtendedPoint { + pub fn mul_by_pow_2(&self, k: u32) -> ExtendedPoint { let mut tmp: ExtendedPoint = *self; for _ in 0..k { tmp = tmp.double(); @@ -396,7 +396,7 @@ impl<'a, 'b> Mul<&'b Scalar> for &'a ExtendedPoint { let mut Q = ExtendedPoint::identity(); for i in (0..64).rev() { // Q = 16*Q - Q = Q.mult_by_pow_2(4); + Q = Q.mul_by_pow_2(4); // Q += P*s_i Q = &Q + &lookup_table.select(scalar_digits[i]); } @@ -420,7 +420,7 @@ impl<'a, 'b> Mul<&'b Scalar> for &'a EdwardsBasepointTable { P = &P + &tables[i/2].select(a[i]); } - P = P.mult_by_pow_2(4); + P = P.mul_by_pow_2(4); for i in (0..64).filter(|x| x % 2 == 0) { P = &P + &tables[i/2].select(a[i]); @@ -448,7 +448,7 @@ impl EdwardsBasepointTable { for i in 0..32 { // P = (16^2)^i * B table.0[i] = LookupTable::from(P); - P = P.mult_by_pow_2(8); + P = P.mul_by_pow_2(8); } table } @@ -507,7 +507,7 @@ pub fn multiscalar_mul(scalars: I, points: J) -> edwards::EdwardsPoint let mut Q = ExtendedPoint::identity(); // XXX this algorithm makes no effort to be cache-aware; maybe it could be improved? for j in (0..64).rev() { - Q = Q.mult_by_pow_2(4); + 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 diff --git a/src/constants.rs b/src/constants.rs index f353d8d..cdb3d7a 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -105,7 +105,7 @@ mod test { #[test] fn test_eight_torsion() { for i in 0..8 { - let Q = constants::EIGHT_TORSION[i].mult_by_pow_2(3); + let Q = constants::EIGHT_TORSION[i].mul_by_pow_2(3); assert!(Q.is_valid()); assert!(Q.is_identity()); } @@ -114,7 +114,7 @@ mod test { #[test] fn test_four_torsion() { for i in (0..8).filter(|i| i % 2 == 0) { - let Q = constants::EIGHT_TORSION[i].mult_by_pow_2(2); + let Q = constants::EIGHT_TORSION[i].mul_by_pow_2(2); assert!(Q.is_valid()); assert!(Q.is_identity()); } @@ -123,7 +123,7 @@ mod test { #[test] fn test_two_torsion() { for i in (0..8).filter(|i| i % 4 == 0) { - let Q = constants::EIGHT_TORSION[i].mult_by_pow_2(1); + let Q = constants::EIGHT_TORSION[i].mul_by_pow_2(1); assert!(Q.is_valid()); assert!(Q.is_identity()); } diff --git a/src/edwards.rs b/src/edwards.rs index 2235528..f364eb8 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -505,7 +505,7 @@ impl<'a, 'b> Mul<&'b Scalar> for &'a EdwardsPoint { let mut Q = EdwardsPoint::identity(); for i in (0..64).rev() { // Q <-- 16*Q - Q = Q.mult_by_pow_2(4); + Q = Q.mul_by_pow_2(4); // Q <-- Q + P * s_i Q = (&Q + &lookup_table.select(scalar_digits[i])).to_extended() } @@ -634,7 +634,7 @@ pub fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint let mut Q = EdwardsPoint::identity(); // XXX this impl makes no effort to be cache-aware; maybe it could be improved? for j in (0..64).rev() { - Q = Q.mult_by_pow_2(4); + 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 @@ -694,7 +694,7 @@ impl EdwardsBasepointTable { P = (&P + &tables[i/2].select(a[i])).to_extended(); } - P = P.mult_by_pow_2(4); + P = P.mul_by_pow_2(4); for i in (0..64).filter(|x| x % 2 == 0) { P = (&P + &tables[i/2].select(a[i])).to_extended(); @@ -734,7 +734,7 @@ impl EdwardsBasepointTable { for i in 0..32 { // P = (16^2)^i * B table.0[i] = LookupTable::from(&P); - P = P.mult_by_pow_2(8); + P = P.mul_by_pow_2(8); } table } @@ -752,11 +752,11 @@ impl EdwardsBasepointTable { impl EdwardsPoint { /// Multiply by the cofactor: return \\([8]P\\). pub fn mult_by_cofactor(&self) -> EdwardsPoint { - self.mult_by_pow_2(3) + self.mul_by_pow_2(3) } /// Compute \\([2\^k] P \\) by successive doublings. Requires \\( k > 0 \\). - pub(crate) fn mult_by_pow_2(&self, k: u32) -> EdwardsPoint { + pub(crate) fn mul_by_pow_2(&self, k: u32) -> EdwardsPoint { debug_assert!( k > 0 ); let mut r: CompletedPoint; let mut s = self.to_projective(); @@ -1276,10 +1276,10 @@ mod test { constants::ED25519_BASEPOINT_COMPRESSED); } - /// Test computing 16*basepoint vs mult_by_pow_2(4) + /// Test computing 16*basepoint vs mul_by_pow_2(4) #[test] - fn basepoint16_vs_mult_by_pow_2_4() { - let bp16 = constants::ED25519_BASEPOINT_POINT.mult_by_pow_2(4); + fn basepoint16_vs_mul_by_pow_2_4() { + let bp16 = constants::ED25519_BASEPOINT_POINT.mul_by_pow_2(4); assert_eq!(bp16.compress(), BASE16_CMPRSSD); } diff --git a/src/ristretto.rs b/src/ristretto.rs index ccc7007..6584a0a 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -1327,7 +1327,7 @@ mod test { let bp_recaf = bp_compressed_ristretto.decompress().unwrap().0; // Check that bp_recaf differs from bp by a point of order 4 let diff = &constants::RISTRETTO_BASEPOINT_POINT.0 - &bp_recaf; - let diff4 = diff.mult_by_pow_2(2); + let diff4 = diff.mul_by_pow_2(2); assert_eq!(diff4.compress(), CompressedEdwardsY::identity()); } From 296cd16463d78a2932a089c8250c756f43e22354 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 22 Mar 2018 11:36:34 -0700 Subject: [PATCH 15/23] Rename mult_by_cofactor to mul_by_cofactor for consistency --- src/edwards.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/edwards.rs b/src/edwards.rs index f364eb8..ba8a3e0 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -37,7 +37,7 @@ //! To test if a point is in \\( \mathcal E[\ell] \\), use //! `EdwardsPoint::is_torsion_free()`. //! -//! To multiply by the cofactor, use `EdwardsPoint::mult_by_cofactor()`. +//! To multiply by the cofactor, use `EdwardsPoint::mul_by_cofactor()`. //! //! To avoid dealing with cofactors entirely, consider using Ristretto. //! @@ -751,7 +751,7 @@ impl EdwardsBasepointTable { impl EdwardsPoint { /// Multiply by the cofactor: return \\([8]P\\). - pub fn mult_by_cofactor(&self) -> EdwardsPoint { + pub fn mul_by_cofactor(&self) -> EdwardsPoint { self.mul_by_pow_2(3) } @@ -791,7 +791,7 @@ impl EdwardsPoint { /// assert_eq!(Q.is_small_order(), true); /// ``` pub fn is_small_order(&self) -> bool { - self.mult_by_cofactor().is_identity() + self.mul_by_cofactor().is_identity() } /// Determine if this point is “torsion-free”, i.e., is contained in @@ -1503,10 +1503,10 @@ mod bench { } #[bench] - fn mult_by_cofactor(b: &mut Bencher) { + fn mul_by_cofactor(b: &mut Bencher) { let p1 = constants::ED25519_BASEPOINT_POINT; - b.iter(|| p1.mult_by_cofactor()); + b.iter(|| p1.mul_by_cofactor()); } #[bench] From 70eee5208a3e259edeee49e3bd287e9c52c85640 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 22 Mar 2018 11:39:47 -0700 Subject: [PATCH 16/23] Rename double_scalar_mult_basepoint to double_scalar_mul_basepoint for consistency --- src/backend/avx2/edwards.rs | 4 ++-- src/edwards.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/backend/avx2/edwards.rs b/src/backend/avx2/edwards.rs index fc083e7..bf1426b 100644 --- a/src/backend/avx2/edwards.rs +++ b/src/backend/avx2/edwards.rs @@ -550,7 +550,7 @@ pub mod vartime { /// with x positive). /// /// This is the same as calling the iterator-based function, but slightly faster. - pub fn double_scalar_mult_basepoint(a: &Scalar, + pub fn double_scalar_mul_basepoint(a: &Scalar, A: &edwards::EdwardsPoint, b: &Scalar) -> edwards::EdwardsPoint { let a_naf = a.non_adjacent_form(); @@ -1019,7 +1019,7 @@ mod bench { let s2 = Scalar::random(&mut csprng); let P = &s1 * &constants::ED25519_BASEPOINT_TABLE; - b.iter(|| vartime::double_scalar_mult_basepoint(&s2, &P, &s1) ); + b.iter(|| vartime::double_scalar_mul_basepoint(&s2, &P, &s1) ); } #[bench] diff --git a/src/edwards.rs b/src/edwards.rs index ba8a3e0..e1f6d06 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -994,7 +994,7 @@ pub mod vartime { /// \\(aA+bB\\), where \\(B\\) is the Ed25519 basepoint (i.e., \\(B = (x,4/5)\\) /// with x positive). #[cfg(feature="precomputed_tables")] - pub fn double_scalar_mult_basepoint( + pub fn double_scalar_mul_basepoint( a: &Scalar, A: &EdwardsPoint, b: &Scalar, @@ -1003,7 +1003,7 @@ pub mod vartime { #[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] { use backend::avx2::edwards as edwards_avx2; - edwards_avx2::vartime::double_scalar_mult_basepoint(a, A, b) + edwards_avx2::vartime::double_scalar_mul_basepoint(a, A, b) } // Otherwise, proceed as normal: #[cfg(not(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2"))))] { @@ -1356,9 +1356,9 @@ mod test { /// Test double_scalar_mult_vartime vs ed25519.py #[test] #[cfg(feature="precomputed_tables")] - fn double_scalar_mult_basepoint_vs_ed25519py() { + fn double_scalar_mul_basepoint_vs_ed25519py() { let A = A_TIMES_BASEPOINT.decompress().unwrap(); - let result = vartime::double_scalar_mult_basepoint(&A_SCALAR, &A, &B_SCALAR); + let result = vartime::double_scalar_mul_basepoint(&A_SCALAR, &A, &B_SCALAR); assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT); } @@ -1535,9 +1535,9 @@ mod bench { use super::{Bencher, OsRng}; #[bench] - fn bench_double_scalar_mult_basepoint(b: &mut Bencher) { + fn bench_double_scalar_mul_basepoint(b: &mut Bencher) { let A = A_TIMES_BASEPOINT.decompress().unwrap(); - b.iter(|| vartime::double_scalar_mult_basepoint(&A_SCALAR, &A, &B_SCALAR)); + b.iter(|| vartime::double_scalar_mul_basepoint(&A_SCALAR, &A, &B_SCALAR)); } #[bench] From 2e73b2bc20757327c089065ea3168c8098dfc3d9 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 22 Mar 2018 11:40:13 -0700 Subject: [PATCH 17/23] Use scalar_mul instead of scalar_mult --- src/backend/avx2/edwards.rs | 16 ++++++++-------- src/edwards.rs | 14 +++++++------- src/scalar.rs | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/backend/avx2/edwards.rs b/src/backend/avx2/edwards.rs index bf1426b..33b30fc 100644 --- a/src/backend/avx2/edwards.rs +++ b/src/backend/avx2/edwards.rs @@ -855,7 +855,7 @@ mod test { } #[test] - fn scalar_mult_vs_edwards_scalar_mult() { + fn scalar_mul_vs_edwards_scalar_mul() { let B: ExtendedPoint = constants::ED25519_BASEPOINT_POINT.into(); // some random bytes let s = Scalar::from_bits([233, 1, 233, 147, 113, 78, 244, 120, 40, 45, 103, 51, 224, 199, 189, 218, 96, 140, 211, 112, 39, 194, 73, 216, 173, 33, 102, 93, 76, 200, 84, 12]); @@ -867,7 +867,7 @@ mod test { } #[test] - fn scalar_mult_vs_basepoint_table_scalar_mult() { + fn scalar_mul_vs_basepoint_table_scalar_mul() { let B: ExtendedPoint = constants::ED25519_BASEPOINT_POINT.into(); let B_table = EdwardsBasepointTable::create(&B); // some random bytes @@ -881,7 +881,7 @@ mod test { } #[test] - fn multiscalar_mul_vs_adding_scalar_mults() { + fn multiscalar_mul_vs_adding_scalar_muls() { let B: ExtendedPoint = constants::ED25519_BASEPOINT_POINT.into(); let s1 = Scalar::from_bits([233, 1, 233, 147, 113, 78, 244, 120, 40, 45, 103, 51, 224, 199, 189, 218, 96, 140, 211, 112, 39, 194, 73, 216, 173, 33, 102, 93, 76, 200, 84, 12]); let s2 = Scalar::from_bits([165, 30, 79, 89, 58, 24, 195, 245, 248, 146, 203, 236, 119, 43, 64, 119, 196, 111, 188, 251, 248, 53, 234, 59, 215, 28, 218, 13, 59, 120, 14, 4]); @@ -901,7 +901,7 @@ mod test { use super::*; #[test] - fn multiscalar_mul_vs_adding_scalar_mults() { + fn multiscalar_mul_vs_adding_scalar_muls() { let B: ExtendedPoint = constants::ED25519_BASEPOINT_POINT.into(); let s1 = Scalar::from_bits([233, 1, 233, 147, 113, 78, 244, 120, 40, 45, 103, 51, 224, 199, 189, 218, 96, 140, 211, 112, 39, 194, 73, 216, 173, 33, 102, 93, 76, 200, 84, 12]); let s2 = Scalar::from_bits([165, 30, 79, 89, 58, 24, 195, 245, 248, 146, 203, 236, 119, 43, 64, 119, 196, 111, 188, 251, 248, 53, 234, 59, 215, 28, 218, 13, 59, 120, 14, 4]); @@ -971,7 +971,7 @@ mod bench { } #[bench] - fn scalar_mult(b: &mut Bencher) { + fn scalar_mul(b: &mut Bencher) { let B = &constants::ED25519_BASEPOINT_TABLE; let P = ExtendedPoint::from(B * &Scalar::from_u64(83973422)); let s = Scalar::from_bits([233, 1, 233, 147, 113, 78, 244, 120, 40, 45, 103, 51, 224, 199, 189, 218, 96, 140, 211, 112, 39, 194, 73, 216, 173, 33, 102, 93, 76, 200, 84, 12]); @@ -996,7 +996,7 @@ mod bench { } #[bench] - fn ten_fold_scalar_mult(b: &mut Bencher) { + fn ten_fold_scalar_mul(b: &mut Bencher) { let mut csprng: OsRng = OsRng::new().unwrap(); // Create 10 random scalars let scalars: Vec<_> = (0..10).map(|_| Scalar::random(&mut csprng)).collect(); @@ -1012,7 +1012,7 @@ mod bench { use super::{constants, Bencher, OsRng}; #[bench] - fn double_scalar_mult(b: &mut Bencher) { + fn double_scalar_mul(b: &mut Bencher) { let mut csprng: OsRng = OsRng::new().unwrap(); // Create 2 random scalars let s1 = Scalar::random(&mut csprng); @@ -1023,7 +1023,7 @@ mod bench { } #[bench] - fn ten_fold_scalar_mult(b: &mut Bencher) { + fn ten_fold_scalar_mul(b: &mut Bencher) { let mut csprng: OsRng = OsRng::new().unwrap(); // Create 10 random scalars let scalars: Vec<_> = (0..10).map(|_| Scalar::random(&mut csprng)).collect(); diff --git a/src/edwards.rs b/src/edwards.rs index e1f6d06..4105d57 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -1245,9 +1245,9 @@ mod test { assert_eq!(aB_1.compress(), aB_2.compress()); } - /// Test scalar_mult versus a known scalar multiple from ed25519.py + /// Test scalar_mul versus a known scalar multiple from ed25519.py #[test] - fn scalar_mult_vs_ed25519py() { + fn scalar_mul_vs_ed25519py() { let aB = &constants::ED25519_BASEPOINT_POINT * &A_SCALAR; assert_eq!(aB.compress(), A_TIMES_BASEPOINT); } @@ -1331,7 +1331,7 @@ mod test { #[test] fn monte_carlo_overflow_underflow_debug_assert_test() { let mut P = constants::ED25519_BASEPOINT_POINT; - // N.B. each scalar_mult does 1407 field mults, 1024 field squarings, + // N.B. each scalar_mul does 1407 field mults, 1024 field squarings, // so this does ~ 1M of each operation. for _ in 0..1_000 { P *= &A_SCALAR; @@ -1353,7 +1353,7 @@ mod test { use super::super::*; use super::{A_SCALAR, B_SCALAR, A_TIMES_BASEPOINT, DOUBLE_SCALAR_MULT_RESULT}; - /// Test double_scalar_mult_vartime vs ed25519.py + /// Test double_scalar_mul_vartime vs ed25519.py #[test] #[cfg(feature="precomputed_tables")] fn double_scalar_mul_basepoint_vs_ed25519py() { @@ -1443,7 +1443,7 @@ mod bench { } #[bench] - fn scalar_mult(b: &mut Bencher) { + fn scalar_mul(b: &mut Bencher) { let B = &constants::ED25519_BASEPOINT_POINT; b.iter(|| B * &A_SCALAR); } @@ -1518,7 +1518,7 @@ mod bench { #[bench] #[cfg(feature="precomputed_tables")] - fn ten_fold_scalar_mult(b: &mut Bencher) { + fn ten_fold_scalar_mul(b: &mut Bencher) { let mut csprng: OsRng = OsRng::new().unwrap(); // Create 10 random scalars let scalars: Vec<_> = (0..10).map(|_| Scalar::random(&mut csprng)).collect(); @@ -1542,7 +1542,7 @@ mod bench { #[bench] #[cfg(feature="precomputed_tables")] - fn ten_fold_scalar_mult(b: &mut Bencher) { + fn ten_fold_scalar_mul(b: &mut Bencher) { let mut csprng: OsRng = OsRng::new().unwrap(); // Create 10 random scalars let scalars: Vec<_> = (0..10).map(|_| Scalar::random(&mut csprng)).collect(); diff --git a/src/scalar.rs b/src/scalar.rs index ae17169..b51c099 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -775,7 +775,7 @@ mod test { } #[test] - fn scalar_multiply_by_one() { + fn scalar_mul_by_one() { let test_scalar = &X * &Scalar::one(); for i in 0..32 { assert!(test_scalar[i] == X[i]); From 844da9712b342231427ab14bbd2b8dfbbdf71b68 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 22 Mar 2018 12:13:39 -0700 Subject: [PATCH 18/23] Fix AVX2 docs formatting, remove obsolete AVX512 note --- src/backend/avx2/mod.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/backend/avx2/mod.rs b/src/backend/avx2/mod.rs index 385be33..ebae72f 100644 --- a/src/backend/avx2/mod.rs +++ b/src/backend/avx2/mod.rs @@ -463,18 +463,12 @@ //! `ymm16..ymm31` registers from AVX512VL), and of approximately 1.0x //! for Ryzen (which implements AVX2 at half rate). //! -//! When used for variable-time double-base scalar multiplication \\( aA -//! + bB \\) for fixed \\(B\\) (as in, e.g., signature verification), +//! When used for variable-time double-base scalar multiplication +//! \\( aA + bB \\) for fixed \\(B\\) (as in, e.g., signature verification), //! this strategy provides a 1.4x speedup on Skylake-X over the same //! operation as implemented in `ed25519-donna`, the fastest //! production-quality Ed25519 implementation. //! -//! (Note: since testing this, the experimental `llvm50` Rust branch -//! used to compile the experimental `stdsimd` intrinsics have fallen -//! out of sync and it is no longer possible to compile for -//! `skylake-avx512`. This is why all of this branch is part of the -//! `yolocrypto` feature, pending upstream work.) -//! //! [sandy2x]: https://eprint.iacr.org/2015/943.pdf //! [avx2trac]: https://trac.torproject.org/projects/tor/ticket/8897#comment:28 //! [hwcd08]: https://www.iacr.org/archive/asiacrypt2008/53500329/53500329.pdf From e73b635fe0bd9cd9d82ae1cb0054942b116940ee Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 22 Mar 2018 11:54:06 -0700 Subject: [PATCH 19/23] Remove unused constants --- src/backend/u32/constants.rs | 11 ----------- src/backend/u64/constants.rs | 6 ------ src/constants.rs | 24 ------------------------ src/montgomery.rs | 6 ++---- src/scalar.rs | 18 ------------------ 5 files changed, 2 insertions(+), 63 deletions(-) diff --git a/src/backend/u32/constants.rs b/src/backend/u32/constants.rs index 94702d7..f68f662 100644 --- a/src/backend/u32/constants.rs +++ b/src/backend/u32/constants.rs @@ -46,22 +46,11 @@ pub(crate) const SQRT_M1: FieldElement32 = FieldElement32([ 33281959, 41962654, 31548777, 326685, 11406482, ]); -/// In Montgomery form y² = x³+Ax²+x, Curve25519 has A=486662. -pub(crate) const MONTGOMERY_A: FieldElement32 = FieldElement32([ - 486662, 0, 0, 0, 0, 0, 0, 0, 0, 0, -]); - /// `APLUS2_OVER_FOUR` is (A+2)/4. (This is used internally within the Montgomery ladder.) pub(crate) const APLUS2_OVER_FOUR: FieldElement32 = FieldElement32([ 121666, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]); -/// `SQRT_MINUS_APLUS2` is sqrt(-486664) -pub(crate) const SQRT_MINUS_APLUS2: FieldElement32 = FieldElement32([ - 54885894, 25242303, 55597453, 9067496, 51808079, - 33312638, 25456129, 14121551, 54921728, 3972023, -]); - /// `L` is the order of base point, i.e. 2^252 + /// 27742317777372353535851937790883648493 pub(crate) const L: Scalar32 = Scalar32([ 0x1cf5d3ed, 0x009318d2, 0x1de73596, 0x1df3bd45, diff --git a/src/backend/u64/constants.rs b/src/backend/u64/constants.rs index 254c81c..1cc23fa 100644 --- a/src/backend/u64/constants.rs +++ b/src/backend/u64/constants.rs @@ -33,15 +33,9 @@ pub(crate) const INVSQRT_A_MINUS_D: FieldElement64 = FieldElement64([ /// Precomputed value of one of the square roots of -1 (mod p) pub(crate) const SQRT_M1: FieldElement64 = FieldElement64([1718705420411056, 234908883556509, 2233514472574048, 2117202627021982, 765476049583133]); -/// In Montgomery form y² = x³+Ax²+x, Curve25519 has A=486662. -pub(crate) const MONTGOMERY_A: FieldElement64 = FieldElement64([486662, 0, 0, 0, 0]); - /// `APLUS2_OVER_FOUR` is (A+2)/4. (This is used internally within the Montgomery ladder.) pub(crate) const APLUS2_OVER_FOUR: FieldElement64 = FieldElement64([121666, 0, 0, 0, 0]); -/// `SQRT_MINUS_APLUS2` is sqrt(-486664) -pub(crate) const SQRT_MINUS_APLUS2: FieldElement64 = FieldElement64([1693982333959686, 608509411481997, 2235573344831311, 947681270984193, 266558006233600]); - /// `L` is the order of base point, i.e. 2^252 + 27742317777372353535851937790883648493 pub(crate) const L: Scalar64 = Scalar64([ 0x0002631a5cf5d3ed, 0x000dea2f79cd6581, 0x000000000014def9, 0x0000000000000000, 0x0000100000000000 ]); diff --git a/src/constants.rs b/src/constants.rs index 9da0800..c8cf1c1 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -129,30 +129,6 @@ mod test { } } - /// Test that the constant for sqrt(-486664) really is a square - /// root of -486664. - #[test] - #[cfg(feature="radix_51")] - fn sqrt_minus_aplus2() { - use backend::u64::field::FieldElement64; - let minus_aplus2 = -&FieldElement64([486664,0,0,0,0]); - let sqrt = constants::SQRT_MINUS_APLUS2; - let sq = &sqrt * &sqrt; - assert_eq!(sq, minus_aplus2); - } - - /// Test that the constant for sqrt(-486664) really is a square - /// root of -486664. - #[test] - #[cfg(not(feature="radix_51"))] - fn sqrt_minus_aplus2() { - use backend::u32::field::FieldElement32; - let minus_aplus2 = -&FieldElement32([486664,0,0,0,0,0,0,0,0,0]); - let sqrt = constants::SQRT_MINUS_APLUS2; - let sq = &sqrt * &sqrt; - assert_eq!(sq, minus_aplus2); - } - #[test] /// Test that SQRT_M1 is a square root of -1 fn test_sqrt_minus_one() { diff --git a/src/montgomery.rs b/src/montgomery.rs index 8ba7670..81337c5 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -50,13 +50,12 @@ use core::ops::{Mul, MulAssign}; -use constants; use constants::APLUS2_OVER_FOUR; use field::FieldElement; use edwards::{EdwardsPoint, CompressedEdwardsY}; use scalar::Scalar; -use traits::{Identity, ValidityCheck}; +use traits::Identity; use subtle::ConditionallyAssignable; use subtle::ConditionallySwappable; @@ -277,8 +276,7 @@ impl Mul for Scalar { #[cfg(test)] mod test { - use constants::X25519_BASEPOINT; - use traits::Identity; + use constants; use super::*; use rand::OsRng; diff --git a/src/scalar.rs b/src/scalar.rs index c2b177b..34e1776 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -715,24 +715,6 @@ mod test { 0xe8, 0xef, 0x7a, 0xc3, 0x1f, 0x35, 0xbb, 0x05, ], }; - /// z = 5033871415930814945849241457262266927579821285980625165479289807629491019013 - pub static Z: Scalar = Scalar{ - bytes: [ - 0x05, 0x9d, 0x3e, 0x0b, 0x09, 0x26, 0x50, 0x3d, - 0xa3, 0x84, 0xa1, 0x3c, 0x92, 0x7a, 0xc2, 0x06, - 0x41, 0x98, 0xcf, 0x34, 0x3a, 0x24, 0xd5, 0xb7, - 0xeb, 0x33, 0x6a, 0x2d, 0xfc, 0x11, 0x21, 0x0b, - ], - }; - /// w = 3486911242272497535104403593250518247409663771668155364040899665266216860804 - static W: Scalar = Scalar{ - bytes: [ - 0x84, 0xfc, 0xbc, 0x4f, 0x78, 0x12, 0xa0, 0x06, - 0xd7, 0x91, 0xd9, 0x7a, 0x3a, 0x27, 0xdd, 0x1e, - 0x21, 0x43, 0x45, 0xf7, 0xb1, 0xb9, 0x56, 0x7a, - 0x81, 0x30, 0x73, 0x44, 0x96, 0x85, 0xb5, 0x07, - ], - }; /// x*y = 5690045403673944803228348699031245560686958845067437804563560795922180092780 static X_TIMES_Y: Scalar = Scalar{ From 0ba5c721225551327d4f8a496bf791b7be57e1bd Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 22 Mar 2018 11:59:13 -0700 Subject: [PATCH 20/23] This variable doesn't need to be mut since it's immediately consumed --- src/scalar.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scalar.rs b/src/scalar.rs index 34e1776..db17193 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -453,7 +453,7 @@ impl Scalar { // Wrap the tree storage in a ClearOnDrop to wipe it when we // pass out of scope. - let mut tree_vec = vec![one; 2*n]; + let tree_vec = vec![one; 2*n]; let mut tree = ClearOnDrop::new(tree_vec); for i in 0..inputs.len() { From f2e44898ee575c05995c5b7aa4f252799e9371b9 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 22 Mar 2018 12:06:00 -0700 Subject: [PATCH 21/23] Suppress warnings about square() on UnpackedScalars --- src/backend/u32/scalar.rs | 1 + src/backend/u64/scalar.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/backend/u32/scalar.rs b/src/backend/u32/scalar.rs index 238ee09..af88b79 100644 --- a/src/backend/u32/scalar.rs +++ b/src/backend/u32/scalar.rs @@ -341,6 +341,7 @@ impl Scalar32 { /// Compute `a^2` (mod l). #[inline(never)] + #[allow(dead_code)] // XXX we don't expose square() via the Scalar API pub fn square(&self) -> Scalar32 { let aa = Scalar32::montgomery_reduce(&Scalar32::square_internal(self)); Scalar32::montgomery_reduce(&Scalar32::mul_internal(&aa, &constants::RR)) diff --git a/src/backend/u64/scalar.rs b/src/backend/u64/scalar.rs index 91d4acc..c5d46d3 100644 --- a/src/backend/u64/scalar.rs +++ b/src/backend/u64/scalar.rs @@ -270,6 +270,7 @@ impl Scalar64 { /// Compute `a^2` (mod l) #[inline(never)] + #[allow(dead_code)] // XXX we don't expose square() via the Scalar API pub fn square(&self) -> Scalar64 { let aa = Scalar64::montgomery_reduce(&Scalar64::square_internal(self)); Scalar64::montgomery_reduce(&Scalar64::mul_internal(&aa, &constants::RR)) From 6f9c229e6574943e3002775d8ad4fedfc86d641d Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 22 Mar 2018 12:06:48 -0700 Subject: [PATCH 22/23] Remove Elligator stubs for now, since this isn't the API we want anyways --- src/edwards.rs | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/edwards.rs b/src/edwards.rs index dd979ba..33d299f 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -824,31 +824,6 @@ impl EdwardsPoint { } } -// ------------------------------------------------------------------------ -// Elligator2 (uniform encoding/decoding of curve points) -// ------------------------------------------------------------------------ - -// XXX should this be in another module, with types and `From` impls, like `CompressedEdwardsY`? - -impl EdwardsPoint { - /// Use Elligator2 to try to convert `self` to a uniformly random - /// string. - /// - /// Returns `Some<[u8;32]>` if `self` is in the image of the - /// Elligator2 map. For a random point on the curve, this happens - /// with probability 1/2. Otherwise, returns `None`. - fn to_uniform_representative(&self) -> Option<[u8; 32]> { - unimplemented!(); - } - - /// Use Elligator2 to convert a uniformly random string to a curve - /// point. - #[allow(unused_variables)] // REMOVE WHEN IMPLEMENTED - fn from_uniform_representative(bytes: &[u8; 32]) -> EdwardsPoint { - unimplemented!(); - } -} - // ------------------------------------------------------------------------ // Debug traits // ------------------------------------------------------------------------ From ce439458a19092ce232360b714511be753fa657f Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 22 Mar 2018 12:35:41 -0700 Subject: [PATCH 23/23] Bump version to 0.16.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ebcdf71..c9ff5ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "curve25519-dalek" -version = "0.15.1" +version = "0.16.0" authors = ["Isis Lovecruft ", "Henry de Valence "] readme = "README.md"