From 2864a422bc2025be78413404a707d19cd66839d1 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 26 Mar 2018 15:32:20 -0700 Subject: [PATCH] Pull out constant-time straus implementation --- src/backend/avx2/scalar_mul/mod.rs | 2 + src/backend/avx2/scalar_mul/straus.rs | 57 ++++++++++++++++++ src/edwards.rs | 74 ++++-------------------- src/scalar_mul/mod.rs | 2 + src/scalar_mul/straus.rs | 83 +++++++++++++++++++++++++++ src/scalar_mul/vartime_straus.rs | 64 +++++++++++++++++++++ 6 files changed, 219 insertions(+), 63 deletions(-) create mode 100644 src/backend/avx2/scalar_mul/straus.rs create mode 100644 src/scalar_mul/straus.rs create mode 100644 src/scalar_mul/vartime_straus.rs diff --git a/src/backend/avx2/scalar_mul/mod.rs b/src/backend/avx2/scalar_mul/mod.rs index 54f5214..f529730 100644 --- a/src/backend/avx2/scalar_mul/mod.rs +++ b/src/backend/avx2/scalar_mul/mod.rs @@ -9,3 +9,5 @@ // - Henry de Valence pub mod variable_base; + +pub mod straus; diff --git a/src/backend/avx2/scalar_mul/straus.rs b/src/backend/avx2/scalar_mul/straus.rs new file mode 100644 index 0000000..5f76abe --- /dev/null +++ b/src/backend/avx2/scalar_mul/straus.rs @@ -0,0 +1,57 @@ +// -*- 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 +#![allow(non_snake_case)] + +use core::borrow::Borrow; + +use clear_on_drop::ClearOnDrop; + +use traits::Identity; +use scalar::Scalar; +use edwards::EdwardsPoint; +use scalar_mul::window::LookupTable; +use backend::avx2::edwards::{CachedPoint, ExtendedPoint}; + +/// Perform constant-time, variable-base scalar multiplication. +pub(crate) fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint +where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, +{ + // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] + // for each input point P + let lookup_tables: Vec<_> = points + .into_iter() + .map(|point| { + let avx2_point = ExtendedPoint::from(*point.borrow()); + LookupTable::::from(avx2_point) + }) + .collect(); + + let scalar_digits_vec: Vec<_> = scalars + .into_iter() + .map(|s| s.borrow().to_radix_16()) + .collect(); + // Pass ownership to a ClearOnDrop wrapper + let scalar_digits = ClearOnDrop::new(scalar_digits_vec); + + let mut Q = ExtendedPoint::identity(); + for j in (0..64).rev() { + 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 + Q = &Q + &lookup_table_i.select(s_i[j]); + } + } + Q.into() +} diff --git a/src/edwards.rs b/src/edwards.rs index 1e2f31d..42802cb 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -547,8 +547,6 @@ impl<'a, 'b> Mul<&'b EdwardsPoint> for &'a Scalar { /// /// 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_mul(scalars: I, points: J) -> EdwardsPoint where I: IntoIterator, @@ -556,70 +554,20 @@ pub fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint 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; + // XXX later when we do more fancy multiscalar mults, we can + // delegate based on the iter's size hint -- hdevalence - edwards_avx2::multiscalar_mul(scalars, points) + // If we built with AVX2, use the AVX2 backend. + #[cfg(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2")))] + { + use backend::avx2::scalar_mul::straus::multiscalar_mul; + 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()); - - use clear_on_drop::ClearOnDrop; - - let lookup_tables_vec: Vec<_> = points.into_iter() - .map(|P| LookupTable::::from(P.borrow()) ) - .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_vec: Vec<_> = scalars.into_iter() - .map(|c| c.borrow().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 - // - // s_i*P_i = P_i*(s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63) - // s_i*P_i = P_i*s_{i,0} + P_i*s_{i,1}*16^1 + ... + P_i*s_{i,63}*16^63 - // s_i*P_i = P_i*s_{i,0} + 16*(P_i*s_{i,1} + 16*( ... + 16*P_i*s_{i,63})...) - // - // we have the two-dimensional sum - // - // s_1*P_1 = P_1*s_{1,0} + 16*(P_1*s_{1,1} + 16*( ... + 16*P_1*s_{1,63})...) - // + s_2*P_2 = + P_2*s_{2,0} + 16*(P_2*s_{2,1} + 16*( ... + 16*P_2*s_{2,63})...) - // ... - // + s_n*P_n = + P_n*s_{n,0} + 16*(P_n*s_{n,1} + 16*( ... + 16*P_n*s_{n,63})...) - // - // We sum column-wise top-to-bottom, then right-to-left, - // multiplying by 16 only once per column. - // - // This provides the speedup over doing n independent scalar - // mults: we perform 63 multiplications by 16 instead of 63*n - // multiplications, saving 252*(n-1) doublings. - 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.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 - let R_i = lookup_table_i.select(s_i[j]); - // Q = Q + R_i - Q = (&Q + &R_i).to_extended(); - } - } - Q + #[cfg(not(all(feature="nightly", all(feature="avx2_backend", target_feature="avx2"))))] + { + use scalar_mul::straus::multiscalar_mul; + multiscalar_mul(scalars, points) } } diff --git a/src/scalar_mul/mod.rs b/src/scalar_mul/mod.rs index acb5020..b69535d 100644 --- a/src/scalar_mul/mod.rs +++ b/src/scalar_mul/mod.rs @@ -11,3 +11,5 @@ pub mod window; pub mod variable_base; + +pub mod straus; diff --git a/src/scalar_mul/straus.rs b/src/scalar_mul/straus.rs new file mode 100644 index 0000000..e448ee9 --- /dev/null +++ b/src/scalar_mul/straus.rs @@ -0,0 +1,83 @@ +// -*- 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 +#![allow(non_snake_case)] + +use core::borrow::Borrow; + +use clear_on_drop::ClearOnDrop; + +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 multiscalar_mul(scalars: I, points: J) -> EdwardsPoint +where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, +{ + // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] + // for each input point P + let lookup_tables: Vec<_> = points + .into_iter() + .map(|point| LookupTable::::from(point.borrow())) + .collect(); + + // 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`. + // + // This 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_vec: Vec<_> = scalars + .into_iter() + .map(|s| s.borrow().to_radix_16()) + .collect(); + let scalar_digits = ClearOnDrop::new(scalar_digits_vec); + + // Compute s_1*P_1 + ... + s_n*P_n: since + // + // s_i*P_i = P_i*(s_{i,0} + s_{i,1}*16^1 + ... + s_{i,63}*16^63) + // s_i*P_i = P_i*s_{i,0} + P_i*s_{i,1}*16^1 + ... + P_i*s_{i,63}*16^63 + // s_i*P_i = P_i*s_{i,0} + 16*(P_i*s_{i,1} + 16*( ... + 16*P_i*s_{i,63})...) + // + // we have the two-dimensional sum + // + // s_1*P_1 = P_1*s_{1,0} + 16*(P_1*s_{1,1} + 16*( ... + 16*P_1*s_{1,63})...) + // + s_2*P_2 = + P_2*s_{2,0} + 16*(P_2*s_{2,1} + 16*( ... + 16*P_2*s_{2,63})...) + // ... + // + s_n*P_n = + P_n*s_{n,0} + 16*(P_n*s_{n,1} + 16*( ... + 16*P_n*s_{n,63})...) + // + // We sum column-wise top-to-bottom, then right-to-left, + // multiplying by 16 only once per column. + // + // This provides the speedup over doing n independent scalar + // mults: we perform 63 multiplications by 16 instead of 63*n + // multiplications, saving 252*(n-1) doublings. + let mut Q = EdwardsPoint::identity(); + for j in (0..64).rev() { + 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 + let R_i = lookup_table_i.select(s_i[j]); + // Q = Q + R_i + Q = (&Q + &R_i).to_extended(); + } + } + Q +} diff --git a/src/scalar_mul/vartime_straus.rs b/src/scalar_mul/vartime_straus.rs new file mode 100644 index 0000000..890677b --- /dev/null +++ b/src/scalar_mul/vartime_straus.rs @@ -0,0 +1,64 @@ +// -*- 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 +#![allow(non_snake_case)] + +use core::ops::{Add, Sub}; +use core::borrow::Borrow; + +use scalar::Scalar; + +//use super::window::OddLookupTable; +use scalar_mul::window::OddLookupTable; +use traits::{Doubleable, Identity}; + +/// Perform variable-time, variable-base scalar multiplication. +pub(crate) fn multiscalar_mul( + scalars: I, + points: J, +) -> ExtendedPoint +where + I: IntoIterator, + I::Item: Borrow, + J: IntoIterator, + J::Item: Borrow, + for<'a> OddLookupTable: From<&'a ExtendedPoint>, + for<'a, 'b> &'a ExtendedPoint: Add<&'b CachedPoint, Output = CompletedPoint>, + for<'a, 'b> &'a ExtendedPoint: Sub<&'b CachedPoint, Output = CompletedPoint>, + ExtendedPoint: Identity + From + From, + ProjectivePoint: Identity + Doubleable + From, + CachedPoint: Copy, +{ + let nafs: Vec<_> = scalars + .into_iter() + .map(|c| c.borrow().non_adjacent_form()) + .collect(); + let lookup_tables: Vec<_> = points + .into_iter() + .map(|P| OddLookupTable::from(P.borrow())) + .collect(); + + let mut r = ProjectivePoint::identity(); + + for i in (0..255).rev() { + let mut t: CompletedPoint = r.double(); + + for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) { + if naf[i] > 0 { + t = &ExtendedPoint::from(t) + &lookup_table.select(naf[i] as usize); + } else if naf[i] < 0 { + t = &ExtendedPoint::from(t) - &lookup_table.select(-naf[i] as usize); + } + } + + r = ProjectivePoint::from(t); + } + + ExtendedPoint::from(r) +}