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.
This commit is contained in:
Henry de Valence 2018-03-26 15:01:48 -07:00
parent 76a8d43a04
commit ac739a3edd
7 changed files with 90 additions and 146 deletions

View file

@ -371,40 +371,6 @@ impl From<ExtendedPoint> for LookupTable<CachedPoint> {
} }
} }
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::<CachedPoint>::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)] #[derive(Clone)]
pub struct EdwardsBasepointTable(pub [LookupTable<CachedPoint>; 32]); pub struct EdwardsBasepointTable(pub [LookupTable<CachedPoint>; 32]);
@ -739,7 +705,6 @@ mod test {
assert_eq!(R1.compress(), edwards::EdwardsPoint::identity().compress()); assert_eq!(R1.compress(), edwards::EdwardsPoint::identity().compress());
} }
#[test] #[test]
fn vector_addition_vs_serial_addition_vs_edwards_extendedpoint() { fn vector_addition_vs_serial_addition_vs_edwards_extendedpoint() {
use constants; use constants;
@ -839,83 +804,4 @@ mod test {
let P = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from_u64(8475983829); let P = &constants::ED25519_BASEPOINT_TABLE * &Scalar::from_u64(8475983829);
doubling_test_helper(P); 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());
}
}
} }

View file

@ -479,3 +479,5 @@ pub(crate) mod field;
pub(crate) mod edwards; pub(crate) mod edwards;
pub(crate) mod constants; pub(crate) mod constants;
pub(crate) mod scalar_mul;

View file

@ -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 <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca>
pub mod variable_base;

View file

@ -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::<CachedPoint>::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()
}

View file

@ -477,39 +477,16 @@ impl<'a, 'b> Mul<&'b Scalar> for &'a EdwardsPoint {
/// `EdwardsBasepointTable` is approximately 4x faster. /// `EdwardsBasepointTable` is approximately 4x faster.
fn mul(self, scalar: &'b Scalar) -> EdwardsPoint { fn mul(self, scalar: &'b Scalar) -> EdwardsPoint {
// If we built with AVX2, use the AVX2 backend. // If we built with AVX2, use the AVX2 backend.
#[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] { #[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))]
use backend::avx2::edwards::ExtendedPoint; {
let P_avx2 = ExtendedPoint::from(*self); use backend::avx2::scalar_mul::variable_base::mul;
return EdwardsPoint::from(&P_avx2 * scalar); mul(self, scalar)
} }
// Otherwise, proceed as normal: // Otherwise, use the serial backend:
#[cfg(not(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2"))))] { #[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::<ProjectiveNielsPoint>::from(self); use scalar_mul::variable_base::mul;
mul(self, scalar)
// 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
} }
} }
} }

View file

@ -9,3 +9,5 @@
// - Henry de Valence <hdevalence@hdevalence.ca> // - Henry de Valence <hdevalence@hdevalence.ca>
pub mod window; pub mod window;
pub mod variable_base;

View file

@ -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::<ProjectiveNielsPoint>::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
}