From ac739a3edddb60d8c6fc2e71cbafdf7cdb011092 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 26 Mar 2018 15:01:48 -0700 Subject: [PATCH] Split out constant-time variable-base scalar mul. The serial (`u32`/`u64`) implementations use a multiple curve models, passing between extended and projective coordinates when performing addition and doubling (respectively). But the AVX2 backend doesn't, so in order to write a single scalar mult implementation, we have to either abstract over the curve models or have two implementations. A generic solution is possible but extremely unreadable: the scalar mul implementation would be parameterized over the point types used by the serial implementations, with many where clauses describing how the types relate. The AVX2 types could then be substituted in the appropriate places. Instead we just duplicate the code into the `avx2` backend. --- src/backend/avx2/edwards.rs | 114 ------------------- src/backend/avx2/mod.rs | 2 + src/backend/avx2/scalar_mul/mod.rs | 11 ++ src/backend/avx2/scalar_mul/variable_base.rs | 34 ++++++ src/edwards.rs | 41 ++----- src/scalar_mul/mod.rs | 2 + src/scalar_mul/variable_base.rs | 32 ++++++ 7 files changed, 90 insertions(+), 146 deletions(-) create mode 100644 src/backend/avx2/scalar_mul/mod.rs create mode 100644 src/backend/avx2/scalar_mul/variable_base.rs create mode 100644 src/scalar_mul/variable_base.rs diff --git a/src/backend/avx2/edwards.rs b/src/backend/avx2/edwards.rs index 74b0797..5aad787 100644 --- a/src/backend/avx2/edwards.rs +++ b/src/backend/avx2/edwards.rs @@ -371,40 +371,6 @@ impl From for LookupTable { } } -impl<'a, 'b> Mul<&'b Scalar> for &'a ExtendedPoint { - type Output = ExtendedPoint; - /// Scalar multiplication: compute `scalar * self`. - /// - /// Uses a window of size 4. - fn mul(self, scalar: &'b Scalar) -> ExtendedPoint { - // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] - let lookup_table = LookupTable::::from(*self); - - // Setting s = scalar, compute - // - // s = s_0 + s_1*16^1 + ... + s_63*16^63, - // - // with `-8 ≤ s_i < 8` for `0 ≤ i < 63` and `-8 ≤ s_63 ≤ 8`. - let scalar_digits = scalar.to_radix_16(); - - // Compute s*P as - // - // s*P = P*(s_0 + s_1*16^1 + s_2*16^2 + ... + s_63*16^63) - // s*P = P*s_0 + P*s_1*16^1 + P*s_2*16^2 + ... + P*s_63*16^63 - // s*P = P*s_0 + 16*(P*s_1 + 16*(P*s_2 + 16*( ... + P*s_63)...)) - // - // We sum right-to-left. - let mut Q = ExtendedPoint::identity(); - for i in (0..64).rev() { - // Q = 16*Q - Q = Q.mul_by_pow_2(4); - // Q += P*s_i - Q = &Q + &lookup_table.select(scalar_digits[i]); - } - Q - } -} - #[derive(Clone)] pub struct EdwardsBasepointTable(pub [LookupTable; 32]); @@ -739,7 +705,6 @@ mod test { assert_eq!(R1.compress(), edwards::EdwardsPoint::identity().compress()); } - #[test] fn vector_addition_vs_serial_addition_vs_edwards_extendedpoint() { use constants; @@ -839,83 +804,4 @@ mod test { let P = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from_u64(8475983829); doubling_test_helper(P); } - - #[test] - fn identity_trait_vs_edwards_identity() { - let id1: edwards::EdwardsPoint = ExtendedPoint::identity().into(); - let id2: edwards::EdwardsPoint = edwards::EdwardsPoint::identity(); - assert_eq!(id1.compress(), id2.compress()); - } - - #[test] - fn neg_vs_edwards_neg() { - let B: ExtendedPoint = constants::ED25519_BASEPOINT_POINT.into(); - let Bneg = -&B; - assert_eq!(edwards::EdwardsPoint::from(Bneg).compress(), - (-&constants::ED25519_BASEPOINT_POINT).compress()); - } - - #[test] - 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]); - - let R1 = edwards::EdwardsPoint::from(&B * &s); - let R2 = &constants::ED25519_BASEPOINT_TABLE * &s; - - assert_eq!(R1.compress(), R2.compress()); - } - - #[test] - 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 - 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]); - - let P1 = &B * &s; - let P2 = &B_table * &s; - - assert_eq!(edwards::EdwardsPoint::from(P1).compress(), - edwards::EdwardsPoint::from(P2).compress()); - } - - #[test] - 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]); - - let P1 = &B * &s2; - let P2 = &B * &s1; - - let R = &(&P1 * &s1) + &(&P2 * &s2); - - let R_multiscalar = multiscalar_mul(&[s1, s2], &[P1.into(), P2.into()]); - - assert_eq!(edwards::EdwardsPoint::from(R).compress(), - R_multiscalar.compress()); - } - - mod vartime { - use super::*; - - #[test] - 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]); - - let P1 = &B * &s2; - let P2 = &B * &s1; - - let R = &(&P1 * &s1) + &(&P2 * &s2); - - let R_multiscalar = vartime::multiscalar_mul(&[s1, s2], &[P1.into(), P2.into()]); - - assert_eq!(edwards::EdwardsPoint::from(R).compress(), - R_multiscalar.compress()); - } - } } diff --git a/src/backend/avx2/mod.rs b/src/backend/avx2/mod.rs index ebae72f..bd3e860 100644 --- a/src/backend/avx2/mod.rs +++ b/src/backend/avx2/mod.rs @@ -479,3 +479,5 @@ pub(crate) mod field; pub(crate) mod edwards; pub(crate) mod constants; + +pub(crate) mod scalar_mul; diff --git a/src/backend/avx2/scalar_mul/mod.rs b/src/backend/avx2/scalar_mul/mod.rs new file mode 100644 index 0000000..54f5214 --- /dev/null +++ b/src/backend/avx2/scalar_mul/mod.rs @@ -0,0 +1,11 @@ +// -*- mode: rust; -*- +// +// This file is part of curve25519-dalek. +// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence +// See LICENSE for licensing information. +// +// Authors: +// - Isis Agora Lovecruft +// - Henry de Valence + +pub mod variable_base; diff --git a/src/backend/avx2/scalar_mul/variable_base.rs b/src/backend/avx2/scalar_mul/variable_base.rs new file mode 100644 index 0000000..e5261fb --- /dev/null +++ b/src/backend/avx2/scalar_mul/variable_base.rs @@ -0,0 +1,34 @@ +#![allow(non_snake_case)] + +use traits::Identity; +use scalar::Scalar; +use edwards::EdwardsPoint; +use backend::avx2::edwards::{ExtendedPoint, CachedPoint}; +use scalar_mul::window::LookupTable; + +/// Perform constant-time, variable-base scalar multiplication. +pub fn mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint { + // XXX combine these conversions + let avx2_point = ExtendedPoint::from(*point); + // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] + let lookup_table = LookupTable::::from(avx2_point); + // Setting s = scalar, compute + // + // s = s_0 + s_1*16^1 + ... + s_63*16^63, + // + // with `-8 ≤ s_i < 8` for `0 ≤ i < 63` and `-8 ≤ s_63 ≤ 8`. + let scalar_digits = scalar.to_radix_16(); + // Compute s*P as + // + // s*P = P*(s_0 + s_1*16^1 + s_2*16^2 + ... + s_63*16^63) + // s*P = P*s_0 + P*s_1*16^1 + P*s_2*16^2 + ... + P*s_63*16^63 + // s*P = P*s_0 + 16*(P*s_1 + 16*(P*s_2 + 16*( ... + P*s_63)...)) + // + // We sum right-to-left. + let mut Q = ExtendedPoint::identity(); + for i in (0..64).rev() { + Q = Q.mul_by_pow_2(4); + Q = &Q + &lookup_table.select(scalar_digits[i]); + } + Q.into() +} diff --git a/src/edwards.rs b/src/edwards.rs index 439d6e6..1e2f31d 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -477,39 +477,16 @@ impl<'a, 'b> Mul<&'b Scalar> for &'a EdwardsPoint { /// `EdwardsBasepointTable` is approximately 4x faster. fn mul(self, scalar: &'b Scalar) -> EdwardsPoint { // If we built with AVX2, use the AVX2 backend. - #[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] { - use backend::avx2::edwards::ExtendedPoint; - let P_avx2 = ExtendedPoint::from(*self); - return EdwardsPoint::from(&P_avx2 * scalar); + #[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] + { + use backend::avx2::scalar_mul::variable_base::mul; + mul(self, scalar) } - // Otherwise, proceed as normal: - #[cfg(not(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2"))))] { - // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] - let lookup_table = LookupTable::::from(self); - - // Setting s = scalar, compute - // - // s = s_0 + s_1*16^1 + ... + s_63*16^63, - // - // with `-8 ≤ s_i < 8` for `0 ≤ i < 63` and `-8 ≤ s_63 ≤ 8`. - let scalar_digits = scalar.to_radix_16(); - - // Compute s*P as - // - // s*P = P*(s_0 + s_1*16^1 + s_2*16^2 + ... + s_63*16^63) - // s*P = P*s_0 + P*s_1*16^1 + P*s_2*16^2 + ... + P*s_63*16^63 - // s*P = P*s_0 + 16*(P*s_1 + 16*(P*s_2 + 16*( ... + P*s_63)...)) - // - // We sum right-to-left. - let mut Q = EdwardsPoint::identity(); - for i in (0..64).rev() { - // Q <-- 16*Q - Q = Q.mul_by_pow_2(4); - // Q <-- Q + P * s_i - Q = (&Q + &lookup_table.select(scalar_digits[i])).to_extended() - } - - Q + // Otherwise, use the serial backend: + #[cfg(not(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2"))))] + { + use scalar_mul::variable_base::mul; + mul(self, scalar) } } } diff --git a/src/scalar_mul/mod.rs b/src/scalar_mul/mod.rs index bc3ef90..acb5020 100644 --- a/src/scalar_mul/mod.rs +++ b/src/scalar_mul/mod.rs @@ -9,3 +9,5 @@ // - Henry de Valence pub mod window; + +pub mod variable_base; diff --git a/src/scalar_mul/variable_base.rs b/src/scalar_mul/variable_base.rs new file mode 100644 index 0000000..9569bd5 --- /dev/null +++ b/src/scalar_mul/variable_base.rs @@ -0,0 +1,32 @@ +#![allow(non_snake_case)] + +use traits::Identity; +use scalar::Scalar; +use edwards::EdwardsPoint; +use curve_models::ProjectiveNielsPoint; +use scalar_mul::window::LookupTable; + +/// Perform constant-time, variable-base scalar multiplication. +pub(crate) fn mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint { + // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] + let lookup_table = LookupTable::::from(point); + // Setting s = scalar, compute + // + // s = s_0 + s_1*16^1 + ... + s_63*16^63, + // + // with `-8 ≤ s_i < 8` for `0 ≤ i < 63` and `-8 ≤ s_63 ≤ 8`. + let scalar_digits = scalar.to_radix_16(); + // Compute s*P as + // + // s*P = P*(s_0 + s_1*16^1 + s_2*16^2 + ... + s_63*16^63) + // s*P = P*s_0 + P*s_1*16^1 + P*s_2*16^2 + ... + P*s_63*16^63 + // s*P = P*s_0 + 16*(P*s_1 + 16*(P*s_2 + 16*( ... + P*s_63)...)) + // + // We sum right-to-left. + let mut Q = EdwardsPoint::identity(); + for i in (0..64).rev() { + Q = Q.mul_by_pow_2(4); + Q = (&Q + &lookup_table.select(scalar_digits[i])).to_extended(); + } + Q +}