From 58be91f502a834eb07ba16f4a797026339f07949 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Tue, 30 Oct 2018 13:41:25 -0700 Subject: [PATCH 01/12] Refactor sqrt_ratio to return either sqrt(u/v) or sqrt(iu/v) Also removes the chi function since Ristretto elligator merges it with the square root. --- src/constants.rs | 5 +- src/edwards.rs | 4 +- src/field.rs | 134 +++++++++++++++++++++-------------------------- src/ristretto.rs | 19 +++---- 4 files changed, 71 insertions(+), 91 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index c990b099b..51833ac29 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -129,12 +129,13 @@ mod test { } } + /// Test that SQRT_M1 is the positive square root of -1 #[test] - /// Test that SQRT_M1 is a square root of -1 fn test_sqrt_minus_one() { let minus_one = FieldElement::minus_one(); let sqrt_m1_sq = &constants::SQRT_M1 * &constants::SQRT_M1; assert_eq!(minus_one, sqrt_m1_sq); + assert_eq!(constants::SQRT_M1.is_negative().unwrap_u8(), 0); } #[test] @@ -143,8 +144,6 @@ mod test { let (was_nonzero_square, invsqrt_m1) = minus_one.invsqrt(); 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? assert_eq!(sign_test_sqrt, minus_one); } diff --git a/src/edwards.rs b/src/edwards.rs index a09ce4570..7d801b11b 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -169,9 +169,9 @@ impl CompressedEdwardsY { let YY = Y.square(); let u = &YY - &Z; // u = y²-1 let v = &(&YY * &constants::EDWARDS_D) + &Z; // v = dy²+1 - let (is_nonzero_square, mut X) = FieldElement::sqrt_ratio(&u, &v); + let (is_valid_y_coord, mut X) = FieldElement::sqrt_ratio_i(&u, &v); - if is_nonzero_square.unwrap_u8() != 1u8 { return None; } + if is_valid_y_coord.unwrap_u8() != 1u8 { return None; } // Flip the sign of X if it's not correct let compressed_sign_bit = Choice::from(self.as_bytes()[31] >> 7); diff --git a/src/field.rs b/src/field.rs index e523df9ad..93d59d036 100644 --- a/src/field.rs +++ b/src/field.rs @@ -195,40 +195,19 @@ impl FieldElement { t21 } - /// Given `FieldElements` `u` and `v`, attempt to compute - /// `sqrt(u/v)` in constant time. + /// Given `FieldElements` `u` and `v`, compute either `sqrt(u/v)` + /// or `sqrt(i*u/v)` in constant time. /// - /// This function always returns the nonnegative square root, if it exists. - /// - /// It would be much better to use an `Option` type here, but - /// doing so forces the caller to branch, which we don't want to - /// do. This seems like the least bad solution. + /// This function always returns the nonnegative square root. /// /// # Return /// - /// - `(1u8, sqrt(u/v))` if `v` is nonzero and `u/v` is square; - /// - `(0u8, zero)` if `v` is zero; - /// - `(0u8, garbage)` if `u/v` is nonsquare. - /// - /// # 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 - /// ``` + /// - `(Choice(1), +sqrt(u/v)) ` if `v` is nonzero and `u/v` is square; + /// - `(Choice(1), zero) ` if `u` is zero; + /// - `(Choice(0), zero) ` if `v` is zero and `u` is nonzero; + /// - `(Choice(0), +sqrt(i*u/v))` if `u/v` is nonsquare (so `i*u/v` is square). /// - pub fn sqrt_ratio(u: &FieldElement, v: &FieldElement) -> (Choice, FieldElement) { + pub fn sqrt_ratio_i(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. // @@ -258,11 +237,14 @@ impl FieldElement { let mut r = &(u * &v3) * &(u * &v7).pow_p58(); let check = v * &r.square(); - let correct_sign_sqrt = check.ct_eq( u); - let flipped_sign_sqrt = check.ct_eq(&(-u)); + let i = &constants::SQRT_M1; + + let correct_sign_sqrt = check.ct_eq( u); + let flipped_sign_sqrt = check.ct_eq( &(-u)); + let flipped_sign_sqrt_i = check.ct_eq(&(&(-u)*i)); let r_prime = &constants::SQRT_M1 * &r; - r.conditional_assign(&r_prime, flipped_sign_sqrt); + r.conditional_assign(&r_prime, flipped_sign_sqrt | flipped_sign_sqrt_i); // Choose the nonnegative square root. let r_is_negative = r.is_negative(); @@ -273,42 +255,20 @@ impl FieldElement { (was_nonzero_square, r) } - /// For `self` a nonzero square, compute 1/sqrt(self) in - /// constant time. + /// Attempt to compute `1/sqrt(self)` in constant time. + /// + /// Convenience wrapper around `sqrt_ratio_i`. /// - /// It would be much better to use an `Option` type here, but - /// doing so forces the caller to branch, which we don't want to - /// do. This seems like the least bad solution. + /// This function always returns the nonnegative square root. /// /// # Return /// - /// - `(1u8, 1/sqrt(self))` if `self` is a nonzero square; - /// - `(0u8, zero)` if `self` is zero; - /// - `(0u8, garbage)` if `self` is nonsquare. + /// - `(Choice(1), +sqrt(1/self)) ` if `self` is a nonzero square; + /// - `(Choice(0), zero) ` if `self` is zero; + /// - `(Choice(0), +sqrt(i*u/v)) ` if `self` is a nonzero nonsquare; /// pub fn invsqrt(&self) -> (Choice, FieldElement) { - FieldElement::sqrt_ratio(&FieldElement::one(), self) - } - - /// chi calculates `self^((p-1)/2)`. - /// - /// # Return - /// - /// * If this element is a non-zero square, returns `1`. - /// * If it is zero, returns `0`. - /// * If it is non-square, returns `-1`. - pub fn chi(&self) -> FieldElement { // extra25519.chi - // The bits of (p-1)/2 = 2^254 -10 are 0110111111...11. - // - // nonzero bits of exponent - let (t19, _) = self.pow22501(); // 249..0 - let t20 = t19.pow2k(4); // 253..4 - let t21 = self.square(); // 1 - let t22 = t21.square(); // 2 - let t23 = &t22 * &t21; // 2,1 - let t24 = &t20 * &t23; // 253..4,2,1 - - t24 + FieldElement::sqrt_ratio_i(&FieldElement::one(), self) } } @@ -392,6 +352,45 @@ mod test { } } + #[test] + fn sqrt_ratio_behavior() { + let zero = FieldElement::zero(); + let one = FieldElement::one(); + let i = constants::SQRT_M1; + let two = &one + &one; // 2 is nonsquare mod p. + let four = &two + &two; // 4 is square mod p. + + // 0/0 should return (1, 0) since u is 0 + let (choice, sqrt) = FieldElement::sqrt_ratio_i(&zero, &zero); + assert_eq!(choice.unwrap_u8(), 1); + assert_eq!(sqrt, zero); + assert_eq!(sqrt.is_negative().unwrap_u8(), 0); + + // 1/0 should return (0, 0) since v is 0, u is nonzero + let (choice, sqrt) = FieldElement::sqrt_ratio_i(&one, &zero); + assert_eq!(choice.unwrap_u8(), 0); + assert_eq!(sqrt, zero); + assert_eq!(sqrt.is_negative().unwrap_u8(), 0); + + // 2/1 is nonsquare, so we expect (0, sqrt(i*2)) + let (choice, sqrt) = FieldElement::sqrt_ratio_i(&two, &one); + assert_eq!(choice.unwrap_u8(), 0); + assert_eq!(sqrt.square(), &two * &i); + assert_eq!(sqrt.is_negative().unwrap_u8(), 0); + + // 4/1 is square, so we expect (1, sqrt(4)) + let (choice, sqrt) = FieldElement::sqrt_ratio_i(&four, &one); + assert_eq!(choice.unwrap_u8(), 1); + assert_eq!(sqrt.square(), four); + assert_eq!(sqrt.is_negative().unwrap_u8(), 0); + + // 1/4 is square, so we expect (1, 1/sqrt(4)) + let (choice, sqrt) = FieldElement::sqrt_ratio_i(&one, &four); + assert_eq!(choice.unwrap_u8(), 1); + assert_eq!(&sqrt.square() * &four, one); + assert_eq!(sqrt.is_negative().unwrap_u8(), 0); + } + #[test] fn a_p58_vs_ap58_constant() { let a = FieldElement::from_bytes(&A_BYTES); @@ -399,17 +398,6 @@ mod test { assert_eq!(ap58, a.pow_p58()); } - #[test] - fn chi_on_square_and_nonsquare() { - let a = FieldElement::from_bytes(&A_BYTES); - // a is square - assert_eq!(a.chi(), FieldElement::one()); - let mut two_bytes = [0u8; 32]; two_bytes[0] = 2; - let two = FieldElement::from_bytes(&two_bytes); - // 2 is nonsquare - assert_eq!(two.chi(), FieldElement::minus_one()); - } - #[test] fn equality() { let a = FieldElement::from_bytes(&A_BYTES); diff --git a/src/ristretto.rs b/src/ristretto.rs index b34dbe89f..a2dc19e83 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -586,21 +586,14 @@ impl RistrettoPoint { let d_sq = d.square(); let N = -&( &(&d_sq - &one) * &(&r + &one) ); - let mut s = FieldElement::zero(); let mut c = -&one; + let (N_over_D_is_square, mut s) = FieldElement::sqrt_ratio_i(&N, &D); + let mut s_prime = &s * r_0; + let s_prime_is_pos = !s_prime.is_negative(); + s_prime.conditional_negate(s_prime_is_pos); - let (N_over_D_is_square, maybe_s) = FieldElement::sqrt_ratio(&N, &D); - // s = sqrt(N/D) if N/D is square - s.conditional_assign(&maybe_s, N_over_D_is_square); - - // XXX how exactly do we reuse the computation of sqrt(N/D) to find sqrt(rN/D) ? - let (rN_over_D_is_square, mut maybe_s) = FieldElement::sqrt_ratio(&(&r*&N), &D); - 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).unwrap_u8(), 1u8); - s.conditional_assign(&maybe_s, rN_over_D_is_square); - c.conditional_assign(&r, rN_over_D_is_square); + s.conditional_assign(&s_prime, !N_over_D_is_square); + c.conditional_assign(&r, !N_over_D_is_square); // T = (c * (r - one) * (d-one).square()) - D; let T = &(&c * &(&(&r - &one) * &((d - &one).square()))) - &D; From d664a0bdf45efebe3f9d18d8d13b82a237311f2e Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 1 Nov 2018 15:19:13 -0700 Subject: [PATCH 02/12] Tweak ristretto elligator to match ristretto.group description --- src/ristretto.rs | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/ristretto.rs b/src/ristretto.rs index a2dc19e83..a0dd2afce 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -577,37 +577,32 @@ impl RistrettoPoint { pub(crate) fn elligator_ristretto_flavor(r_0: &FieldElement) -> RistrettoPoint { let (i, d) = (&constants::SQRT_M1, &constants::EDWARDS_D); let one = FieldElement::one(); + let one_minus_d_sq = &one - &d.square(); + let d_minus_one_sq = (d - &one).square(); let r = i * &r_0.square(); - - // D = (dr -a)(ar-d) = -(dr+1)(r+d) - let D = -&( &(&(d * &r) + &one) * &(&r + d) ); - // N = a(d-a)(d+a)(r+1) = -(r+1)(d^2 -1) - let d_sq = d.square(); - let N = -&( &(&d_sq - &one) * &(&r + &one) ); - + let N_s = &(&r + &one) * &one_minus_d_sq; let mut c = -&one; - let (N_over_D_is_square, mut s) = FieldElement::sqrt_ratio_i(&N, &D); + let D = &(&c - &(d * &r)) * &(&r + d); + + let (Ns_D_is_sq, mut s) = FieldElement::sqrt_ratio_i(&N_s, &D); let mut s_prime = &s * r_0; let s_prime_is_pos = !s_prime.is_negative(); s_prime.conditional_negate(s_prime_is_pos); - s.conditional_assign(&s_prime, !N_over_D_is_square); - c.conditional_assign(&r, !N_over_D_is_square); - - // T = (c * (r - one) * (d-one).square()) - D; - let T = &(&c * &(&(&r - &one) * &((d - &one).square()))) - &D; + s.conditional_assign(&s_prime, !Ns_D_is_sq); + c.conditional_assign(&r, !Ns_D_is_sq); + let N_t = &(&(&c * &(&r - &one)) * &d_minus_one_sq) - &D; let s_sq = s.square(); - let P = CompletedPoint{ + + // The conversion from W_i is exactly the conversion from P1xP1. + RistrettoPoint(CompletedPoint{ X: &(&s + &s) * &D, - Z: &T * &constants::SQRT_AD_MINUS_ONE, + Z: &N_t * &constants::SQRT_AD_MINUS_ONE, Y: &FieldElement::one() - &s_sq, T: &FieldElement::one() + &s_sq, - }; - - // Convert to extended and return. - RistrettoPoint(P.to_extended()) + }.to_extended()) } /// Return a `RistrettoPoint` chosen uniformly at random using a user-provided RNG. From 44b9589154c03fd0843d1d6647c8632f3a935d2e Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Fri, 2 Nov 2018 14:16:51 -0700 Subject: [PATCH 03/12] Change externally-exposed API to implement ConditionallySelectable --- src/edwards.rs | 40 +++++++++++++++++++++++----------------- src/ristretto.rs | 38 ++++++++++++++++++++------------------ src/scalar.rs | 24 +++++++++++++----------- 3 files changed, 56 insertions(+), 46 deletions(-) diff --git a/src/edwards.rs b/src/edwards.rs index 7d801b11b..9ed9f1dc0 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -70,7 +70,7 @@ //! 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][curve_models] +//! see the [`curve_models` submodule][curve_models] //! of the internal documentation. //! //! ## Validity Checking @@ -92,17 +92,17 @@ // affine and projective cakes and eat both of them too. #![allow(non_snake_case)] +use core::borrow::Borrow; use core::fmt::Debug; use core::iter::Iterator; -use core::ops::{Add, Sub, Neg}; +use core::iter::Sum; +use core::ops::{Add, Neg, Sub}; use core::ops::{AddAssign, SubAssign}; use core::ops::{Mul, MulAssign}; -use core::iter::Sum; -use core::borrow::Borrow; -use subtle::ConditionallyAssignable; -use subtle::ConditionallyNegatable; use subtle::Choice; +use subtle::ConditionallyNegatable; +use subtle::ConditionallySelectable; use subtle::ConstantTimeEq; use constants; @@ -328,10 +328,12 @@ impl CompressedEdwardsY { impl Identity for EdwardsPoint { fn identity() -> EdwardsPoint { - EdwardsPoint{ X: FieldElement::zero(), - Y: FieldElement::one(), - Z: FieldElement::one(), - T: FieldElement::zero() } + EdwardsPoint { + X: FieldElement::zero(), + Y: FieldElement::one(), + Z: FieldElement::one(), + T: FieldElement::zero(), + } } } @@ -358,12 +360,14 @@ impl ValidityCheck for EdwardsPoint { // Constant-time assignment // ------------------------------------------------------------------------ -impl ConditionallyAssignable for EdwardsPoint { - 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); - self.T.conditional_assign(&other.T, choice); +impl ConditionallySelectable for EdwardsPoint { + fn conditional_select(a: &EdwardsPoint, b: &EdwardsPoint, choice: Choice) -> EdwardsPoint { + EdwardsPoint { + X: FieldElement::conditional_select(&a.X, &b.X, choice), + Y: FieldElement::conditional_select(&a.Y, &b.Y, choice), + Z: FieldElement::conditional_select(&a.Z, &b.Z, choice), + T: FieldElement::conditional_select(&a.T, &b.T, choice), + } } } @@ -373,7 +377,9 @@ impl ConditionallyAssignable for EdwardsPoint { impl ConstantTimeEq for EdwardsPoint { fn ct_eq(&self, other: &EdwardsPoint) -> Choice { - self.compress().as_bytes().ct_eq(other.compress().as_bytes()) + self.compress() + .as_bytes() + .ct_eq(other.compress().as_bytes()) } } diff --git a/src/ristretto.rs b/src/ristretto.rs index a0dd2afce..ed2abc421 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -157,28 +157,29 @@ //! [ristretto_main]: //! https://ristretto.group/ +use core::borrow::Borrow; use core::fmt::Debug; -use core::ops::{Add, Sub, Neg}; +use core::iter::Sum; +use core::ops::{Add, Neg, Sub}; use core::ops::{AddAssign, SubAssign}; use core::ops::{Mul, MulAssign}; -use core::iter::Sum; -use core::borrow::Borrow; -use rand::{Rng, CryptoRng}; +use rand::{CryptoRng, Rng}; -use digest::Digest; use digest::generic_array::typenum::U64; +use digest::Digest; use constants; use field::FieldElement; +use subtle::Choice; use subtle::ConditionallyAssignable; use subtle::ConditionallyNegatable; +use subtle::ConditionallySelectable; use subtle::ConstantTimeEq; -use subtle::Choice; -use edwards::EdwardsPoint; use edwards::EdwardsBasepointTable; +use edwards::EdwardsPoint; #[allow(unused_imports)] use prelude::*; @@ -949,11 +950,11 @@ impl RistrettoBasepointTable { } // ------------------------------------------------------------------------ -// Constant-time conditional assignment +// Constant-time conditional selection // ------------------------------------------------------------------------ -impl ConditionallyAssignable for RistrettoPoint { - /// Conditionally assign `other` to `self`, if `choice == Choice(1)`. +impl ConditionallySelectable for RistrettoPoint { + /// Conditionally select between `self` and `other`. /// /// # Example /// @@ -961,7 +962,7 @@ impl ConditionallyAssignable for RistrettoPoint { /// # extern crate subtle; /// # extern crate curve25519_dalek; /// # - /// use subtle::ConditionallyAssignable; + /// use subtle::ConditionallySelectable; /// use subtle::Choice; /// # /// # use curve25519_dalek::traits::Identity; @@ -974,17 +975,18 @@ impl ConditionallyAssignable for RistrettoPoint { /// /// let mut P = A; /// - /// P.conditional_assign(&B, Choice::from(0)); + /// P = RistrettoPoint::conditional_select(&A, &B, Choice::from(0)); /// assert_eq!(P, A); - /// P.conditional_assign(&B, Choice::from(1)); + /// P = RistrettoPoint::conditional_select(&A, &B, Choice::from(1)); /// assert_eq!(P, B); /// # } /// ``` - 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); - self.0.T.conditional_assign(&other.0.T, choice); + fn conditional_select( + a: &RistrettoPoint, + b: &RistrettoPoint, + choice: Choice, + ) -> RistrettoPoint { + RistrettoPoint(EdwardsPoint::conditional_select(&a.0, &b.0, choice)) } } diff --git a/src/scalar.rs b/src/scalar.rs index 06c2f3c64..eacd4e343 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -138,26 +138,26 @@ //! The resulting `Scalar` has exactly the specified bit pattern, //! **except for the highest bit, which will be set to 0**. +use core::borrow::Borrow; +use core::cmp::{Eq, PartialEq}; use core::fmt::Debug; +use core::iter::{Product, Sum}; +use core::ops::Index; use core::ops::Neg; use core::ops::{Add, AddAssign}; -use core::ops::{Sub, SubAssign}; use core::ops::{Mul, MulAssign}; -use core::ops::{Index}; -use core::cmp::{Eq, PartialEq}; -use core::iter::{Product, Sum}; -use core::borrow::Borrow; +use core::ops::{Sub, SubAssign}; #[allow(unused_imports)] use prelude::*; -use rand::{Rng, CryptoRng}; +use rand::{CryptoRng, Rng}; -use digest::Digest; use digest::generic_array::typenum::U64; +use digest::Digest; use subtle::Choice; -use subtle::ConditionallyAssignable; +use subtle::ConditionallySelectable; use subtle::ConstantTimeEq; use backend; @@ -343,11 +343,13 @@ impl<'a> Neg for Scalar { } } -impl ConditionallyAssignable for Scalar { - fn conditional_assign(&mut self, other: &Scalar, choice: Choice) { +impl ConditionallySelectable for Scalar { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + let mut bytes = [0u8; 32]; for i in 0..32 { - self.bytes[i].conditional_assign(&other.bytes[i], choice); + bytes[i] = u8::conditional_select(&a.bytes[i], &b.bytes[i], choice); } + Scalar { bytes } } } From 8821f0ee7291e933b4fc0d3e31ac2b622a6924c2 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Fri, 2 Nov 2018 14:17:43 -0700 Subject: [PATCH 04/12] Change internal API to use ConditionallySelectable --- src/backend/avx2/edwards.rs | 14 ++++++------- src/backend/avx2/field.rs | 20 ++++++++++++------ src/backend/u32/field.rs | 25 ++++++++++++++++------ src/backend/u64/field.rs | 24 ++++++++++++++-------- src/curve_models/mod.rs | 41 ++++++++++++++++++++----------------- src/montgomery.rs | 24 ++++++++++++++-------- 6 files changed, 93 insertions(+), 55 deletions(-) diff --git a/src/backend/avx2/edwards.rs b/src/backend/avx2/edwards.rs index 1510bdafe..cf9e881ba 100644 --- a/src/backend/avx2/edwards.rs +++ b/src/backend/avx2/edwards.rs @@ -38,7 +38,7 @@ use core::convert::From; use core::ops::{Add, Neg, Sub}; use subtle::Choice; -use subtle::ConditionallyAssignable; +use subtle::ConditionallySelectable; use edwards; use scalar_mul::window::{LookupTable, NafLookupTable5, NafLookupTable8}; @@ -76,9 +76,9 @@ impl From for edwards::EdwardsPoint { } } -impl ConditionallyAssignable for ExtendedPoint { - fn conditional_assign(&mut self, other: &ExtendedPoint, choice: Choice) { - self.0.conditional_assign(&other.0, choice); +impl ConditionallySelectable for ExtendedPoint { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + ExtendedPoint(FieldElement32x4::conditional_select(&a.0, &b.0, choice)) } } @@ -209,9 +209,9 @@ impl Identity for CachedPoint { } } -impl ConditionallyAssignable for CachedPoint { - fn conditional_assign(&mut self, other: &CachedPoint, choice: Choice) { - self.0.conditional_assign(&other.0, choice); +impl ConditionallySelectable for CachedPoint { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + CachedPoint(FieldElement32x4::conditional_select(&a.0, &b.0, choice)) } } diff --git a/src/backend/avx2/field.rs b/src/backend/avx2/field.rs index 6f4e1fd44..a37ed50ec 100644 --- a/src/backend/avx2/field.rs +++ b/src/backend/avx2/field.rs @@ -144,15 +144,23 @@ pub enum Shuffle { pub struct FieldElement32x4(pub(crate) [u32x8; 5]); use subtle::Choice; -use subtle::ConditionallyAssignable; +use subtle::ConditionallySelectable; -impl ConditionallyAssignable for FieldElement32x4 { - fn conditional_assign(&mut self, other: &FieldElement32x4, choice: Choice) { +impl ConditionallySelectable for FieldElement32x4 { + fn conditional_select( + a: &FieldElement32x4, + b: &FieldElement32x4, + choice: Choice, + ) -> FieldElement32x4 { 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])); - } + FieldElement32x4([ + self.0[0] ^ (mask_vec & (self.0[0] ^ other.0[0])), + self.0[1] ^ (mask_vec & (self.0[1] ^ other.0[1])), + self.0[2] ^ (mask_vec & (self.0[2] ^ other.0[2])), + self.0[3] ^ (mask_vec & (self.0[3] ^ other.0[3])), + self.0[4] ^ (mask_vec & (self.0[4] ^ other.0[4])), + ]) } } diff --git a/src/backend/u32/field.rs b/src/backend/u32/field.rs index 9de460aa9..2a9f5bb80 100644 --- a/src/backend/u32/field.rs +++ b/src/backend/u32/field.rs @@ -21,7 +21,7 @@ use core::ops::{Sub, SubAssign}; use core::ops::{Mul, MulAssign}; use core::ops::Neg; -use subtle::ConditionallyAssignable; +use subtle::ConditionallySelectable; use subtle::Choice; /// A `FieldElement32` represents an element of the field @@ -219,11 +219,24 @@ impl<'a> Neg for &'a FieldElement32 { } } -impl ConditionallyAssignable for FieldElement32 { - fn conditional_assign(&mut self, other: &FieldElement32, choice: Choice) { - for i in 0..10 { - self.0[i].conditional_assign(&other.0[i], choice); - } +impl ConditionallySelectable for FieldElement32 { + fn conditional_select( + a: &FieldElement32, + b: &FieldElement32, + choice: Choice, + ) -> FieldElement32 { + FieldElement32([ + u32::conditional_select(&a.0[0], &b.0[0], choice), + u32::conditional_select(&a.0[1], &b.0[1], choice), + u32::conditional_select(&a.0[2], &b.0[2], choice), + u32::conditional_select(&a.0[3], &b.0[3], choice), + u32::conditional_select(&a.0[4], &b.0[4], choice), + u32::conditional_select(&a.0[5], &b.0[5], choice), + u32::conditional_select(&a.0[6], &b.0[6], choice), + u32::conditional_select(&a.0[7], &b.0[7], choice), + u32::conditional_select(&a.0[8], &b.0[8], choice), + u32::conditional_select(&a.0[9], &b.0[9], choice), + ]) } } diff --git a/src/backend/u64/field.rs b/src/backend/u64/field.rs index 25a013e45..9276af4da 100644 --- a/src/backend/u64/field.rs +++ b/src/backend/u64/field.rs @@ -12,13 +12,13 @@ //! limbs with \\(128\\)-bit products. use core::fmt::Debug; +use core::ops::Neg; use core::ops::{Add, AddAssign}; -use core::ops::{Sub, SubAssign}; use core::ops::{Mul, MulAssign}; -use core::ops::Neg; +use core::ops::{Sub, SubAssign}; -use subtle::ConditionallyAssignable; use subtle::Choice; +use subtle::ConditionallySelectable; /// A `FieldElement64` represents an element of the field /// \\( \mathbb Z / (2\^{255} - 19)\\). @@ -209,11 +209,19 @@ impl<'a> Neg for &'a FieldElement64 { } } -impl ConditionallyAssignable for FieldElement64 { - fn conditional_assign(&mut self, other: &FieldElement64, choice: Choice) { - for i in 0..5 { - self.0[i].conditional_assign(&other.0[i], choice); - } +impl ConditionallySelectable for FieldElement64 { + fn conditional_select( + a: &FieldElement64, + b: &FieldElement64, + choice: Choice, + ) -> FieldElement64 { + FieldElement64([ + u64::conditional_select(&a.0[0], &b.0[0], choice), + u64::conditional_select(&a.0[1], &b.0[1], choice), + u64::conditional_select(&a.0[2], &b.0[2], choice), + u64::conditional_select(&a.0[3], &b.0[3], choice), + u64::conditional_select(&a.0[4], &b.0[4], choice), + ]) } } diff --git a/src/curve_models/mod.rs b/src/curve_models/mod.rs index 3f68132b8..7c472c916 100644 --- a/src/curve_models/mod.rs +++ b/src/curve_models/mod.rs @@ -124,15 +124,15 @@ #![allow(non_snake_case)] use core::fmt::Debug; -use core::ops::{Add, Sub, Neg}; +use core::ops::{Add, Neg, Sub}; -use subtle::ConditionallyAssignable; use subtle::Choice; +use subtle::ConditionallySelectable; use constants; -use field::FieldElement; use edwards::EdwardsPoint; +use field::FieldElement; use traits::ValidityCheck; // ------------------------------------------------------------------------ @@ -204,7 +204,7 @@ use traits::Identity; impl Identity for ProjectivePoint { fn identity() -> ProjectivePoint { - ProjectivePoint{ + ProjectivePoint { X: FieldElement::zero(), Y: FieldElement::one(), Z: FieldElement::one(), @@ -268,21 +268,24 @@ impl ValidityCheck for ProjectivePoint { // Constant-time assignment // ------------------------------------------------------------------------ -impl ConditionallyAssignable for ProjectiveNielsPoint { - 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); - self.T2d.conditional_assign(&other.T2d, choice); +impl ConditionallySelectable for ProjectiveNielsPoint { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + ProjectiveNielsPoint { + Y_plus_X: FieldElement::conditional_select(&a.Y_plus_X, &b.Y_plus_X, choice), + Y_minus_X: FieldElement::conditional_select(&a.Y_minus_X, &b.Y_minus_X, choice), + Z: FieldElement::conditional_select(&a.Z, &b.Z, choice), + T2d: FieldElement::conditional_select(&a.T2d, &b.T2d, choice), + } } } -impl ConditionallyAssignable for AffineNielsPoint { - 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); - self.xy2d.conditional_assign(&other.xy2d, choice); +impl ConditionallySelectable for AffineNielsPoint { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + AffineNielsPoint { + y_plus_x: FieldElement::conditional_select(&a.y_plus_x, &b.y_plus_x, choice), + y_minus_x: FieldElement::conditional_select(&a.y_minus_x, &b.y_minus_x, choice), + xy2d: FieldElement::conditional_select(&a.xy2d, &b.xy2d, choice), + } } } @@ -296,7 +299,7 @@ impl ProjectivePoint { /// /// This costs \\(3 \mathrm M + 1 \mathrm S\\). pub fn to_extended(&self) -> EdwardsPoint { - EdwardsPoint{ + EdwardsPoint { X: &self.X * &self.Z, Y: &self.Y * &self.Z, Z: self.Z.square(), @@ -311,7 +314,7 @@ impl CompletedPoint { /// /// This costs \\(3 \mathrm M \\). pub fn to_projective(&self) -> ProjectivePoint { - ProjectivePoint{ + ProjectivePoint { X: &self.X * &self.T, Y: &self.Y * &self.Z, Z: &self.Z * &self.T, @@ -323,7 +326,7 @@ impl CompletedPoint { /// /// This costs \\(4 \mathrm M \\). pub fn to_extended(&self) -> EdwardsPoint { - EdwardsPoint{ + EdwardsPoint { X: &self.X * &self.T, Y: &self.Y * &self.Z, Z: &self.Z * &self.T, diff --git a/src/montgomery.rs b/src/montgomery.rs index fe7423b4a..76ef4e266 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -29,7 +29,7 @@ //! //! Scalar multiplication on `MontgomeryPoint`s is provided by the `*` //! operator, which implements the Montgomery ladder. -//! +//! //! # Edwards Conversion //! //! The \\(2\\)-to-\\(1\\) map from the Edwards model to the Montgomery @@ -57,10 +57,10 @@ use scalar::Scalar; use traits::Identity; -use subtle::ConditionallyAssignable; +use subtle::Choice; +use subtle::ConditionallySelectable; use subtle::ConditionallySwappable; use subtle::ConstantTimeEq; -use subtle::Choice; /// Holds the \\(u\\)-coordinate of a point on the Montgomery form of /// Curve25519 or its twist. @@ -141,7 +141,7 @@ impl MontgomeryPoint { /// \\( \mathbb P(\mathbb F\_p) \\), which we identify with the Kummer /// line of the Montgomery curve. #[derive(Copy, Clone, Debug)] -struct ProjectivePoint{ +struct ProjectivePoint { pub U: FieldElement, pub W: FieldElement, } @@ -161,10 +161,16 @@ impl Default for ProjectivePoint { } } -impl ConditionallyAssignable for ProjectivePoint { - fn conditional_assign(&mut self, that: &ProjectivePoint, choice: Choice) { - self.U.conditional_assign(&that.U, choice); - self.W.conditional_assign(&that.W, choice); +impl ConditionallySelectable for ProjectivePoint { + fn conditional_select( + a: &ProjectivePoint, + b: &ProjectivePoint, + choice: Choice, + ) -> ProjectivePoint { + ProjectivePoint { + U: FieldElement::conditional_select(&a.U, &b.U, choice), + W: FieldElement::conditional_select(&a.W, &b.W, choice), + } } } @@ -196,7 +202,7 @@ impl ProjectivePoint { /// (U\_Q : W\_Q) \gets u(P + Q). /// $$ fn differential_add_and_double( - P: &mut ProjectivePoint, + P: &mut ProjectivePoint, Q: &mut ProjectivePoint, affine_PmQ: &FieldElement, ) { From 9d17e5937dac7a4d64c0820e693b795313d325df Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 5 Nov 2018 11:16:24 -0800 Subject: [PATCH 05/12] test subtle 2.0 --- Cargo.toml | 7 +++++-- src/backend/avx2/edwards.rs | 8 ++++++++ src/backend/avx2/field.rs | 24 +++++++++++++++++++----- src/backend/u32/field.rs | 32 +++++++++++++++++++++++++++++--- src/backend/u64/field.rs | 16 ++++++++++++++++ src/curve_models/mod.rs | 13 +++++++++++++ src/edwards.rs | 2 +- src/field.rs | 2 +- src/montgomery.rs | 14 ++++++++------ src/ristretto.rs | 3 +-- src/scalar.rs | 2 +- src/scalar_mul/window.rs | 4 ++-- 12 files changed, 104 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 558a062b0..4ca1999c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,7 @@ rand = { version = "0.5", default-features = false } byteorder = { version = "^1.2.3", default-features = false, features = ["i128"] } digest = { version = "0.8", default-features = false } clear_on_drop = "=0.2.3" -subtle = { version = "1", default-features = false } +subtle = { version = "2", default-features = false } serde = { version = "1.0", optional = true } packed_simd = { version = "0.3.0", features = ["into_bits"], optional = true } @@ -54,7 +54,7 @@ rand = { version = "0.5", default-features = false } byteorder = { version = "^1.2.3", default-features = false, features = ["i128"] } digest = { version = "0.8", default-features = false } clear_on_drop = "=0.2.3" -subtle = { version = "1", default-features = false } +subtle = { version = "2", default-features = false } serde = { version = "1.0", optional = true } packed_simd = { version = "0.3.0", features = ["into_bits"], optional = true } @@ -78,3 +78,6 @@ avx2_backend = ["nightly", "u64_backend", "packed_simd"] # into the build script. Then, the build.rs emits the stage2_build # feature before the main-stage compilation. stage2_build = [] + +[patch.crates-io] +subtle = { git = "https://github.com/dalek-cryptography/subtle", branch = "fix-subtle-traits" } diff --git a/src/backend/avx2/edwards.rs b/src/backend/avx2/edwards.rs index cf9e881ba..18ecfde00 100644 --- a/src/backend/avx2/edwards.rs +++ b/src/backend/avx2/edwards.rs @@ -80,6 +80,10 @@ impl ConditionallySelectable for ExtendedPoint { fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { ExtendedPoint(FieldElement32x4::conditional_select(&a.0, &b.0, choice)) } + + fn conditional_assign(&mut self, other: &Self, choice: Choice) { + self.0.conditional_assign(&other.0, choice); + } } impl Default for ExtendedPoint { @@ -213,6 +217,10 @@ impl ConditionallySelectable for CachedPoint { fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { CachedPoint(FieldElement32x4::conditional_select(&a.0, &b.0, choice)) } + + fn conditional_assign(&mut self, other: &Self, choice: Choice) { + self.0.conditional_assign(&other.0, choice); + } } impl<'a> Neg for &'a CachedPoint { diff --git a/src/backend/avx2/field.rs b/src/backend/avx2/field.rs index a37ed50ec..27ba5584e 100644 --- a/src/backend/avx2/field.rs +++ b/src/backend/avx2/field.rs @@ -155,13 +155,27 @@ impl ConditionallySelectable for FieldElement32x4 { let mask = (-(choice.unwrap_u8() as i32)) as u32; let mask_vec = u32x8::splat(mask); FieldElement32x4([ - self.0[0] ^ (mask_vec & (self.0[0] ^ other.0[0])), - self.0[1] ^ (mask_vec & (self.0[1] ^ other.0[1])), - self.0[2] ^ (mask_vec & (self.0[2] ^ other.0[2])), - self.0[3] ^ (mask_vec & (self.0[3] ^ other.0[3])), - self.0[4] ^ (mask_vec & (self.0[4] ^ other.0[4])), + a.0[0] ^ (mask_vec & (a.0[0] ^ b.0[0])), + a.0[1] ^ (mask_vec & (a.0[1] ^ b.0[1])), + a.0[2] ^ (mask_vec & (a.0[2] ^ b.0[2])), + a.0[3] ^ (mask_vec & (a.0[3] ^ b.0[3])), + a.0[4] ^ (mask_vec & (a.0[4] ^ b.0[4])), ]) } + + fn conditional_assign( + &mut self, + other: &FieldElement32x4, + choice: Choice, + ) { + let mask = (-(choice.unwrap_u8() as i32)) as u32; + let mask_vec = u32x8::splat(mask); + self.0[0] ^= mask_vec & (self.0[0] ^ other.0[0]); + self.0[1] ^= mask_vec & (self.0[1] ^ other.0[1]); + self.0[2] ^= mask_vec & (self.0[2] ^ other.0[2]); + self.0[3] ^= mask_vec & (self.0[3] ^ other.0[3]); + self.0[4] ^= mask_vec & (self.0[4] ^ other.0[4]); + } } impl FieldElement32x4 { diff --git a/src/backend/u32/field.rs b/src/backend/u32/field.rs index 2a9f5bb80..5f2fe1d07 100644 --- a/src/backend/u32/field.rs +++ b/src/backend/u32/field.rs @@ -16,13 +16,13 @@ //! of signed limbs. use core::fmt::Debug; +use core::ops::Neg; use core::ops::{Add, AddAssign}; -use core::ops::{Sub, SubAssign}; use core::ops::{Mul, MulAssign}; -use core::ops::Neg; +use core::ops::{Sub, SubAssign}; -use subtle::ConditionallySelectable; use subtle::Choice; +use subtle::ConditionallySelectable; /// A `FieldElement32` represents an element of the field /// \\( \mathbb Z / (2\^{255} - 19)\\). @@ -238,6 +238,32 @@ impl ConditionallySelectable for FieldElement32 { u32::conditional_select(&a.0[9], &b.0[9], choice), ]) } + + fn conditional_assign(&mut self, other: &FieldElement32, choice: Choice) { + self.0[0].conditional_assign(&other.0[0], choice); + self.0[1].conditional_assign(&other.0[1], choice); + self.0[2].conditional_assign(&other.0[2], choice); + self.0[3].conditional_assign(&other.0[3], choice); + self.0[4].conditional_assign(&other.0[4], choice); + self.0[5].conditional_assign(&other.0[5], choice); + self.0[6].conditional_assign(&other.0[6], choice); + self.0[7].conditional_assign(&other.0[7], choice); + self.0[8].conditional_assign(&other.0[8], choice); + self.0[9].conditional_assign(&other.0[9], choice); + } + + fn conditional_swap(a: &mut FieldElement32, b: &mut FieldElement32, choice: Choice) { + u32::conditional_swap(&mut a.0[0], &mut b.0[0], choice); + u32::conditional_swap(&mut a.0[1], &mut b.0[1], choice); + u32::conditional_swap(&mut a.0[2], &mut b.0[2], choice); + u32::conditional_swap(&mut a.0[3], &mut b.0[3], choice); + u32::conditional_swap(&mut a.0[4], &mut b.0[4], choice); + u32::conditional_swap(&mut a.0[5], &mut b.0[5], choice); + u32::conditional_swap(&mut a.0[6], &mut b.0[6], choice); + u32::conditional_swap(&mut a.0[7], &mut b.0[7], choice); + u32::conditional_swap(&mut a.0[8], &mut b.0[8], choice); + u32::conditional_swap(&mut a.0[9], &mut b.0[9], choice); + } } impl FieldElement32 { diff --git a/src/backend/u64/field.rs b/src/backend/u64/field.rs index 9276af4da..ffe1a486c 100644 --- a/src/backend/u64/field.rs +++ b/src/backend/u64/field.rs @@ -223,6 +223,22 @@ impl ConditionallySelectable for FieldElement64 { u64::conditional_select(&a.0[4], &b.0[4], choice), ]) } + + fn conditional_swap(a: &mut FieldElement64, b: &mut FieldElement64, choice: Choice) { + u64::conditional_swap(&mut a.0[0], &mut b.0[0], choice); + u64::conditional_swap(&mut a.0[1], &mut b.0[1], choice); + u64::conditional_swap(&mut a.0[2], &mut b.0[2], choice); + u64::conditional_swap(&mut a.0[3], &mut b.0[3], choice); + u64::conditional_swap(&mut a.0[4], &mut b.0[4], choice); + } + + fn conditional_assign(&mut self, other: &FieldElement64, choice: Choice) { + self.0[0].conditional_assign(&other.0[0], choice); + self.0[1].conditional_assign(&other.0[1], choice); + self.0[2].conditional_assign(&other.0[2], choice); + self.0[3].conditional_assign(&other.0[3], choice); + self.0[4].conditional_assign(&other.0[4], choice); + } } impl FieldElement64 { diff --git a/src/curve_models/mod.rs b/src/curve_models/mod.rs index 7c472c916..8133818c6 100644 --- a/src/curve_models/mod.rs +++ b/src/curve_models/mod.rs @@ -277,6 +277,13 @@ impl ConditionallySelectable for ProjectiveNielsPoint { T2d: FieldElement::conditional_select(&a.T2d, &b.T2d, choice), } } + + fn conditional_assign(&mut self, other: &Self, 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); + self.T2d.conditional_assign(&other.T2d, choice); + } } impl ConditionallySelectable for AffineNielsPoint { @@ -287,6 +294,12 @@ impl ConditionallySelectable for AffineNielsPoint { xy2d: FieldElement::conditional_select(&a.xy2d, &b.xy2d, choice), } } + + fn conditional_assign(&mut self, other: &Self, 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.xy2d.conditional_assign(&other.xy2d, choice); + } } // ------------------------------------------------------------------------ diff --git a/src/edwards.rs b/src/edwards.rs index 9ed9f1dc0..b37a9da40 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -903,7 +903,7 @@ impl Debug for EdwardsBasepointTable { mod test { use field::FieldElement; use scalar::Scalar; - use subtle::ConditionallyAssignable; + use subtle::ConditionallySelectable; use constants; use super::*; diff --git a/src/field.rs b/src/field.rs index 93d59d036..44486958e 100644 --- a/src/field.rs +++ b/src/field.rs @@ -24,7 +24,7 @@ use core::cmp::{Eq, PartialEq}; -use subtle::ConditionallyAssignable; +use subtle::ConditionallySelectable; use subtle::ConditionallyNegatable; use subtle::Choice; use subtle::ConstantTimeEq; diff --git a/src/montgomery.rs b/src/montgomery.rs index 76ef4e266..d07e6af55 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -51,15 +51,14 @@ use core::ops::{Mul, MulAssign}; use constants::APLUS2_OVER_FOUR; +use edwards::{CompressedEdwardsY, EdwardsPoint}; use field::FieldElement; -use edwards::{EdwardsPoint, CompressedEdwardsY}; use scalar::Scalar; use traits::Identity; use subtle::Choice; use subtle::ConditionallySelectable; -use subtle::ConditionallySwappable; use subtle::ConstantTimeEq; /// Holds the \\(u\\)-coordinate of a point on the Montgomery form of @@ -255,19 +254,22 @@ impl<'a, 'b> Mul<&'b Scalar> for &'a MontgomeryPoint { // Algorithm 8 of Costello-Smith 2017 let affine_u = FieldElement::from_bytes(&self.0); let mut x0 = ProjectivePoint::identity(); - let mut x1 = ProjectivePoint{ U: affine_u, W: FieldElement::one() }; + let mut x1 = ProjectivePoint { + U: affine_u, + W: FieldElement::one(), + }; let bits: [i8; 256] = scalar.bits(); for i in (0..255).rev() { - let choice: u8 = (bits[i+1] ^ bits[i]) as u8; + let choice: u8 = (bits[i + 1] ^ bits[i]) as u8; debug_assert!(choice == 0 || choice == 1); - x0.conditional_swap(&mut x1, choice.into()); + ProjectivePoint::conditional_swap(&mut x0, &mut x1, choice.into()); differential_add_and_double(&mut x0, &mut x1, &affine_u); } - x0.conditional_swap(&mut x1, Choice::from(bits[0] as u8)); + ProjectivePoint::conditional_swap(&mut x0, &mut x1, Choice::from(bits[0] as u8)); x0.to_affine() } diff --git a/src/ristretto.rs b/src/ristretto.rs index ed2abc421..e6375edae 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -173,9 +173,8 @@ use constants; use field::FieldElement; use subtle::Choice; -use subtle::ConditionallyAssignable; -use subtle::ConditionallyNegatable; use subtle::ConditionallySelectable; +use subtle::ConditionallyNegatable; use subtle::ConstantTimeEq; use edwards::EdwardsBasepointTable; diff --git a/src/scalar.rs b/src/scalar.rs index eacd4e343..2e4d2560f 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -983,7 +983,7 @@ impl Scalar { /// # extern crate curve25519_dalek; /// # extern crate subtle; /// # use curve25519_dalek::scalar::Scalar; - /// # use subtle::ConditionallyAssignable; + /// # use subtle::ConditionallySelectable; /// # fn main() { /// // 2^255 - 1, since `from_bits` clears the high bit /// let _2_255_minus_1 = Scalar::from_bits([0xff;32]); diff --git a/src/scalar_mul/window.rs b/src/scalar_mul/window.rs index c11613669..d5ad2d6cf 100644 --- a/src/scalar_mul/window.rs +++ b/src/scalar_mul/window.rs @@ -15,7 +15,7 @@ use core::fmt::Debug; use subtle::ConditionallyNegatable; -use subtle::ConditionallyAssignable; +use subtle::ConditionallySelectable; use subtle::ConstantTimeEq; use subtle::Choice; @@ -59,7 +59,7 @@ unsafe impl ZeroSafe for LookupTable {} impl LookupTable where - T: Identity + ConditionallyAssignable + ConditionallyNegatable, + T: Identity + ConditionallySelectable + ConditionallyNegatable, { /// Given \\(-8 \leq x \leq 8\\), return \\(xP\\) in constant time. pub fn select(&self, x: i8) -> T { From 5fe20ae1274b80d8bfdd7e575a621a8c22628291 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 5 Nov 2018 15:09:50 -0800 Subject: [PATCH 06/12] Change to subtle 2.0.0-pre.0 --- Cargo.toml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4ca1999c1..f3a106979 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,7 @@ rand = { version = "0.5", default-features = false } byteorder = { version = "^1.2.3", default-features = false, features = ["i128"] } digest = { version = "0.8", default-features = false } clear_on_drop = "=0.2.3" -subtle = { version = "2", default-features = false } +subtle = { version = "2.0.0-pre.0", default-features = false } serde = { version = "1.0", optional = true } packed_simd = { version = "0.3.0", features = ["into_bits"], optional = true } @@ -54,7 +54,7 @@ rand = { version = "0.5", default-features = false } byteorder = { version = "^1.2.3", default-features = false, features = ["i128"] } digest = { version = "0.8", default-features = false } clear_on_drop = "=0.2.3" -subtle = { version = "2", default-features = false } +subtle = { version = "2.0.0-pre.0", default-features = false } serde = { version = "1.0", optional = true } packed_simd = { version = "0.3.0", features = ["into_bits"], optional = true } @@ -79,5 +79,3 @@ avx2_backend = ["nightly", "u64_backend", "packed_simd"] # feature before the main-stage compilation. stage2_build = [] -[patch.crates-io] -subtle = { git = "https://github.com/dalek-cryptography/subtle", branch = "fix-subtle-traits" } From 677d542ff1ccea8af11debc0ef6138b6b10ffbe1 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 5 Nov 2018 16:15:07 -0800 Subject: [PATCH 07/12] Bump version to 1.0.0-pre.0 --- Cargo.toml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f3a106979..dbf4a28f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "curve25519-dalek" -version = "0.21.0" +version = "1.0.0-pre.0" authors = ["Isis Lovecruft ", "Henry de Valence "] readme = "README.md" diff --git a/README.md b/README.md index 745c52a6a..52a54811e 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ make doc-internal To import `curve25519-dalek`, add the following to the dependencies section of your project's `Cargo.toml`: ```toml -curve25519-dalek = "0.21" +curve25519-dalek = "1.0.0-pre.0" ``` Then import the crate as: ```rust,no_run From 3e91ba023b6b836b5cbbb10419252314e8937c73 Mon Sep 17 00:00:00 2001 From: Jeff Burdges Date: Wed, 26 Dec 2018 15:04:41 +0100 Subject: [PATCH 08/12] Permit keying HashMap and BTreeMap off CompressedRistretto --- src/ristretto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ristretto.rs b/src/ristretto.rs index e6375edae..2c3882bce 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -199,7 +199,7 @@ use traits::{MultiscalarMul, VartimeMultiscalarMul}; /// /// The Ristretto encoding is canonical, so two points are equal if and /// only if their encodings are equal. -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct CompressedRistretto(pub [u8; 32]); impl CompressedRistretto { From 459d6d8abdfe7906f6dce42d316a9261e1afc3b1 Mon Sep 17 00:00:00 2001 From: Jeff Burdges Date: Wed, 26 Dec 2018 16:44:47 +0100 Subject: [PATCH 09/12] Add RistrettoBoth to keep compressed and uncompressed formats together There are enough things that require both together that this looks convenient. There is minimal cost to calling as_point when you want a poiont form so I have not added arithmatic traits, but maybe they'd help in places. These should only exist for data being comitted to transcripts, like compressed points, so I implemented many traits using only the compressed form, not the point form with extra constant time assurances. --- src/ristretto.rs | 158 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/src/ristretto.rs b/src/ristretto.rs index 2c3882bce..ee68e0722 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -287,6 +287,15 @@ impl CompressedRistretto { return Some(RistrettoPoint(EdwardsPoint{X: x, Y: y, Z: one, T: t})); } } + + /// Decompress into the `RistrettoBoth` format that also retains the + /// compressed form. + pub fn decompress_to_both(&self) -> Option { + Some(RistrettoBoth { + compressed: self.clone(), + point: self.decompress() ?, + }) + } } impl Identity for CompressedRistretto { @@ -332,6 +341,15 @@ impl Serialize for CompressedRistretto { } } +#[cfg(feature = "serde")] +impl Serialize for RistrettoBoth { + fn serialize(&self, serializer: S) -> Result + where S: Serializer + { + serializer.serialize_bytes(self.compressed.as_bytes()) + } +} + #[cfg(feature = "serde")] impl<'de> Deserialize<'de> for RistrettoPoint { fn deserialize(deserializer: D) -> Result @@ -396,6 +414,40 @@ impl<'de> Deserialize<'de> for CompressedRistretto { } } +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for RistrettoBoth { + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de> + { + struct RistrettoPointVisitor; + + impl<'de> Visitor<'de> for RistrettoPointVisitor { + type Value = RistrettoPoint; + + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + formatter.write_str("a valid point in Ristretto format") + } + + fn visit_bytes(self, v: &[u8]) -> Result + where E: serde::de::Error + { + if v.len() == 32 { + let mut arr32 = [0u8; 32]; + arr32[0..32].copy_from_slice(v); + let compressed = CompressedRistretto(arr32); + let point = compressed.decompress() + .ok_or(serde::de::Error::custom("decompression failed")); + RistrettoBoth { compressed, point } + } else { + Err(serde::de::Error::invalid_length(v.len(), &self)) + } + } + } + + deserializer.deserialize_bytes(RistrettoPointVisitor) + } +} + // ------------------------------------------------------------------------ // Internal point representations // ------------------------------------------------------------------------ @@ -451,6 +503,15 @@ impl RistrettoPoint { CompressedRistretto(s.to_bytes()) } + /// Compress into the `RistrettoBoth` format that also retains the + /// uncompressed form. + pub fn compress_to_both(&self) -> Option { + Some(RistrettoBoth { + compressed: self.compress(), + point: self.clone(), + }) + } + /// Double-and-compress a batch of points. The Ristretto encoding /// is not batchable, since it requires an inverse square root. /// @@ -722,6 +783,103 @@ impl Default for RistrettoPoint { } } +// ------------------------------------------------------------------------ +// Convenience representations +// ------------------------------------------------------------------------ + +/// A `RistrettoBoth` contains both a `CompressedRistretto` as well as the +/// corresponding `RistrettoPoint`. It provides a convenient middle ground +/// for protocols that both hash compressed points to derive scalars for +/// use with uncompressed points. +/// +#[derive(Debug, Copy, Clone)] +pub struct RistrettoBoth { + pub(crate) compressed: CompressedRistretto, + pub(crate) point: RistrettoPoint, +} + +impl RistrettoBoth { + /// Reference to `CompressedRistretto` form. + fn as_compressed(&self) -> &CompressedRistretto { &self.compressed } + + /// Reference to `RistrettoPoint` form. + fn as_point(&self) -> &RistrettoPoint { &self.point } +} + +impl AsRef for RistrettoBoth { + fn as_ref(&self) -> &CompressedRistretto { &self.compressed } +} + +impl AsRef for RistrettoBoth { + fn as_ref(&self) -> &RistrettoPoint { &self.point } +} + +impl Identity for RistrettoBoth { + fn identity() -> RistrettoBoth { + RistrettoBoth { + compressed: CompressedRistretto::identity(), + point: RistrettoPoint::identity(), + } + } +} + +impl Default for RistrettoBoth { + fn default() -> RistrettoBoth { + RistrettoBoth::identity() + } +} + +impl ::core::hash::Hash for RistrettoBoth { + fn hash(&self, state: &mut H) { + self.compressed.hash(state); + } +} + +impl PartialEq for RistrettoBoth { + fn eq(&self, other: &Self) -> bool { + self.compressed.eq(&other.compressed) + } + + // fn ne(&self, other: &Rhs) -> bool { + // self.compressed.ne(&other.compressed) + // } +} + +impl Eq for RistrettoBoth {} + +impl PartialOrd for RistrettoBoth { + fn partial_cmp(&self, other: &RistrettoBoth) -> Option<::core::cmp::Ordering> { + self.compressed.partial_cmp(&other.compressed) + } + + // fn lt(&self, other: &Rhs) -> bool { + // self.compressed.lt(&other.compressed) + // } + // fn le(&self, other: &Rhs) -> bool { + // self.compressed.le(&other.compressed) + // } + // fn gt(&self, other: &Rhs) -> bool { + // self.compressed.gt(&other.compressed) + // } + // fn ge(&self, other: &Rhs) -> bool { + // self.compressed.ge(&other.compressed) + // } +} + +impl Ord for RistrettoBoth { + fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { + self.compressed.cmp(&other.compressed) + } + + // fn max(self, other: Self) -> Self { + // self.compressed.max(other.compressed) + // } + // fn min(self, other: Self) -> Self { + // self.compressed.min(other.compressed) + // } +} + + // ------------------------------------------------------------------------ // Equality // ------------------------------------------------------------------------ From 4139765734fc7e48f4b57ede0ddb8d344c8c12b8 Mon Sep 17 00:00:00 2001 From: Jeff Burdges Date: Wed, 26 Dec 2018 17:05:49 +0100 Subject: [PATCH 10/12] Borrow traits These should make serde and the optional multiscalar stuff play nicely, but maybe another route would work better. --- src/ristretto.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/ristretto.rs b/src/ristretto.rs index ee68e0722..cff370a4d 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -810,10 +810,26 @@ impl AsRef for RistrettoBoth { fn as_ref(&self) -> &CompressedRistretto { &self.compressed } } +impl Borrow for RistrettoBoth { + fn borrow(&self) -> &CompressedRistretto { &self.compressed } +} + +impl<'a> Borrow for &'a RistrettoBoth { + fn borrow(&self) -> &CompressedRistretto { &self.compressed } +} + impl AsRef for RistrettoBoth { fn as_ref(&self) -> &RistrettoPoint { &self.point } } +impl Borrow for RistrettoBoth { + fn borrow(&self) -> &RistrettoPoint { &self.point } +} + +impl<'a> Borrow for &'a RistrettoBoth { + fn borrow(&self) -> &RistrettoPoint { &self.point } +} + impl Identity for RistrettoBoth { fn identity() -> RistrettoBoth { RistrettoBoth { From 3ab9abaeab8f8e08a16bdccaa2358c4ea429db9e Mon Sep 17 00:00:00 2001 From: Jeff Burdges Date: Wed, 26 Dec 2018 22:10:54 +0100 Subject: [PATCH 11/12] whitespace and two pubs --- src/ristretto.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ristretto.rs b/src/ristretto.rs index cff370a4d..da05895e6 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -504,7 +504,7 @@ impl RistrettoPoint { } /// Compress into the `RistrettoBoth` format that also retains the - /// uncompressed form. + /// uncompressed form. pub fn compress_to_both(&self) -> Option { Some(RistrettoBoth { compressed: self.compress(), @@ -800,34 +800,34 @@ pub struct RistrettoBoth { impl RistrettoBoth { /// Reference to `CompressedRistretto` form. - fn as_compressed(&self) -> &CompressedRistretto { &self.compressed } + pub fn as_compressed(&self) -> &CompressedRistretto { &self.compressed } /// Reference to `RistrettoPoint` form. - fn as_point(&self) -> &RistrettoPoint { &self.point } + pub fn as_point(&self) -> &RistrettoPoint { &self.point } } impl AsRef for RistrettoBoth { - fn as_ref(&self) -> &CompressedRistretto { &self.compressed } + fn as_ref(&self) -> &CompressedRistretto { &self.compressed } } impl Borrow for RistrettoBoth { - fn borrow(&self) -> &CompressedRistretto { &self.compressed } + fn borrow(&self) -> &CompressedRistretto { &self.compressed } } impl<'a> Borrow for &'a RistrettoBoth { - fn borrow(&self) -> &CompressedRistretto { &self.compressed } + fn borrow(&self) -> &CompressedRistretto { &self.compressed } } impl AsRef for RistrettoBoth { - fn as_ref(&self) -> &RistrettoPoint { &self.point } + fn as_ref(&self) -> &RistrettoPoint { &self.point } } impl Borrow for RistrettoBoth { - fn borrow(&self) -> &RistrettoPoint { &self.point } + fn borrow(&self) -> &RistrettoPoint { &self.point } } impl<'a> Borrow for &'a RistrettoBoth { - fn borrow(&self) -> &RistrettoPoint { &self.point } + fn borrow(&self) -> &RistrettoPoint { &self.point } } impl Identity for RistrettoBoth { @@ -852,9 +852,9 @@ impl ::core::hash::Hash for RistrettoBoth { } impl PartialEq for RistrettoBoth { - fn eq(&self, other: &Self) -> bool { + fn eq(&self, other: &Self) -> bool { self.compressed.eq(&other.compressed) - } + } // fn ne(&self, other: &Rhs) -> bool { // self.compressed.ne(&other.compressed) @@ -864,7 +864,7 @@ impl PartialEq for RistrettoBoth { impl Eq for RistrettoBoth {} impl PartialOrd for RistrettoBoth { - fn partial_cmp(&self, other: &RistrettoBoth) -> Option<::core::cmp::Ordering> { + fn partial_cmp(&self, other: &RistrettoBoth) -> Option<::core::cmp::Ordering> { self.compressed.partial_cmp(&other.compressed) } From 77ae141db496bb6c9beae71e2b5e08f8554595b5 Mon Sep 17 00:00:00 2001 From: Jeff Burdges Date: Wed, 26 Dec 2018 22:17:22 +0100 Subject: [PATCH 12/12] Oops, this one should be infaliblew We could add TryFrom going the other way, except no error type is defined. --- src/ristretto.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/ristretto.rs b/src/ristretto.rs index da05895e6..4b0754281 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -505,12 +505,12 @@ impl RistrettoPoint { /// Compress into the `RistrettoBoth` format that also retains the /// uncompressed form. - pub fn compress_to_both(&self) -> Option { - Some(RistrettoBoth { - compressed: self.compress(), - point: self.clone(), - }) - } + pub fn compress_to_both(&self) -> RistrettoBoth { + RistrettoBoth { + compressed: self.compress(), + point: self.clone(), + } + } /// Double-and-compress a batch of points. The Ristretto encoding /// is not batchable, since it requires an inverse square root. @@ -806,6 +806,15 @@ impl RistrettoBoth { pub fn as_point(&self) -> &RistrettoPoint { &self.point } } +impl From for RistrettoBoth { + pub fn from(self) -> Option { + Some(RistrettoBoth { + compressed: self.compress(), + point: self, + }) + } +} + impl AsRef for RistrettoBoth { fn as_ref(&self) -> &CompressedRistretto { &self.compressed } }