mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-04 20:24:10 +00:00
Merge remote-tracking branch 'dalek/multiscalar-trait-without-precomputation_r1' into develop
This commit is contained in:
commit
9a89a217f8
10 changed files with 517 additions and 488 deletions
|
|
@ -18,7 +18,10 @@ static MULTISCALAR_SIZES: [usize; 13] = [1, 2, 4, 8, 16, 32, 64, 128, 256, 384,
|
|||
|
||||
mod edwards_benches {
|
||||
use super::*;
|
||||
use curve25519_dalek::edwards::{self, EdwardsPoint};
|
||||
use curve25519_dalek::edwards;
|
||||
use curve25519_dalek::edwards::EdwardsPoint;
|
||||
use curve25519_dalek::traits::MultiscalarMul;
|
||||
use curve25519_dalek::traits::VartimeMultiscalarMul;
|
||||
|
||||
fn compress(c: &mut Criterion) {
|
||||
let B = &constants::ED25519_BASEPOINT_POINT;
|
||||
|
|
@ -56,7 +59,7 @@ mod edwards_benches {
|
|||
let a = Scalar::from_u64(298374928).invert();
|
||||
let b = Scalar::from_u64(897987897).invert();
|
||||
let A = B * (b * a);
|
||||
bench.iter(|| edwards::vartime::double_scalar_mul_basepoint(&a, &A, &b));
|
||||
bench.iter(|| EdwardsPoint::vartime_double_scalar_mul_basepoint(&a, &A, &b));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -70,7 +73,7 @@ mod edwards_benches {
|
|||
.iter()
|
||||
.map(|s| s * &constants::ED25519_BASEPOINT_TABLE)
|
||||
.collect();
|
||||
b.iter(|| edwards::multiscalar_mul(&scalars, &points));
|
||||
b.iter(|| EdwardsPoint::multiscalar_mul(&scalars, &points));
|
||||
},
|
||||
&MULTISCALAR_SIZES,
|
||||
);
|
||||
|
|
@ -86,7 +89,7 @@ mod edwards_benches {
|
|||
.iter()
|
||||
.map(|s| s * &constants::ED25519_BASEPOINT_TABLE)
|
||||
.collect();
|
||||
b.iter(|| edwards::vartime::multiscalar_mul(&scalars, &points));
|
||||
b.iter(|| EdwardsPoint::vartime_multiscalar_mul(&scalars, &points));
|
||||
},
|
||||
&MULTISCALAR_SIZES,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13,8 +13,4 @@ pub mod variable_base;
|
|||
#[cfg(feature = "stage2_build")]
|
||||
pub mod vartime_double_base;
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
pub mod straus;
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
pub mod vartime_straus;
|
||||
|
|
|
|||
|
|
@ -7,48 +7,98 @@
|
|||
// Authors:
|
||||
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||
#![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};
|
||||
use edwards::EdwardsPoint;
|
||||
use scalar::Scalar;
|
||||
use scalar_mul::window::{LookupTable, NafLookupTable5};
|
||||
use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul};
|
||||
|
||||
/// Perform constant-time, variable-base scalar multiplication.
|
||||
pub(crate) fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
{
|
||||
// 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::<CachedPoint>::from(point.borrow()))
|
||||
.collect();
|
||||
/// Multiscalar multiplication using interleaved window / Straus'
|
||||
/// method. See the `Straus` struct in the serial backend for more
|
||||
/// details.
|
||||
///
|
||||
/// This exists as a seperate implementation from that one because the
|
||||
/// AVX2 code uses different curve models (it does not pass between
|
||||
/// multiple models during scalar mul), and it has to convert the
|
||||
/// point representation on the fly.
|
||||
pub struct Straus {}
|
||||
|
||||
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);
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
impl MultiscalarMul for Straus {
|
||||
type Point = EdwardsPoint;
|
||||
|
||||
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]);
|
||||
fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
{
|
||||
// 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::<CachedPoint>::from(point.borrow()))
|
||||
.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()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
impl VartimeMultiscalarMul for Straus {
|
||||
type Point = EdwardsPoint;
|
||||
|
||||
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
{
|
||||
let nafs: Vec<_> = scalars
|
||||
.into_iter()
|
||||
.map(|c| c.borrow().non_adjacent_form(5))
|
||||
.collect();
|
||||
let lookup_tables: Vec<_> = points
|
||||
.into_iter()
|
||||
.map(|point| NafLookupTable5::<CachedPoint>::from(point.borrow()))
|
||||
.collect();
|
||||
|
||||
let mut Q = ExtendedPoint::identity();
|
||||
|
||||
for i in (0..255).rev() {
|
||||
Q = Q.double();
|
||||
|
||||
for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) {
|
||||
if naf[i] > 0 {
|
||||
Q = &Q + &lookup_table.select(naf[i] as usize);
|
||||
} else if naf[i] < 0 {
|
||||
Q = &Q - &lookup_table.select(-naf[i] as usize);
|
||||
}
|
||||
}
|
||||
}
|
||||
Q.into()
|
||||
}
|
||||
Q.into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
// -*- 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>
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use core::borrow::Borrow;
|
||||
|
||||
use traits::Identity;
|
||||
use scalar::Scalar;
|
||||
use edwards::EdwardsPoint;
|
||||
use scalar_mul::window::NafLookupTable5;
|
||||
use backend::avx2::edwards::{CachedPoint, ExtendedPoint};
|
||||
|
||||
/// Perform variable-time, variable-base scalar multiplication.
|
||||
pub(crate) fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
{
|
||||
let nafs: Vec<_> = scalars
|
||||
.into_iter()
|
||||
.map(|c| c.borrow().non_adjacent_form(5))
|
||||
.collect();
|
||||
let lookup_tables: Vec<_> = points
|
||||
.into_iter()
|
||||
.map(|point| NafLookupTable5::<CachedPoint>::from(point.borrow()))
|
||||
.collect();
|
||||
|
||||
let mut Q = ExtendedPoint::identity();
|
||||
|
||||
for i in (0..255).rev() {
|
||||
Q = Q.double();
|
||||
|
||||
for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) {
|
||||
if naf[i] > 0 {
|
||||
Q = &Q + &lookup_table.select(naf[i] as usize);
|
||||
} else if naf[i] < 0 {
|
||||
Q = &Q - &lookup_table.select(-naf[i] as usize);
|
||||
}
|
||||
}
|
||||
}
|
||||
Q.into()
|
||||
}
|
||||
243
src/edwards.rs
243
src/edwards.rs
|
|
@ -112,6 +112,7 @@ use field::FieldElement;
|
|||
use scalar::Scalar;
|
||||
|
||||
use montgomery::MontgomeryPoint;
|
||||
|
||||
use curve_models::ProjectivePoint;
|
||||
use curve_models::CompletedPoint;
|
||||
use curve_models::AffineNielsPoint;
|
||||
|
|
@ -121,6 +122,8 @@ use scalar_mul::window::LookupTable;
|
|||
|
||||
use traits::{Identity, IsIdentity};
|
||||
use traits::ValidityCheck;
|
||||
use traits::MultiscalarMul;
|
||||
use traits::VartimeMultiscalarMul;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Compressed points
|
||||
|
|
@ -516,71 +519,89 @@ impl<'a, 'b> Mul<&'b EdwardsPoint> for &'a Scalar {
|
|||
}
|
||||
}
|
||||
|
||||
/// Given an iterator of (possibly secret) scalars and an iterator of
|
||||
/// (possibly secret) points, compute
|
||||
/// $$
|
||||
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
|
||||
/// $$
|
||||
///
|
||||
/// This function has the same behaviour as
|
||||
/// `vartime::multiscalar_mul` but is constant-time.
|
||||
///
|
||||
/// 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<Scalar>` (or `Borrow<EdwardsPoint>`), 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<Item=&Scalar>
|
||||
///
|
||||
/// // 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<Item=Scalar>
|
||||
///
|
||||
/// assert_eq!(A1.compress(), (-A2).compress());
|
||||
/// ```
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
pub fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||
where I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
{
|
||||
// XXX later when we do more fancy multiscalar mults, we can
|
||||
// delegate based on the iter's size hint -- hdevalence
|
||||
// ------------------------------------------------------------------------
|
||||
// Multiscalar Multiplication impls
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// If we built with AVX2, use the AVX2 backend.
|
||||
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
|
||||
// These use the iterator's size hint and the target settings to
|
||||
// forward to a specific backend implementation.
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
impl MultiscalarMul for EdwardsPoint {
|
||||
type Point = EdwardsPoint;
|
||||
|
||||
fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
{
|
||||
use backend::avx2::scalar_mul::straus::multiscalar_mul;
|
||||
multiscalar_mul(scalars, points)
|
||||
// XXX later when we do more fancy multiscalar mults, we can
|
||||
// delegate based on the iter's size hint -- hdevalence
|
||||
|
||||
// If we built with AVX2, use the AVX2 backend.
|
||||
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
|
||||
{
|
||||
use backend::avx2::scalar_mul::straus::Straus;
|
||||
Straus::multiscalar_mul(scalars, points)
|
||||
}
|
||||
// Otherwise, proceed as normal:
|
||||
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
|
||||
{
|
||||
use scalar_mul::straus::Straus;
|
||||
Straus::multiscalar_mul(scalars, points)
|
||||
}
|
||||
}
|
||||
// Otherwise, proceed as normal:
|
||||
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
impl VartimeMultiscalarMul for EdwardsPoint {
|
||||
type Point = EdwardsPoint;
|
||||
|
||||
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
{
|
||||
use scalar_mul::straus::multiscalar_mul;
|
||||
multiscalar_mul(scalars, points)
|
||||
// XXX later when we do more fancy multiscalar mults, we can
|
||||
// delegate based on the iter's size hint -- hdevalence
|
||||
|
||||
// If we built with AVX2, use the AVX2 backend.
|
||||
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
|
||||
{
|
||||
use backend::avx2::scalar_mul::straus::Straus;
|
||||
Straus::vartime_multiscalar_mul(scalars, points)
|
||||
}
|
||||
// Otherwise, proceed as normal:
|
||||
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
|
||||
{
|
||||
use scalar_mul::straus::Straus;
|
||||
Straus::vartime_multiscalar_mul(scalars, points)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EdwardsPoint {
|
||||
/// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint.
|
||||
///
|
||||
/// XXX eliminate this function when we have the precomputation API
|
||||
#[cfg(feature = "stage2_build")]
|
||||
pub fn vartime_double_scalar_mul_basepoint(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint {
|
||||
// If we built with AVX2, use the AVX2 backend.
|
||||
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
|
||||
{
|
||||
use backend::avx2::scalar_mul::vartime_double_base::mul;
|
||||
mul(a, A, b)
|
||||
}
|
||||
// Otherwise, proceed as normal:
|
||||
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
|
||||
{
|
||||
use scalar_mul::vartime_double_base::mul;
|
||||
mul(a, A, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -783,98 +804,6 @@ impl Debug for EdwardsBasepointTable {
|
|||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Variable-time functions
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
pub mod vartime {
|
||||
//! Variable-time operations on curve points, useful for non-secret data.
|
||||
use super::*;
|
||||
|
||||
/// Given an iterator of public scalars and an iterator of public points, compute
|
||||
/// $$
|
||||
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
|
||||
/// $$
|
||||
///
|
||||
/// This function has the same behaviour as
|
||||
/// `edwards::multiscalar_mul` but operates on non-secret data.
|
||||
///
|
||||
/// 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<Scalar>` (or `Borrow<EdwardsPoint>`), 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<Item=&Scalar>
|
||||
///
|
||||
/// // 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<Item=Scalar>
|
||||
///
|
||||
/// assert_eq!(A1.compress(), (-A2).compress());
|
||||
/// ```
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
pub fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||
where I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
{
|
||||
// XXX later when we do more fancy multiscalar mults, we can delegate
|
||||
// based on the iter's size hint -- hdevalence
|
||||
// If we built with AVX2, use the AVX2 backend.
|
||||
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
|
||||
{
|
||||
use backend::avx2::scalar_mul::vartime_straus::multiscalar_mul;
|
||||
multiscalar_mul(scalars, points)
|
||||
}
|
||||
// Otherwise, proceed as normal:
|
||||
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
|
||||
{
|
||||
use scalar_mul::vartime_straus::multiscalar_mul;
|
||||
multiscalar_mul(scalars, points)
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint.
|
||||
#[cfg(feature="stage2_build")]
|
||||
pub fn double_scalar_mul_basepoint(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint {
|
||||
// If we built with AVX2, use the AVX2 backend.
|
||||
#[cfg(all(feature="avx2_backend", target_feature="avx2"))]
|
||||
{
|
||||
use backend::avx2::scalar_mul::vartime_double_base::mul;
|
||||
mul(a, A, b)
|
||||
}
|
||||
// Otherwise, proceed as normal:
|
||||
#[cfg(not(all(feature="avx2_backend", target_feature="avx2")))]
|
||||
{
|
||||
use scalar_mul::vartime_double_base::mul;
|
||||
mul(a, A, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Tests
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
@ -1204,14 +1133,14 @@ mod test {
|
|||
#[test]
|
||||
fn double_scalar_mul_basepoint_vs_ed25519py() {
|
||||
let A = A_TIMES_BASEPOINT.decompress().unwrap();
|
||||
let result = vartime::double_scalar_mul_basepoint(&A_SCALAR, &A, &B_SCALAR);
|
||||
let result = EdwardsPoint::vartime_double_scalar_mul_basepoint(&A_SCALAR, &A, &B_SCALAR);
|
||||
assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiscalar_mul_vs_ed25519py() {
|
||||
let A = A_TIMES_BASEPOINT.decompress().unwrap();
|
||||
let result = vartime::multiscalar_mul(
|
||||
let result = EdwardsPoint::vartime_multiscalar_mul(
|
||||
&[A_SCALAR, B_SCALAR],
|
||||
&[A, constants::ED25519_BASEPOINT_POINT]
|
||||
);
|
||||
|
|
@ -1221,11 +1150,11 @@ mod test {
|
|||
#[test]
|
||||
fn multiscalar_mul_vartime_vs_consttime() {
|
||||
let A = A_TIMES_BASEPOINT.decompress().unwrap();
|
||||
let result_vartime = vartime::multiscalar_mul(
|
||||
let result_vartime = EdwardsPoint::vartime_multiscalar_mul(
|
||||
&[A_SCALAR, B_SCALAR],
|
||||
&[A, constants::ED25519_BASEPOINT_POINT]
|
||||
);
|
||||
let result_consttime = multiscalar_mul(
|
||||
let result_consttime = EdwardsPoint::multiscalar_mul(
|
||||
&[A_SCALAR, B_SCALAR],
|
||||
&[A, constants::ED25519_BASEPOINT_POINT]
|
||||
);
|
||||
|
|
|
|||
157
src/ristretto.rs
157
src/ristretto.rs
|
|
@ -185,7 +185,6 @@ use subtle::ConditionallyNegatable;
|
|||
use subtle::ConstantTimeEq;
|
||||
use subtle::Choice;
|
||||
|
||||
use edwards;
|
||||
use edwards::EdwardsPoint;
|
||||
use edwards::EdwardsBasepointTable;
|
||||
|
||||
|
|
@ -193,7 +192,7 @@ use scalar::Scalar;
|
|||
|
||||
use curve_models::CompletedPoint;
|
||||
|
||||
use traits::Identity;
|
||||
use traits::{Identity, MultiscalarMul, VartimeMultiscalarMul};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Compressed points
|
||||
|
|
@ -788,60 +787,47 @@ define_mul_assign_variants!(LHS = RistrettoPoint, RHS = Scalar);
|
|||
define_mul_variants!(LHS = RistrettoPoint, RHS = Scalar, Output = RistrettoPoint);
|
||||
define_mul_variants!(LHS = Scalar, RHS = RistrettoPoint, Output = RistrettoPoint);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Multiscalar Multiplication impls
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// These use iterator combinators to unwrap the underlying points and
|
||||
// forward to the EdwardsPoint implementations.
|
||||
|
||||
/// Given an iterator of (possibly secret) scalars and an iterator of
|
||||
/// (possibly secret) points, compute
|
||||
/// $$
|
||||
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
|
||||
/// $$
|
||||
///
|
||||
/// This function has the same behaviour as
|
||||
/// `vartime::multiscalar_mul` but is constant-time.
|
||||
///
|
||||
/// 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<Scalar>` (or `Borrow<RistrettoPoint>`), 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<Item=&Scalar>
|
||||
///
|
||||
/// // 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<Item=Scalar>
|
||||
///
|
||||
/// assert_eq!(A1.compress(), (-A2).compress());
|
||||
/// ```
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
pub fn multiscalar_mul<I, J>(scalars: I, points: J) -> RistrettoPoint
|
||||
where I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<RistrettoPoint>,
|
||||
{
|
||||
let extended_points = points.into_iter().map(|P| P.borrow().0);
|
||||
RistrettoPoint(edwards::multiscalar_mul(scalars, extended_points))
|
||||
impl MultiscalarMul for RistrettoPoint {
|
||||
type Point = RistrettoPoint;
|
||||
|
||||
fn multiscalar_mul<I, J>(scalars: I, points: J) -> RistrettoPoint
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<RistrettoPoint>,
|
||||
{
|
||||
let extended_points = points.into_iter().map(|P| P.borrow().0);
|
||||
RistrettoPoint(
|
||||
EdwardsPoint::multiscalar_mul(scalars, extended_points)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
impl VartimeMultiscalarMul for RistrettoPoint {
|
||||
type Point = RistrettoPoint;
|
||||
|
||||
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> RistrettoPoint
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<RistrettoPoint>,
|
||||
{
|
||||
let extended_points = points.into_iter().map(|P| P.borrow().0);
|
||||
RistrettoPoint(
|
||||
EdwardsPoint::vartime_multiscalar_mul(scalars, extended_points)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A precomputed table of multiples of a basepoint, used to accelerate
|
||||
|
|
@ -945,69 +931,6 @@ impl Debug for RistrettoPoint {
|
|||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Variable-time functions
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
pub mod vartime {
|
||||
//! Variable-time operations on ristretto points, useful for non-secret data.
|
||||
use super::*;
|
||||
|
||||
/// Given an iterator of public scalars and an iterator of public points, compute
|
||||
/// $$
|
||||
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
|
||||
/// $$
|
||||
///
|
||||
/// This function has the same behaviour as
|
||||
/// `vartime::multiscalar_mul` but is constant-time.
|
||||
///
|
||||
/// 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<Scalar>` (or `Borrow<RistrettoPoint>`), 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<Item=&Scalar>
|
||||
///
|
||||
/// // 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<Item=Scalar>
|
||||
///
|
||||
/// assert_eq!(A1.compress(), (-A2).compress());
|
||||
/// ```
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
pub fn multiscalar_mul<I, J>(scalars: I, points: J) -> RistrettoPoint
|
||||
where I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<RistrettoPoint>,
|
||||
{
|
||||
let extended_points = points.into_iter().map(|P| P.borrow().0);
|
||||
RistrettoPoint(edwards::vartime::multiscalar_mul(scalars, extended_points))
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Tests
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -8,6 +8,14 @@
|
|||
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||
|
||||
//! Implementations of various scalar multiplication algorithms.
|
||||
//!
|
||||
//! Note that all of these implementations use serial code for field
|
||||
//! arithmetic with the multi-model strategy described in the
|
||||
//! `curve_models` module. The vectorized AVX2 backend has its own
|
||||
//! scalar multiplication implementations, since it only uses one
|
||||
//! curve model.
|
||||
|
||||
pub mod window;
|
||||
|
||||
pub mod variable_base;
|
||||
|
|
@ -15,8 +23,4 @@ pub mod variable_base;
|
|||
#[cfg(feature = "stage2_build")]
|
||||
pub mod vartime_double_base;
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
pub mod straus;
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
pub mod vartime_straus;
|
||||
|
|
|
|||
|
|
@ -7,77 +7,187 @@
|
|||
// Authors:
|
||||
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||
|
||||
//! Implementation of the interleaved window method, also known as Straus' method.
|
||||
|
||||
#![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;
|
||||
use scalar::Scalar;
|
||||
use traits::MultiscalarMul;
|
||||
use traits::VartimeMultiscalarMul;
|
||||
|
||||
/// Perform constant-time, variable-base scalar multiplication.
|
||||
pub(crate) fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
{
|
||||
// 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::<ProjectiveNielsPoint>::from(point.borrow()))
|
||||
.collect();
|
||||
/// Perform multiscalar multiplication by the interleaved window
|
||||
/// method, also known as Straus' method (since it was apparently
|
||||
/// [first published][solution] by Straus in 1964, as a solution to [a
|
||||
/// problem][problem] posted in the American Mathematical Monthly in
|
||||
/// 1963).
|
||||
///
|
||||
/// It is easy enough to reinvent, and has been repeatedly. The basic
|
||||
/// idea is that when computing
|
||||
/// \\[
|
||||
/// Q = s_1 P_1 + \cdots + s_n P_n
|
||||
/// \\]
|
||||
/// by means of additions and doublings, the doublings can be shared
|
||||
/// across the \\( P_i \\\).
|
||||
///
|
||||
/// We implement two versions, a constant-time algorithm using fixed
|
||||
/// windows and a variable-time algorithm using sliding windows. They
|
||||
/// are slight variations on the same idea, and are described in more
|
||||
/// detail in the respective implementations.
|
||||
///
|
||||
/// [solution]: https://www.jstor.org/stable/2310929
|
||||
/// [problem]: https://www.jstor.org/stable/2312273
|
||||
pub struct Straus {}
|
||||
|
||||
// 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);
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
impl MultiscalarMul for Straus {
|
||||
type Point = EdwardsPoint;
|
||||
|
||||
// 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();
|
||||
/// Constant-time Straus using a fixed window of size \\(4\\).
|
||||
///
|
||||
/// Our goal is to compute
|
||||
/// \\[
|
||||
/// Q = s_1 P_1 + \cdots + s_n P_n.
|
||||
/// \\]
|
||||
///
|
||||
/// For each point \\( P_i \\), precompute a lookup table of
|
||||
/// \\[
|
||||
/// P_i, 2P_i, 3P_i, 4P_i, 5P_i, 6P_i, 7P_i, 8P_i.
|
||||
/// \\]
|
||||
///
|
||||
/// For each scalar \\( s_i \\), compute its radix-\\(2^4\\)
|
||||
/// signed digits \\( s_{i,j} \\), i.e.,
|
||||
/// \\[
|
||||
/// s_i = s_{i,0} + s_{i,1} 16^1 + ... + s_{i,63} 16^{63},
|
||||
/// \\]
|
||||
/// with \\( -8 \leq s_{i,j} < 8 \\). Since \\( 0 \leq |s_{i,j}|
|
||||
/// \leq 8 \\), we can retrieve \\( s_{i,j} P_i \\) from the
|
||||
/// lookup table with a conditional negation: using signed
|
||||
/// digits halves the required table size.
|
||||
///
|
||||
/// Then as in the single-base fixed window case, we have
|
||||
/// \\[
|
||||
/// \begin{aligned}
|
||||
/// s_i P_i &= P_i (s_{i,0} + s_{i,1} 16^1 + \cdots + s_{i,63} 16^{63}) \\\\
|
||||
/// s_i P_i &= P_i s_{i,0} + P_i s_{i,1} 16^1 + \cdots + P_i s_{i,63} 16^{63} \\\\
|
||||
/// s_i P_i &= P_i s_{i,0} + 16(P_i s_{i,1} + 16( \cdots +16P_i s_{i,63})\cdots )
|
||||
/// \end{aligned}
|
||||
/// \\]
|
||||
/// so each \\( s_i P_i \\) can be computed by alternately adding
|
||||
/// a precomputed multiple \\( P_i s_{i,j} \\) of \\( P_i \\) and
|
||||
/// repeatedly doubling.
|
||||
///
|
||||
/// Now consider the two-dimensional sum
|
||||
/// \\[
|
||||
/// \begin{aligned}
|
||||
/// s\_1 P\_1 &=& P\_1 s\_{1,0} &+& 16 (P\_1 s\_{1,1} &+& 16 ( \cdots &+& 16 P\_1 s\_{1,63}&) \cdots ) \\\\
|
||||
/// + & & + & & + & & & & + & \\\\
|
||||
/// s\_2 P\_2 &=& P\_2 s\_{2,0} &+& 16 (P\_2 s\_{2,1} &+& 16 ( \cdots &+& 16 P\_2 s\_{2,63}&) \cdots ) \\\\
|
||||
/// + & & + & & + & & & & + & \\\\
|
||||
/// \vdots & & \vdots & & \vdots & & & & \vdots & \\\\
|
||||
/// + & & + & & + & & & & + & \\\\
|
||||
/// s\_n P\_n &=& P\_n s\_{n,0} &+& 16 (P\_n s\_{n,1} &+& 16 ( \cdots &+& 16 P\_n s\_{n,63}&) \cdots )
|
||||
/// \end{aligned}
|
||||
/// \\]
|
||||
/// The sum of the left-hand column is the result \\( Q \\); by
|
||||
/// computing the two-dimensional sum on the right column-wise,
|
||||
/// top-to-bottom, then right-to-left, we need to multiply by \\(
|
||||
/// 16\\) only once per column, sharing the doublings across all
|
||||
/// of the input points.
|
||||
fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
{
|
||||
use clear_on_drop::ClearOnDrop;
|
||||
|
||||
use curve_models::ProjectiveNielsPoint;
|
||||
use scalar_mul::window::LookupTable;
|
||||
use traits::Identity;
|
||||
|
||||
let lookup_tables: Vec<_> = points
|
||||
.into_iter()
|
||||
.map(|point| LookupTable::<ProjectiveNielsPoint>::from(point.borrow()))
|
||||
.collect();
|
||||
|
||||
// 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);
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||
impl VartimeMultiscalarMul for Straus {
|
||||
type Point = EdwardsPoint;
|
||||
|
||||
/// Variable-time Straus using a non-adjacent form of width \\(5\\).
|
||||
///
|
||||
/// This is completely similar to the constant-time code, but we
|
||||
/// use a non-adjacent form for the scalar, and do not do table
|
||||
/// lookups in constant time.
|
||||
///
|
||||
/// The non-adjacent form has signed, odd digits. Using only odd
|
||||
/// digits halves the table size (since we only need odd
|
||||
/// multiples), or gives fewer additions for the same table size.
|
||||
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
{
|
||||
use curve_models::{CompletedPoint, ProjectiveNielsPoint, ProjectivePoint};
|
||||
use scalar_mul::window::NafLookupTable5;
|
||||
use traits::Identity;
|
||||
|
||||
let nafs: Vec<_> = scalars
|
||||
.into_iter()
|
||||
.map(|c| c.borrow().non_adjacent_form(5))
|
||||
.collect();
|
||||
let lookup_tables: Vec<_> = points
|
||||
.into_iter()
|
||||
.map(|P| NafLookupTable5::<ProjectiveNielsPoint>::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 = &t.to_extended() + &lookup_table.select(naf[i] as usize);
|
||||
} else if naf[i] < 0 {
|
||||
t = &t.to_extended() - &lookup_table.select(-naf[i] as usize);
|
||||
}
|
||||
}
|
||||
|
||||
r = t.to_projective();
|
||||
}
|
||||
|
||||
r.to_extended()
|
||||
}
|
||||
Q
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
// -*- 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>
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use core::borrow::Borrow;
|
||||
|
||||
use traits::Identity;
|
||||
use scalar::Scalar;
|
||||
use edwards::EdwardsPoint;
|
||||
use curve_models::{CompletedPoint, ProjectivePoint, ProjectiveNielsPoint};
|
||||
use scalar_mul::window::NafLookupTable5;
|
||||
|
||||
/// Perform variable-time, variable-base scalar multiplication.
|
||||
pub(crate) fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<EdwardsPoint>,
|
||||
{
|
||||
let nafs: Vec<_> = scalars
|
||||
.into_iter()
|
||||
.map(|c| c.borrow().non_adjacent_form(5))
|
||||
.collect();
|
||||
let lookup_tables: Vec<_> = points
|
||||
.into_iter()
|
||||
.map(|P| NafLookupTable5::<ProjectiveNielsPoint>::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 = &t.to_extended() + &lookup_table.select(naf[i] as usize);
|
||||
} else if naf[i] < 0 {
|
||||
t = &t.to_extended() - &lookup_table.select(-naf[i] as usize);
|
||||
}
|
||||
}
|
||||
|
||||
r = t.to_projective();
|
||||
}
|
||||
|
||||
r.to_extended()
|
||||
}
|
||||
121
src/traits.rs
121
src/traits.rs
|
|
@ -10,8 +10,12 @@
|
|||
|
||||
//! Module for common traits.
|
||||
|
||||
use core::borrow::Borrow;
|
||||
|
||||
use subtle;
|
||||
|
||||
use scalar::Scalar;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Public Traits
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
@ -32,12 +36,127 @@ pub trait IsIdentity {
|
|||
/// Implement generic identity equality testing for a point representations
|
||||
/// which have constant-time equality testing and a defined identity
|
||||
/// constructor.
|
||||
impl<T> IsIdentity for T where T: subtle::ConstantTimeEq + Identity {
|
||||
impl<T> IsIdentity for T
|
||||
where
|
||||
T: subtle::ConstantTimeEq + Identity,
|
||||
{
|
||||
fn is_identity(&self) -> bool {
|
||||
self.ct_eq(&T::identity()).unwrap_u8() == 1u8
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for constant-time multiscalar multiplication without precomputation.
|
||||
pub trait MultiscalarMul {
|
||||
/// The type of point being multiplied, e.g., `RistrettoPoint`.
|
||||
type Point;
|
||||
|
||||
/// Given an iterator of (possibly secret) scalars and an iterator of
|
||||
/// public points, compute
|
||||
/// $$
|
||||
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
|
||||
/// $$
|
||||
///
|
||||
/// 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<Scalar>` (or `Borrow<Point>`), to allow
|
||||
/// iterators returning either `Scalar`s or `&Scalar`s.
|
||||
///
|
||||
/// ```
|
||||
/// use curve25519_dalek::constants;
|
||||
/// use curve25519_dalek::traits::MultiscalarMul;
|
||||
/// use curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// 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 = RistrettoPoint::multiscalar_mul(&abc, &[P,Q,R]);
|
||||
/// // Note: (&abc).into_iter(): Iterator<Item=&Scalar>
|
||||
///
|
||||
/// // A2 = (-a)*P + (-b)*Q + (-c)*R
|
||||
/// let minus_abc = abc.iter().map(|x| -x);
|
||||
/// let A2 = RistrettoPoint::multiscalar_mul(minus_abc, &[P,Q,R]);
|
||||
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
|
||||
///
|
||||
/// assert_eq!(A1.compress(), (-A2).compress());
|
||||
/// ```
|
||||
fn multiscalar_mul<I, J>(scalars: I, points: J) -> Self::Point
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<Self::Point>;
|
||||
}
|
||||
|
||||
/// A trait for variable-time multiscalar multiplication without precomputation.
|
||||
pub trait VartimeMultiscalarMul {
|
||||
/// The type of point being multiplied, e.g., `RistrettoPoint`.
|
||||
type Point;
|
||||
|
||||
/// Given an iterator of (possibly secret) scalars and an iterator of
|
||||
/// public points, compute
|
||||
/// $$
|
||||
/// Q = c\_1 P\_1 + \cdots + c\_n P\_n.
|
||||
/// $$
|
||||
///
|
||||
/// 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<Scalar>` (or `Borrow<Point>`), to allow
|
||||
/// iterators returning either `Scalar`s or `&Scalar`s.
|
||||
///
|
||||
/// ```
|
||||
/// use curve25519_dalek::constants;
|
||||
/// use curve25519_dalek::traits::MultiscalarMul;
|
||||
/// use curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// 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 = RistrettoPoint::multiscalar_mul(&abc, &[P,Q,R]);
|
||||
/// // Note: (&abc).into_iter(): Iterator<Item=&Scalar>
|
||||
///
|
||||
/// // A2 = (-a)*P + (-b)*Q + (-c)*R
|
||||
/// let minus_abc = abc.iter().map(|x| -x);
|
||||
/// let A2 = RistrettoPoint::multiscalar_mul(minus_abc, &[P,Q,R]);
|
||||
/// // Note: minus_abc.into_iter(): Iterator<Item=Scalar>
|
||||
///
|
||||
/// assert_eq!(A1.compress(), (-A2).compress());
|
||||
/// ```
|
||||
fn vartime_multiscalar_mul<I, J>(scalars: I, points: J) -> Self::Point
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Borrow<Scalar>,
|
||||
J: IntoIterator,
|
||||
J::Item: Borrow<Self::Point>;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Private Traits
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Reference in a new issue