diff --git a/.travis.yml b/.travis.yml index 6a3e92b..cffc1c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,3 +42,9 @@ matrix: script: - cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS + +notifications: + slack: + rooms: + - dalek-cryptography:Xxv9WotKYWdSoKlgKNqXiHoD#dalek-bots + - dalek-cryptography:Xxv9WotKYWdSoKlgKNqXiHoD#curve25519-dalek diff --git a/Cargo.toml b/Cargo.toml index 470d7e5..aa3fd59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "curve25519-dalek" -version = "0.14.0" +version = "0.14.1" authors = ["Isis Lovecruft ", "Henry de Valence "] readme = "README.md" @@ -30,38 +30,42 @@ optional = true [dependencies.rand] optional = true -version = "0.3" +version = "0.4" [dependencies.digest] -version = "0.6" +version = "0.7" [dependencies.subtle] version = "^0.3" default-features = false +[dependencies.clear_on_drop] +version = "=0.2.3" + [dependencies.generic-array] # same version that digest depends on -version = "^0.8" +version = "0.9" [dev-dependencies.sha2] -version = "0.6" +version = "0.7" [dev-dependencies.serde_cbor] version = "0.6" [build-dependencies] subtle = "^0.3" -rand = "0.3" -generic-array = "^0.8" -digest = "0.6" +rand = "0.4" +generic-array = "0.9" +digest = "0.7" arrayref = "0.3.4" +clear_on_drop = "=0.2.3" [build-dependencies.serde] version = "1.0" optional = true [features] -nightly = ["radix_51", "subtle/nightly"] +nightly = ["radix_51", "subtle/nightly", "clear_on_drop/nightly"] default = ["std"] std = ["rand", "subtle/std"] alloc = [] diff --git a/build.rs b/build.rs index 13d9715..28044c3 100644 --- a/build.rs +++ b/build.rs @@ -8,6 +8,7 @@ extern crate subtle; extern crate rand; extern crate digest; extern crate generic_array; +extern crate clear_on_drop; use std::env; use std::fs::File; @@ -68,6 +69,7 @@ use backend::u32::field::FieldElement32; use edwards::EdwardsBasepointTable; +use curve_models::window::LookupTable; use curve_models::AffineNielsPoint; /// Table containing precomputed multiples of the basepoint `B = (x,4/5)`. diff --git a/src/curve_models/mod.rs b/src/curve_models/mod.rs index 19fc07e..b514405 100644 --- a/src/curve_models/mod.rs +++ b/src/curve_models/mod.rs @@ -134,6 +134,8 @@ use edwards::ExtendedPoint; use subtle::ConditionallyAssignable; use traits::ValidityCheck; +pub mod window; + // ------------------------------------------------------------------------ // Internal point representations // ------------------------------------------------------------------------ @@ -211,6 +213,12 @@ impl Identity for ProjectivePoint { } } +impl Default for ProjectivePoint { + fn default() -> ProjectivePoint { + ProjectivePoint::identity() + } +} + impl Identity for ProjectiveNielsPoint { fn identity() -> ProjectiveNielsPoint { ProjectiveNielsPoint{ @@ -222,6 +230,12 @@ impl Identity for ProjectiveNielsPoint { } } +impl Default for ProjectiveNielsPoint { + fn default() -> ProjectiveNielsPoint { + ProjectiveNielsPoint::identity() + } +} + impl Identity for AffineNielsPoint { fn identity() -> AffineNielsPoint { AffineNielsPoint{ @@ -232,6 +246,12 @@ impl Identity for AffineNielsPoint { } } +impl Default for AffineNielsPoint { + fn default() -> AffineNielsPoint { + AffineNielsPoint::identity() + } +} + // ------------------------------------------------------------------------ // Validity checks (for debugging, not CT) // ------------------------------------------------------------------------ diff --git a/src/curve_models/window.rs b/src/curve_models/window.rs new file mode 100644 index 0000000..6a0091e --- /dev/null +++ b/src/curve_models/window.rs @@ -0,0 +1,122 @@ +// -*- mode: rust; -*- +// +// This file is part of curve25519-dalek. +// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// See LICENSE for licensing information. +// +// Authors: +// - Isis Agora Lovecruft +// - Henry de Valence + +//! Code for fixed- and sliding-window functionality + +#![allow(non_snake_case)] + +use core::fmt::Debug; + +use subtle; +use subtle::ConditionallyNegatable; +use subtle::ConditionallyAssignable; + +use traits::Identity; + +/// A lookup table of precomputed multiples of a point \\(P\\), used to +/// compute \\( xP \\) for \\( -8 \leq x \leq 8 \\). +/// +/// The computation of \\( xP \\) is done in constant time by the `select` function. +/// +/// Since `LookupTable` does not implement `Index`, it's more difficult +/// to accidentally use the table directly. Unfortunately the table is +/// only `pub(crate)` so that we can write hardcoded constants, so it's +/// still technically possible. It would be nice to prevent direct +/// access to the table. +/// +/// XXX make this generic with respect to table size +#[derive(Copy, Clone)] +pub struct LookupTable(pub(crate) [T; 8]); + +use clear_on_drop::clear::ZeroSafe; + +/// This type isn't actually zeroable (all zero bytes are not valid +/// points), but we want to be able to use `clear_on_drop` to erase slices +/// of `LookupTable`. +/// +/// Since the `ZeroSafe` trait is only used by `clear_on_drop`, the only +/// situation where this would be a problem is if code attempted to use +/// a `ClearOnDrop` to erase a `LookupTable` and then used the table +/// afterwards. +/// +/// Normally this is not a problem, since the table's storage is usually +/// dropped too. +/// +/// XXX is this a good compromise? +unsafe impl ZeroSafe for LookupTable {} + +impl LookupTable +where T: Identity + ConditionallyAssignable + ConditionallyNegatable +{ + /// Given \\(-8 \leq x \leq 8\\), return \\(xP\\) in constant time. + pub fn select(&self, x: i8) -> T { + debug_assert!(x >= -8); debug_assert!(x <= 8); + + // Compute xabs = |x| + let xmask = x >> 7; + let xabs = (x + xmask) ^ xmask; + + // Set t = 0 * P = identity + 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)); + } + // Now t == |x| * P. + + let neg_mask = (xmask & 1) as u8; + t.conditional_negate(neg_mask); + // Now t == x * P. + + t + } +} + +impl Default for LookupTable { + fn default() -> LookupTable { + LookupTable([T::default(); 8]) + } +} + +impl Debug for LookupTable { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "LookupTable({:?})", self.0) + } +} + +use edwards::ExtendedPoint; +use curve_models::ProjectiveNielsPoint; +use curve_models::AffineNielsPoint; + +impl<'a> From<&'a ExtendedPoint> for LookupTable { + fn from(P: &'a ExtendedPoint) -> Self { + let mut points = [P.to_projective_niels(); 8]; + for j in 0..7 { + points[j+1] = (P + &points[j]) + .to_extended() + .to_projective_niels(); + } + LookupTable(points) + } +} + +impl<'a> From<&'a ExtendedPoint> for LookupTable { + fn from(P: &'a ExtendedPoint) -> Self { + let mut points = [P.to_affine_niels(); 8]; + // XXX batch inversion would be good if perf mattered here + for j in 0..7 { + points[j+1] = (P + &points[j]) + .to_extended() + .to_affine_niels() + } + LookupTable(points) + } +} diff --git a/src/edwards.rs b/src/edwards.rs index f8101df..1f00d9b 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -43,11 +43,11 @@ use curve_models::CompletedPoint; use curve_models::AffineNielsPoint; use curve_models::ProjectiveNielsPoint; +use curve_models::window::LookupTable; + use traits::{Identity, IsIdentity}; use traits::ValidityCheck; -use traits::select_precomputed_point; - // ------------------------------------------------------------------------ // Compressed points // ------------------------------------------------------------------------ @@ -235,7 +235,7 @@ impl Equal for ExtendedPoint { // ------------------------------------------------------------------------ impl ExtendedPoint { - /// Convert to a `ProjectiveNielsPoint` + /// Convert to a ProjectiveNielsPoint pub(crate) fn to_projective_niels(&self) -> ProjectiveNielsPoint { ProjectiveNielsPoint{ Y_plus_X: &self.Y + &self.X, @@ -433,12 +433,7 @@ impl<'a, 'b> Mul<&'b Scalar> for &'a ExtendedPoint { /// `EdwardsBasepointTable` is approximately 4x faster. fn mul(self, scalar: &'b Scalar) -> ExtendedPoint { // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] - let P = self.to_projective_niels(); - let mut lookup_table: [ProjectiveNielsPoint; 8] = [P; 8]; - for i in 0..7 { - lookup_table[i+1] = (self + &lookup_table[i]) - .to_extended().to_projective_niels(); - } + let lookup_table = LookupTable::::from(self); // Setting s = scalar, compute // @@ -456,13 +451,12 @@ impl<'a, 'b> Mul<&'b Scalar> for &'a ExtendedPoint { // We sum right-to-left. let mut Q = ExtendedPoint::identity(); for i in (0..64).rev() { - // Q = 16*Q + // Q <-- 16*Q Q = Q.mult_by_pow_2(4); - // R = s_i * Q - let R = select_precomputed_point(scalar_digits[i], &lookup_table); - // Q = Q + R - Q = (&Q + &R).to_extended(); + // Q <-- Q + P * s_i + Q = (&Q + &lookup_table.select(scalar_digits[i])).to_extended() } + Q } } @@ -502,25 +496,28 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint J: IntoIterator { //assert_eq!(scalars.len(), points.len()); + + use clear_on_drop::ClearOnDrop; - let lookup_tables: Vec<_> = points.into_iter() - .map(|P_i| { - // Construct a lookup table of [P_i,2*P_i,3*P_i,4*P_i,5*P_i,6*P_i,7*P_i] - let mut lookup_table = [P_i.to_projective_niels(); 8]; - for j in 0..7 { - lookup_table[j+1] = (P_i + &lookup_table[j]) - .to_extended().to_projective_niels(); - } - lookup_table - }).collect(); + let lookup_tables_vec: Vec<_> = points.into_iter() + .map(|P| LookupTable::::from(P) ) + .collect(); + + let lookup_tables = ClearOnDrop::new(lookup_tables_vec); // Setting s_i = i-th scalar, compute // // s_i = s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63, // // with `-8 ≤ s_{i,j} < 8` for `0 ≤ j < 63` and `-8 ≤ s_{i,63} ≤ 8`. - let scalar_digits_list: Vec<_> = scalars.into_iter() - .map(|c| c.to_radix_16()).collect(); + let scalar_digits_vec: Vec<_> = scalars.into_iter() + .map(|c| c.to_radix_16()) + .collect(); + + // This above puts the scalar digits into a heap-allocated Vec. + // To ensure that these are erased, pass ownership of the Vec into a + // ClearOnDrop wrapper. + let scalar_digits = ClearOnDrop::new(scalar_digits_vec); // Compute s_1*P_1 + ... + s_n*P_n: since // @@ -545,10 +542,10 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint // 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); - let it = scalar_digits_list.iter().zip(lookup_tables.iter()); + let it = scalar_digits.iter().zip(lookup_tables.iter()); for (s_i, lookup_table_i) in it { // R_i = s_{i,j} * P_i - let R_i = select_precomputed_point(s_i[j], lookup_table_i); + let R_i = lookup_table_i.select(s_i[j]); // Q = Q + R_i Q = (&Q + &R_i).to_extended(); } @@ -563,7 +560,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint /// The basepoint tables are reasonably large (30KB), so they should /// probably be boxed. #[derive(Clone)] -pub struct EdwardsBasepointTable(pub(crate) [[AffineNielsPoint; 8]; 32]); +pub struct EdwardsBasepointTable(pub(crate) [LookupTable; 32]); impl EdwardsBasepointTable { /// The computation uses Pippeneger's algorithm, as described on @@ -572,7 +569,7 @@ impl EdwardsBasepointTable { /// $$ /// a = a\_0 + a\_1 16\^1 + \cdots + a\_{63} 16\^{63}, /// $$ - /// with \\(-8 \leq a_i < 8\\). Then + /// with \\(-8 \leq a_i < 8\\), \\(-8 \leq a\_{63} \leq 8\\). Then /// $$ /// a B = a\_0 B + a\_1 16\^1 B + \cdots + a\_{63} 16\^{63} B. /// $$ @@ -585,25 +582,28 @@ impl EdwardsBasepointTable { /// + 16(a\_1 16\^0 B +& a\_3 16\^2 B + \cdots + a\_{63} 16\^{62} B). \\\\ /// \end{aligned} /// $$ - /// We then use the `select_precomputed_point` function, which - /// takes \\(-8 \leq x < 8\\) and \\([16\^{2i} B, \ldots, 8\cdot16\^{2i} B]\\), - /// and returns \\(x \cdot 16\^{2i} \cdot B\\) in constant time. + /// For each \\(i = 0 \ldots 31\\), we create a lookup table of + /// $$ + /// [16\^{2i} B, \ldots, 8\cdot16\^{2i} B], + /// $$ + /// and use it to select \\( x \cdot 16\^{2i} \cdot B \\) in constant time. /// /// The radix-\\(16\\) representation requires that the scalar is bounded /// by \\(2\^{255}\\), which is always the case. fn basepoint_mul(&self, scalar: &Scalar) -> ExtendedPoint { let a = scalar.to_radix_16(); + let tables = &self.0; let mut P = ExtendedPoint::identity(); for i in (0..64).filter(|x| x % 2 == 1) { - P = (&P + &select_precomputed_point(a[i], &self.0[i/2])).to_extended(); + P = (&P + &tables[i/2].select(a[i])).to_extended(); } P = P.mult_by_pow_2(4); for i in (0..64).filter(|x| x % 2 == 0) { - P = (&P + &select_precomputed_point(a[i], &self.0[i/2])).to_extended(); + P = (&P + &tables[i/2].select(a[i])).to_extended(); } P @@ -634,29 +634,24 @@ impl<'a, 'b> Mul<&'a EdwardsBasepointTable> for &'b Scalar { impl EdwardsBasepointTable { /// Create a table of precomputed multiples of `basepoint`. pub fn create(basepoint: &ExtendedPoint) -> EdwardsBasepointTable { - // Create the table storage - // XXX can we skip the initialization without too much unsafety? - // stick 30K on the stack and call it a day. - let mut table = EdwardsBasepointTable([[AffineNielsPoint::identity(); 8]; 32]); + // XXX use init_with + let mut table = EdwardsBasepointTable([LookupTable::default(); 32]); let mut P = *basepoint; for i in 0..32 { // P = (16^2)^i * B - let mut jP = P.to_affine_niels(); - for j in 1..9 { - // table[i][j-1] is supposed to be j*(16^2)^i*B - table.0[i][j-1] = jP; - jP = (&P + &jP).to_extended().to_affine_niels(); - } + table.0[i] = LookupTable::from(&P); P = P.mult_by_pow_2(8); } table } /// Get the basepoint for this table as an `ExtendedPoint`. + /// + /// XXX maybe this would be better as a `From` impl pub fn basepoint(&self) -> ExtendedPoint { - // self.0[0][0] has 1*(16^2)^0*B, but as an `AffineNielsPoint` - // Add identity to convert to extended. - (&ExtendedPoint::identity() + &self.0[0][0]).to_extended() + // self.0[0].select(1) = 1*(16^2)^0*B + // but as an `AffineNielsPoint`, so add identity to convert to extended. + (&ExtendedPoint::identity() + &self.0[0].select(1)).to_extended() } } @@ -1268,7 +1263,9 @@ mod bench { #[bench] #[cfg(feature="precomputed_tables")] fn bench_select_precomputed_point(b: &mut Bencher) { - b.iter(|| select_precomputed_point(0, &constants::ED25519_BASEPOINT_TABLE.0[0])); + use test::black_box; + let table = &constants::ED25519_BASEPOINT_TABLE.0[0]; + b.iter(|| table.select(black_box(5)) ); } #[bench] diff --git a/src/lib.rs b/src/lib.rs index 2756376..51364f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,6 +47,8 @@ extern crate rand; #[cfg(feature = "alloc")] extern crate alloc; +extern crate clear_on_drop; + #[cfg(all(test, feature = "bench"))] extern crate test; diff --git a/src/traits.rs b/src/traits.rs index aaac49a..b0beeb9 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -10,11 +10,7 @@ //! Module for common traits. -use core::ops::Neg; - use subtle; -use subtle::ConditionallyAssignable; -use subtle::ConditionallyNegatable; // ------------------------------------------------------------------------ // Public Traits @@ -55,33 +51,3 @@ pub(crate) trait ValidityCheck { /// Checks whether the point is on the curve. Not CT. fn is_valid(&self) -> bool; } - -// This isn't a trait, but it is fully generic... - -/// Given precomputed points `[P, 2P, 3P, ..., 8P]`, as well as `-8 ≤ -/// x ≤ 8`, compute `x * B` in constant time, i.e., without branching -/// on x or using it as an array index. -pub(crate) fn select_precomputed_point(x: i8, points: &[T; 8]) -> T - where T: Identity + ConditionallyAssignable, for<'a> &'a T: Neg -{ - debug_assert!(x >= -8); debug_assert!(x <= 8); - - // Compute xabs = |x| - let xmask = x >> 7; - let xabs = (x + xmask) ^ xmask; - - // Set t = 0 * P = identity - 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(&points[j-1], - subtle::bytes_equal(xabs as u8, j as u8)); - } - // Now t == |x| * P. - - let neg_mask = (xmask & 1) as u8; - t.conditional_negate(neg_mask); - // Now t == x * P. - - t -}