diff --git a/Cargo.toml b/Cargo.toml index e5570e9..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" @@ -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/README.md b/README.md index f7ad37d..1ac09aa 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# 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://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) + src="https://doc.dalek.rs/assets/dalek-logo-clear.png"/> **A pure-Rust implementation of group operations on Ristretto and Curve25519.** @@ -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/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/backend/avx2/edwards.rs b/src/backend/avx2/edwards.rs index 1a58133..575619a 100644 --- a/src/backend/avx2/edwards.rs +++ b/src/backend/avx2/edwards.rs @@ -13,12 +13,14 @@ // 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}; use subtle::ConditionallyAssignable; +use subtle::Choice; use edwards; use scalar::Scalar; @@ -51,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); } } @@ -114,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); } } @@ -236,7 +238,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(); @@ -395,7 +397,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]); } @@ -419,7 +421,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]); @@ -447,38 +449,25 @@ 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 } } -/// 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_mul(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 +478,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. @@ -519,7 +508,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> edwards::Edwards 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 @@ -562,7 +551,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(); @@ -606,27 +595,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_mul(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(); @@ -873,7 +856,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]); @@ -885,7 +868,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 @@ -899,7 +882,7 @@ mod test { } #[test] - fn multiscalar_mult_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]); @@ -909,7 +892,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()); @@ -919,7 +902,7 @@ mod test { use super::*; #[test] - fn multiscalar_mult_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]); @@ -929,7 +912,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()); @@ -989,7 +972,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]); @@ -1014,7 +997,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(); @@ -1022,7 +1005,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 { @@ -1030,18 +1013,18 @@ 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); 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] - 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(); @@ -1049,7 +1032,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/backend/avx2/field.rs b/src/backend/avx2/field.rs index 3f69aad..d8b7883 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}; @@ -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/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 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/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/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/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/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/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)) diff --git a/src/constants.rs b/src/constants.rs index f353d8d..c8cf1c1 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,36 +123,12 @@ 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()); } } - /// 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() { @@ -165,7 +141,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 4a4914e..33d299f 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 //! @@ -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. //! @@ -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 @@ -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 @@ -96,12 +99,12 @@ 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; use subtle::ConditionallyNegatable; -// XXX subtle::Equal -use subtle::Equal; +use subtle::Choice; +use subtle::ConstantTimeEq; use constants; @@ -160,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 }) @@ -278,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); @@ -290,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 } } @@ -370,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) } } @@ -501,7 +504,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() } @@ -530,25 +533,57 @@ 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. /// -/// # Input +/// It is an error to call this function with two iterators of different lengths. /// -/// 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 /// +/// The trait bound aims for maximum flexibility: the inputs must be +/// convertable to iterators (`I: IntoIter`), and the iterator's items +/// must be `Borrow` (or `Borrow`), to allow +/// iterators returning either `Scalar`s or `&Scalar`s. +/// +/// ``` +/// use curve25519_dalek::{constants, edwards}; +/// use curve25519_dalek::scalar::Scalar; +/// +/// // Some scalars +/// let a = Scalar::from_u64(87329482); +/// let b = Scalar::from_u64(37264829); +/// let c = Scalar::from_u64(98098098); +/// +/// // Some points +/// let P = constants::ED25519_BASEPOINT_POINT; +/// let Q = P + P; +/// let R = P + Q; +/// +/// // A1 = a*P + b*Q + c*R +/// let abc = [a,b,c]; +/// let A1 = edwards::multiscalar_mul(&abc, &[P,Q,R]); +/// // Note: (&abc).into_iter(): Iterator +/// +/// // A2 = (-a)*P + (-b)*Q + (-c)*R +/// let minus_abc = abc.iter().map(|x| -x); +/// let A2 = edwards::multiscalar_mul(minus_abc, &[P,Q,R]); +/// // Note: minus_abc.into_iter(): Iterator +/// +/// assert_eq!(A1.compress(), (-A2).compress()); +/// ``` // 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_mul(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")))] { 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"))))] { @@ -557,7 +592,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); @@ -568,7 +603,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. @@ -598,7 +633,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(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 @@ -658,7 +693,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(); @@ -698,7 +733,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 } @@ -715,12 +750,12 @@ impl EdwardsBasepointTable { impl EdwardsPoint { /// Multiply by the cofactor: return \\([8]P\\). - pub fn mult_by_cofactor(&self) -> EdwardsPoint { - self.mult_by_pow_2(3) + pub fn mul_by_cofactor(&self) -> EdwardsPoint { + 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(); @@ -755,7 +790,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 @@ -789,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 // ------------------------------------------------------------------------ @@ -867,35 +877,72 @@ 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_mul` 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_mul(&abc, &[P,Q,R]); + /// // Note: (&abc).into_iter(): Iterator + /// + /// // A2 = (-a)*P + (-b)*Q + (-c)*R + /// let minus_abc = abc.iter().map(|x| -x); + /// let A2 = edwards::vartime::multiscalar_mul(minus_abc, &[P,Q,R]); + /// // Note: minus_abc.into_iter(): Iterator + /// + /// assert_eq!(A1.compress(), (-A2).compress()); + /// ``` + // 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_mul(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")))] { 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"))))] { //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(); @@ -921,7 +968,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, @@ -930,7 +977,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"))))] { @@ -1130,7 +1177,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 @@ -1172,9 +1219,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); } @@ -1203,10 +1250,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); } @@ -1217,9 +1264,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); } @@ -1258,7 +1305,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; @@ -1280,19 +1327,19 @@ 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_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); } #[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] ); @@ -1300,13 +1347,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] ); @@ -1370,7 +1417,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); } @@ -1430,10 +1477,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] @@ -1445,7 +1492,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(); @@ -1453,7 +1500,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 { @@ -1462,14 +1509,14 @@ 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] #[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(); @@ -1483,7 +1530,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/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/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: diff --git a/src/montgomery.rs b/src/montgomery.rs index 0c953bc..81337c5 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -50,18 +50,17 @@ 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; -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 +68,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 +79,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 +156,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 +243,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() } @@ -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/ristretto.rs b/src/ristretto.rs index 6c30121..0426766 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 //! @@ -78,10 +79,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 @@ -129,7 +130,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 +139,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 { @@ -463,24 +466,24 @@ 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; use digest::Digest; -use generic_array::typenum::U32; +use generic_array::typenum::U64; 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; -use subtle::Equal; +use subtle::ConstantTimeEq; +use subtle::Choice; use edwards; use edwards::EdwardsPoint; @@ -536,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; } @@ -563,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})); @@ -813,7 +816,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(); @@ -837,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); @@ -868,27 +871,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 /// @@ -896,18 +912,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); @@ -920,13 +936,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 } } @@ -942,17 +969,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; @@ -1057,19 +1085,52 @@ 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. /// -/// # 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_mul(&abc, &[P,Q,R]); +/// // Note: (&abc).into_iter(): Iterator +/// +/// // A2 = (-a)*P + (-b)*Q + (-c)*R +/// let minus_abc = abc.iter().map(|x| -x); +/// let A2 = ristretto::multiscalar_mul(minus_abc, &[P,Q,R]); +/// // Note: minus_abc.into_iter(): Iterator +/// +/// assert_eq!(A1.compress(), (-A2).compress()); +/// ``` #[cfg(any(feature = "alloc", feature = "std"))] -pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> RistrettoPoint - where I: IntoIterator, - J: IntoIterator, +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.0); - RistrettoPoint(edwards::multiscalar_mult(scalars, extended_points)) + let extended_points = points.into_iter().map(|P| P.borrow().0); + RistrettoPoint(edwards::multiscalar_mul(scalars, extended_points)) } /// A precomputed table of multiples of a basepoint, used to accelerate @@ -1110,7 +1171,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 /// @@ -1118,24 +1179,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); @@ -1169,23 +1232,58 @@ 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_mul` 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_mul(&abc, &[P,Q,R]); + /// // Note: (&abc).into_iter(): Iterator + /// + /// // A2 = (-a)*P + (-b)*Q + (-c)*R + /// let minus_abc = abc.iter().map(|x| -x); + /// let A2 = ristretto::vartime::multiscalar_mul(minus_abc, &[P,Q,R]); + /// // Note: minus_abc.into_iter(): Iterator + /// + /// assert_eq!(A1.compress(), (-A2).compress()); + /// ``` #[cfg(any(feature = "alloc", feature = "std"))] - pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> RistrettoPoint - where I: IntoIterator, - J: IntoIterator + 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.0); - RistrettoPoint(edwards::vartime::multiscalar_mult(scalars, extended_points)) + let extended_points = points.into_iter().map(|P| P.borrow().0); + RistrettoPoint(edwards::vartime::multiscalar_mul(scalars, extended_points)) } } @@ -1257,7 +1355,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()); } @@ -1357,7 +1455,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]); } } diff --git a/src/scalar.rs b/src/scalar.rs index ae17169..db17193 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); } } } @@ -433,6 +393,95 @@ 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 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]); + } + + // 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() { + 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]; @@ -535,6 +584,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); @@ -571,13 +621,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); @@ -626,7 +674,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() } } @@ -662,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{ @@ -775,7 +810,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]); @@ -927,6 +962,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"))] 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 } }