mirror of
https://github.com/saymrwulf/betrusted-curve25519-dalek-source.git
synced 2026-09-12 21:40:32 +00:00
Merge pull request #230 from hdevalence/updated-precomputation
Multiscalar multiplication with precomputation.
This commit is contained in:
commit
a1123e7cd3
12 changed files with 719 additions and 62 deletions
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
use rand::thread_rng;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate criterion;
|
extern crate criterion;
|
||||||
|
|
||||||
|
use criterion::BatchSize;
|
||||||
use criterion::Criterion;
|
use criterion::Criterion;
|
||||||
|
|
||||||
extern crate curve25519_dalek;
|
extern crate curve25519_dalek;
|
||||||
|
|
@ -20,14 +22,10 @@ mod edwards_benches {
|
||||||
use super::*;
|
use super::*;
|
||||||
use curve25519_dalek::edwards;
|
use curve25519_dalek::edwards;
|
||||||
use curve25519_dalek::edwards::EdwardsPoint;
|
use curve25519_dalek::edwards::EdwardsPoint;
|
||||||
use curve25519_dalek::traits::MultiscalarMul;
|
|
||||||
use curve25519_dalek::traits::VartimeMultiscalarMul;
|
|
||||||
|
|
||||||
fn compress(c: &mut Criterion) {
|
fn compress(c: &mut Criterion) {
|
||||||
let B = &constants::ED25519_BASEPOINT_POINT;
|
let B = &constants::ED25519_BASEPOINT_POINT;
|
||||||
c.bench_function("EdwardsPoint compression", move |b| {
|
c.bench_function("EdwardsPoint compression", move |b| b.iter(|| B.compress()));
|
||||||
b.iter(|| B.compress())
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decompress(c: &mut Criterion) {
|
fn decompress(c: &mut Criterion) {
|
||||||
|
|
@ -55,25 +53,65 @@ mod edwards_benches {
|
||||||
|
|
||||||
fn vartime_double_base_scalar_mul(c: &mut Criterion) {
|
fn vartime_double_base_scalar_mul(c: &mut Criterion) {
|
||||||
c.bench_function("Variable-time aA+bB, A variable, B fixed", |bench| {
|
c.bench_function("Variable-time aA+bB, A variable, B fixed", |bench| {
|
||||||
let B = &constants::ED25519_BASEPOINT_POINT;
|
let mut rng = thread_rng();
|
||||||
let a = Scalar::from(298374928u64).invert();
|
let A = &Scalar::random(&mut rng) * &constants::ED25519_BASEPOINT_TABLE;
|
||||||
let b = Scalar::from(897987897u64).invert();
|
bench.iter_batched(
|
||||||
let A = B * (b * a);
|
|| (Scalar::random(&mut rng), Scalar::random(&mut rng)),
|
||||||
bench.iter(|| EdwardsPoint::vartime_double_scalar_mul_basepoint(&a, &A, &b));
|
|(a, b)| EdwardsPoint::vartime_double_scalar_mul_basepoint(&a, &A, &b),
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
criterion_group! {
|
||||||
|
name = edwards_benches;
|
||||||
|
config = Criterion::default();
|
||||||
|
targets =
|
||||||
|
compress,
|
||||||
|
decompress,
|
||||||
|
consttime_fixed_base_scalar_mul,
|
||||||
|
consttime_variable_base_scalar_mul,
|
||||||
|
vartime_double_base_scalar_mul,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod multiscalar_benches {
|
||||||
|
use super::*;
|
||||||
|
use curve25519_dalek::edwards;
|
||||||
|
use curve25519_dalek::edwards::EdwardsPoint;
|
||||||
|
use curve25519_dalek::edwards::VartimeEdwardsPrecomputation;
|
||||||
|
use curve25519_dalek::traits::MultiscalarMul;
|
||||||
|
use curve25519_dalek::traits::VartimeMultiscalarMul;
|
||||||
|
use curve25519_dalek::traits::VartimePrecomputedMultiscalarMul;
|
||||||
|
|
||||||
|
fn construct_scalars(n: usize) -> Vec<Scalar> {
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
(0..n).map(|_| Scalar::random(&mut rng)).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn construct_points(n: usize) -> Vec<EdwardsPoint> {
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
(0..n)
|
||||||
|
.map(|_| &Scalar::random(&mut rng) * &constants::ED25519_BASEPOINT_TABLE)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn construct(n: usize) -> (Vec<Scalar>, Vec<EdwardsPoint>) {
|
||||||
|
(construct_scalars(n), construct_points(n))
|
||||||
|
}
|
||||||
|
|
||||||
fn consttime_multiscalar_mul(c: &mut Criterion) {
|
fn consttime_multiscalar_mul(c: &mut Criterion) {
|
||||||
c.bench_function_over_inputs(
|
c.bench_function_over_inputs(
|
||||||
"Constant-time variable-base multiscalar multiplication",
|
"Constant-time variable-base multiscalar multiplication",
|
||||||
|b, &&size| {
|
|b, &&size| {
|
||||||
let mut rng = OsRng::new().unwrap();
|
let points = construct_points(size);
|
||||||
let scalars: Vec<Scalar> = (0..size).map(|_| Scalar::random(&mut rng)).collect();
|
// This is supposed to be constant-time, but we might as well
|
||||||
let points: Vec<EdwardsPoint> = scalars
|
// rerandomize the scalars for every call just in case.
|
||||||
.iter()
|
b.iter_batched(
|
||||||
.map(|s| s * &constants::ED25519_BASEPOINT_TABLE)
|
|| construct_scalars(size),
|
||||||
.collect();
|
|scalars| EdwardsPoint::multiscalar_mul(&scalars, &points),
|
||||||
b.iter(|| EdwardsPoint::multiscalar_mul(&scalars, &points));
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
&MULTISCALAR_SIZES,
|
&MULTISCALAR_SIZES,
|
||||||
);
|
);
|
||||||
|
|
@ -83,29 +121,107 @@ mod edwards_benches {
|
||||||
c.bench_function_over_inputs(
|
c.bench_function_over_inputs(
|
||||||
"Variable-time variable-base multiscalar multiplication",
|
"Variable-time variable-base multiscalar multiplication",
|
||||||
|b, &&size| {
|
|b, &&size| {
|
||||||
let mut rng = OsRng::new().unwrap();
|
let points = construct_points(size);
|
||||||
let scalars: Vec<Scalar> = (0..size).map(|_| Scalar::random(&mut rng)).collect();
|
// Rerandomize the scalars for every call to prevent
|
||||||
let points: Vec<EdwardsPoint> = scalars
|
// false timings from better caching (e.g., the CPU
|
||||||
.iter()
|
// cache lifts exactly the right table entries for the
|
||||||
.map(|s| s * &constants::ED25519_BASEPOINT_TABLE)
|
// benchmark into the highest cache levels).
|
||||||
.collect();
|
b.iter_batched(
|
||||||
b.iter(|| EdwardsPoint::vartime_multiscalar_mul(&scalars, &points));
|
|| construct_scalars(size),
|
||||||
|
|scalars| EdwardsPoint::vartime_multiscalar_mul(&scalars, &points),
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
&MULTISCALAR_SIZES,
|
&MULTISCALAR_SIZES,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
criterion_group!{
|
fn vartime_precomputed_pure_static(c: &mut Criterion) {
|
||||||
name = edwards_benches;
|
c.bench_function_over_inputs(
|
||||||
config = Criterion::default();
|
"Variable-time fixed-base multiscalar multiplication",
|
||||||
|
move |b, &&total_size| {
|
||||||
|
let static_size = total_size;
|
||||||
|
|
||||||
|
let static_points = construct_points(static_size);
|
||||||
|
let precomp = VartimeEdwardsPrecomputation::new(&static_points);
|
||||||
|
// Rerandomize the scalars for every call to prevent
|
||||||
|
// false timings from better caching (e.g., the CPU
|
||||||
|
// cache lifts exactly the right table entries for the
|
||||||
|
// benchmark into the highest cache levels).
|
||||||
|
b.iter_batched(
|
||||||
|
|| construct_scalars(static_size),
|
||||||
|
|scalars| precomp.vartime_multiscalar_mul(&scalars),
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
&MULTISCALAR_SIZES,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vartime_precomputed_helper(c: &mut Criterion, dynamic_fraction: f64) {
|
||||||
|
let label = format!(
|
||||||
|
"Variable-time mixed-base multiscalar multiplication ({:.0}pct dyn)",
|
||||||
|
100.0 * dynamic_fraction,
|
||||||
|
);
|
||||||
|
c.bench_function_over_inputs(
|
||||||
|
&label,
|
||||||
|
move |b, &&total_size| {
|
||||||
|
let dynamic_size = ((total_size as f64) * dynamic_fraction) as usize;
|
||||||
|
let static_size = total_size - dynamic_size;
|
||||||
|
|
||||||
|
let static_points = construct_points(static_size);
|
||||||
|
let dynamic_points = construct_points(dynamic_size);
|
||||||
|
let precomp = VartimeEdwardsPrecomputation::new(&static_points);
|
||||||
|
// Rerandomize the scalars for every call to prevent
|
||||||
|
// false timings from better caching (e.g., the CPU
|
||||||
|
// cache lifts exactly the right table entries for the
|
||||||
|
// benchmark into the highest cache levels). Timings
|
||||||
|
// should be independent of points so we don't
|
||||||
|
// randomize them.
|
||||||
|
b.iter_batched(
|
||||||
|
|| {
|
||||||
|
(
|
||||||
|
construct_scalars(static_size),
|
||||||
|
construct_scalars(dynamic_size),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|(static_scalars, dynamic_scalars)| {
|
||||||
|
precomp.vartime_mixed_multiscalar_mul(
|
||||||
|
&static_scalars,
|
||||||
|
&dynamic_scalars,
|
||||||
|
&dynamic_points,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
&MULTISCALAR_SIZES,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vartime_precomputed_00_pct_dynamic(c: &mut Criterion) {
|
||||||
|
vartime_precomputed_helper(c, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vartime_precomputed_20_pct_dynamic(c: &mut Criterion) {
|
||||||
|
vartime_precomputed_helper(c, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vartime_precomputed_50_pct_dynamic(c: &mut Criterion) {
|
||||||
|
vartime_precomputed_helper(c, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group! {
|
||||||
|
name = multiscalar_benches;
|
||||||
|
// Lower the sample size to run the benchmarks faster
|
||||||
|
config = Criterion::default().sample_size(15);
|
||||||
targets =
|
targets =
|
||||||
compress,
|
|
||||||
decompress,
|
|
||||||
consttime_fixed_base_scalar_mul,
|
|
||||||
consttime_variable_base_scalar_mul,
|
|
||||||
vartime_double_base_scalar_mul,
|
|
||||||
consttime_multiscalar_mul,
|
consttime_multiscalar_mul,
|
||||||
vartime_multiscalar_mul,
|
vartime_multiscalar_mul,
|
||||||
|
vartime_precomputed_pure_static,
|
||||||
|
vartime_precomputed_00_pct_dynamic,
|
||||||
|
vartime_precomputed_20_pct_dynamic,
|
||||||
|
vartime_precomputed_50_pct_dynamic,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,7 +257,7 @@ mod ristretto_benches {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
criterion_group!{
|
criterion_group! {
|
||||||
name = ristretto_benches;
|
name = ristretto_benches;
|
||||||
config = Criterion::default();
|
config = Criterion::default();
|
||||||
targets =
|
targets =
|
||||||
|
|
@ -162,7 +278,7 @@ mod montgomery_benches {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
criterion_group!{
|
criterion_group! {
|
||||||
name = montgomery_benches;
|
name = montgomery_benches;
|
||||||
config = Criterion::default();
|
config = Criterion::default();
|
||||||
targets = montgomery_ladder,
|
targets = montgomery_ladder,
|
||||||
|
|
@ -194,7 +310,7 @@ mod scalar_benches {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
criterion_group!{
|
criterion_group! {
|
||||||
name = scalar_benches;
|
name = scalar_benches;
|
||||||
config = Criterion::default();
|
config = Criterion::default();
|
||||||
targets =
|
targets =
|
||||||
|
|
@ -208,4 +324,5 @@ criterion_main!(
|
||||||
montgomery_benches::montgomery_benches,
|
montgomery_benches::montgomery_benches,
|
||||||
ristretto_benches::ristretto_benches,
|
ristretto_benches::ristretto_benches,
|
||||||
edwards_benches::edwards_benches,
|
edwards_benches::edwards_benches,
|
||||||
|
multiscalar_benches::multiscalar_benches,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -21,4 +21,8 @@ pub mod variable_base;
|
||||||
#[cfg(feature = "stage2_build")]
|
#[cfg(feature = "stage2_build")]
|
||||||
pub mod vartime_double_base;
|
pub mod vartime_double_base;
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
pub mod straus;
|
pub mod straus;
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub mod precomputed_straus;
|
||||||
|
|
|
||||||
114
src/backend/serial/scalar_mul/precomputed_straus.rs
Normal file
114
src/backend/serial/scalar_mul/precomputed_straus.rs
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
// -*- mode: rust; -*-
|
||||||
|
//
|
||||||
|
// This file is part of curve25519-dalek.
|
||||||
|
// Copyright (c) 2019 Henry de Valence.
|
||||||
|
// See LICENSE for licensing information.
|
||||||
|
//
|
||||||
|
// Authors:
|
||||||
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||||
|
|
||||||
|
//! Precomputation for Straus's method.
|
||||||
|
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
use core::borrow::Borrow;
|
||||||
|
|
||||||
|
use backend::serial::curve_models::{
|
||||||
|
AffineNielsPoint, CompletedPoint, ProjectiveNielsPoint, ProjectivePoint,
|
||||||
|
};
|
||||||
|
use edwards::EdwardsPoint;
|
||||||
|
use scalar::Scalar;
|
||||||
|
use traits::Identity;
|
||||||
|
use traits::VartimePrecomputedMultiscalarMul;
|
||||||
|
use window::{NafLookupTable5, NafLookupTable8};
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use prelude::*;
|
||||||
|
|
||||||
|
pub struct VartimePrecomputedStraus {
|
||||||
|
static_lookup_tables: Vec<NafLookupTable8<AffineNielsPoint>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus {
|
||||||
|
type Point = EdwardsPoint;
|
||||||
|
|
||||||
|
fn new<I>(static_points: I) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Self::Point>,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
static_lookup_tables: static_points
|
||||||
|
.into_iter()
|
||||||
|
.map(|P| NafLookupTable8::<AffineNielsPoint>::from(P.borrow()))
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn optional_mixed_multiscalar_mul<I, J, K>(
|
||||||
|
&self,
|
||||||
|
static_scalars: I,
|
||||||
|
dynamic_scalars: J,
|
||||||
|
dynamic_points: K,
|
||||||
|
) -> Option<Self::Point>
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Scalar>,
|
||||||
|
J: IntoIterator,
|
||||||
|
J::Item: Borrow<Scalar>,
|
||||||
|
K: IntoIterator<Item = Option<Self::Point>>,
|
||||||
|
{
|
||||||
|
let static_nafs = static_scalars
|
||||||
|
.into_iter()
|
||||||
|
.map(|c| c.borrow().non_adjacent_form(5))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let dynamic_nafs: Vec<_> = dynamic_scalars
|
||||||
|
.into_iter()
|
||||||
|
.map(|c| c.borrow().non_adjacent_form(5))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let dynamic_lookup_tables = match dynamic_points
|
||||||
|
.into_iter()
|
||||||
|
.map(|P_opt| P_opt.map(|P| NafLookupTable5::<ProjectiveNielsPoint>::from(&P)))
|
||||||
|
.collect::<Option<Vec<_>>>()
|
||||||
|
{
|
||||||
|
Some(x) => x,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let sp = self.static_lookup_tables.len();
|
||||||
|
let dp = dynamic_lookup_tables.len();
|
||||||
|
assert_eq!(sp, static_nafs.len());
|
||||||
|
assert_eq!(dp, dynamic_nafs.len());
|
||||||
|
|
||||||
|
// We could save some doublings by looking for the highest
|
||||||
|
// nonzero NAF coefficient, but since we might have a lot of
|
||||||
|
// them to search, it's not clear it's worthwhile to check.
|
||||||
|
let mut S = ProjectivePoint::identity();
|
||||||
|
for j in (0..255).rev() {
|
||||||
|
let mut R: CompletedPoint = S.double();
|
||||||
|
|
||||||
|
for i in 0..dp {
|
||||||
|
let t_ij = dynamic_nafs[i][j];
|
||||||
|
if t_ij > 0 {
|
||||||
|
R = &R.to_extended() + &dynamic_lookup_tables[i].select(t_ij as usize);
|
||||||
|
} else if t_ij < 0 {
|
||||||
|
R = &R.to_extended() - &dynamic_lookup_tables[i].select(-t_ij as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..sp {
|
||||||
|
let t_ij = static_nafs[i][j];
|
||||||
|
if t_ij > 0 {
|
||||||
|
R = &R.to_extended() + &self.static_lookup_tables[i].select(t_ij as usize);
|
||||||
|
} else if t_ij < 0 {
|
||||||
|
R = &R.to_extended() - &self.static_lookup_tables[i].select(-t_ij as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
S = R.to_projective();
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(S.to_extended())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,16 +12,11 @@
|
||||||
|
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
|
||||||
use core::borrow::Borrow;
|
use core::borrow::Borrow;
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
|
||||||
use edwards::EdwardsPoint;
|
use edwards::EdwardsPoint;
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
|
||||||
use scalar::Scalar;
|
use scalar::Scalar;
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
|
||||||
use traits::MultiscalarMul;
|
use traits::MultiscalarMul;
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
|
||||||
use traits::VartimeMultiscalarMul;
|
use traits::VartimeMultiscalarMul;
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
|
|
@ -48,10 +43,8 @@ use prelude::*;
|
||||||
///
|
///
|
||||||
/// [solution]: https://www.jstor.org/stable/2310929
|
/// [solution]: https://www.jstor.org/stable/2310929
|
||||||
/// [problem]: https://www.jstor.org/stable/2312273
|
/// [problem]: https://www.jstor.org/stable/2312273
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
|
||||||
pub struct Straus {}
|
pub struct Straus {}
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
impl MultiscalarMul for Straus {
|
impl MultiscalarMul for Straus {
|
||||||
type Point = EdwardsPoint;
|
type Point = EdwardsPoint;
|
||||||
|
|
||||||
|
|
@ -148,7 +141,6 @@ impl MultiscalarMul for Straus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
impl VartimeMultiscalarMul for Straus {
|
impl VartimeMultiscalarMul for Straus {
|
||||||
type Point = EdwardsPoint;
|
type Point = EdwardsPoint;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ pub(crate) static CACHEDPOINT_IDENTITY: CachedPoint = CachedPoint(FieldElement26
|
||||||
]));
|
]));
|
||||||
|
|
||||||
/// The low limbs of (2p, 2p, 2p, 2p), so that
|
/// The low limbs of (2p, 2p, 2p, 2p), so that
|
||||||
/// ```no_run
|
/// ```ascii,no_run
|
||||||
/// (2p, 2p, 2p, 2p) = [P_TIMES_2_LO, P_TIMES_2_HI, P_TIMES_2_HI, P_TIMES_2_HI, P_TIMES_2_HI]
|
/// (2p, 2p, 2p, 2p) = [P_TIMES_2_LO, P_TIMES_2_HI, P_TIMES_2_HI, P_TIMES_2_HI, P_TIMES_2_HI]
|
||||||
/// ```
|
/// ```
|
||||||
pub(crate) static P_TIMES_2_LO: u32x8 = u32x8::new(
|
pub(crate) static P_TIMES_2_LO: u32x8 = u32x8::new(
|
||||||
|
|
@ -50,7 +50,7 @@ pub(crate) static P_TIMES_2_LO: u32x8 = u32x8::new(
|
||||||
);
|
);
|
||||||
|
|
||||||
/// The high limbs of (2p, 2p, 2p, 2p), so that
|
/// The high limbs of (2p, 2p, 2p, 2p), so that
|
||||||
/// ```no_run
|
/// ```ascii,no_run
|
||||||
/// (2p, 2p, 2p, 2p) = [P_TIMES_2_LO, P_TIMES_2_HI, P_TIMES_2_HI, P_TIMES_2_HI, P_TIMES_2_HI]
|
/// (2p, 2p, 2p, 2p) = [P_TIMES_2_LO, P_TIMES_2_HI, P_TIMES_2_HI, P_TIMES_2_HI, P_TIMES_2_HI]
|
||||||
/// ```
|
/// ```
|
||||||
pub(crate) static P_TIMES_2_HI: u32x8 = u32x8::new(
|
pub(crate) static P_TIMES_2_HI: u32x8 = u32x8::new(
|
||||||
|
|
@ -65,7 +65,7 @@ pub(crate) static P_TIMES_2_HI: u32x8 = u32x8::new(
|
||||||
);
|
);
|
||||||
|
|
||||||
/// The low limbs of (16p, 16p, 16p, 16p), so that
|
/// The low limbs of (16p, 16p, 16p, 16p), so that
|
||||||
/// ```no_run
|
/// ```ascii,no_run
|
||||||
/// (16p, 16p, 16p, 16p) = [P_TIMES_16_LO, P_TIMES_16_HI, P_TIMES_16_HI, P_TIMES_16_HI, P_TIMES_16_HI]
|
/// (16p, 16p, 16p, 16p) = [P_TIMES_16_LO, P_TIMES_16_HI, P_TIMES_16_HI, P_TIMES_16_HI, P_TIMES_16_HI]
|
||||||
/// ```
|
/// ```
|
||||||
pub(crate) static P_TIMES_16_LO: u32x8 = u32x8::new(
|
pub(crate) static P_TIMES_16_LO: u32x8 = u32x8::new(
|
||||||
|
|
@ -80,7 +80,7 @@ pub(crate) static P_TIMES_16_LO: u32x8 = u32x8::new(
|
||||||
);
|
);
|
||||||
|
|
||||||
/// The high limbs of (16p, 16p, 16p, 16p), so that
|
/// The high limbs of (16p, 16p, 16p, 16p), so that
|
||||||
/// ```no_run
|
/// ```ascii,no_run
|
||||||
/// (16p, 16p, 16p, 16p) = [P_TIMES_16_LO, P_TIMES_16_HI, P_TIMES_16_HI, P_TIMES_16_HI, P_TIMES_16_HI]
|
/// (16p, 16p, 16p, 16p) = [P_TIMES_16_LO, P_TIMES_16_HI, P_TIMES_16_HI, P_TIMES_16_HI, P_TIMES_16_HI]
|
||||||
/// ```
|
/// ```
|
||||||
pub(crate) static P_TIMES_16_HI: u32x8 = u32x8::new(
|
pub(crate) static P_TIMES_16_HI: u32x8 = u32x8::new(
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,11 @@ use backend::vector::avx2::constants::{P_TIMES_16_HI, P_TIMES_16_LO, P_TIMES_2_H
|
||||||
use backend::serial::u64::field::FieldElement51;
|
use backend::serial::u64::field::FieldElement51;
|
||||||
|
|
||||||
/// Unpack 32-bit lanes into 64-bit lanes:
|
/// Unpack 32-bit lanes into 64-bit lanes:
|
||||||
/// ```
|
/// ```ascii,no_run
|
||||||
/// (a0, b0, a1, b1, c0, d0, c1, d1)
|
/// (a0, b0, a1, b1, c0, d0, c1, d1)
|
||||||
/// ```
|
/// ```
|
||||||
/// into
|
/// into
|
||||||
/// ```
|
/// ```ascii,no_run
|
||||||
/// (a0, 0, b0, 0, c0, 0, d0, 0)
|
/// (a0, 0, b0, 0, c0, 0, d0, 0)
|
||||||
/// (a1, 0, b1, 0, c1, 0, d1, 0)
|
/// (a1, 0, b1, 0, c1, 0, d1, 0)
|
||||||
/// ```
|
/// ```
|
||||||
|
|
@ -69,12 +69,12 @@ fn unpack_pair(src: u32x8) -> (u32x8, u32x8) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Repack 64-bit lanes into 32-bit lanes:
|
/// Repack 64-bit lanes into 32-bit lanes:
|
||||||
/// ```
|
/// ```ascii,no_run
|
||||||
/// (a0, 0, b0, 0, c0, 0, d0, 0)
|
/// (a0, 0, b0, 0, c0, 0, d0, 0)
|
||||||
/// (a1, 0, b1, 0, c1, 0, d1, 0)
|
/// (a1, 0, b1, 0, c1, 0, d1, 0)
|
||||||
/// ```
|
/// ```
|
||||||
/// into
|
/// into
|
||||||
/// ```
|
/// ```ascii,no_run
|
||||||
/// (a0, b0, a1, b1, c0, d0, c1, d1)
|
/// (a0, b0, a1, b1, c0, d0, c1, d1)
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
|
||||||
|
|
@ -12,4 +12,8 @@ pub mod variable_base;
|
||||||
|
|
||||||
pub mod vartime_double_base;
|
pub mod vartime_double_base;
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
pub mod straus;
|
pub mod straus;
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub mod precomputed_straus;
|
||||||
|
|
|
||||||
111
src/backend/vector/scalar_mul/precomputed_straus.rs
Normal file
111
src/backend/vector/scalar_mul/precomputed_straus.rs
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
// -*- mode: rust; -*-
|
||||||
|
//
|
||||||
|
// This file is part of curve25519-dalek.
|
||||||
|
// Copyright (c) 2019 Henry de Valence.
|
||||||
|
// See LICENSE for licensing information.
|
||||||
|
//
|
||||||
|
// Authors:
|
||||||
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||||
|
|
||||||
|
//! Precomputation for Straus's method.
|
||||||
|
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
use core::borrow::Borrow;
|
||||||
|
|
||||||
|
use backend::vector::{CachedPoint, ExtendedPoint};
|
||||||
|
use edwards::EdwardsPoint;
|
||||||
|
use scalar::Scalar;
|
||||||
|
use traits::Identity;
|
||||||
|
use traits::VartimePrecomputedMultiscalarMul;
|
||||||
|
use window::{NafLookupTable5, NafLookupTable8};
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use prelude::*;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct VartimePrecomputedStraus {
|
||||||
|
static_lookup_tables: Vec<NafLookupTable8<CachedPoint>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus {
|
||||||
|
type Point = EdwardsPoint;
|
||||||
|
|
||||||
|
fn new<I>(static_points: I) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Self::Point>,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
static_lookup_tables: static_points
|
||||||
|
.into_iter()
|
||||||
|
.map(|P| NafLookupTable8::<CachedPoint>::from(P.borrow()))
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn optional_mixed_multiscalar_mul<I, J, K>(
|
||||||
|
&self,
|
||||||
|
static_scalars: I,
|
||||||
|
dynamic_scalars: J,
|
||||||
|
dynamic_points: K,
|
||||||
|
) -> Option<Self::Point>
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Scalar>,
|
||||||
|
J: IntoIterator,
|
||||||
|
J::Item: Borrow<Scalar>,
|
||||||
|
K: IntoIterator<Item = Option<Self::Point>>,
|
||||||
|
{
|
||||||
|
let static_nafs = static_scalars
|
||||||
|
.into_iter()
|
||||||
|
.map(|c| c.borrow().non_adjacent_form(5))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let dynamic_nafs: Vec<_> = dynamic_scalars
|
||||||
|
.into_iter()
|
||||||
|
.map(|c| c.borrow().non_adjacent_form(5))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let dynamic_lookup_tables = match dynamic_points
|
||||||
|
.into_iter()
|
||||||
|
.map(|P_opt| P_opt.map(|P| NafLookupTable5::<CachedPoint>::from(&P)))
|
||||||
|
.collect::<Option<Vec<_>>>()
|
||||||
|
{
|
||||||
|
Some(x) => x,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let sp = self.static_lookup_tables.len();
|
||||||
|
let dp = dynamic_lookup_tables.len();
|
||||||
|
assert_eq!(sp, static_nafs.len());
|
||||||
|
assert_eq!(dp, dynamic_nafs.len());
|
||||||
|
|
||||||
|
// We could save some doublings by looking for the highest
|
||||||
|
// nonzero NAF coefficient, but since we might have a lot of
|
||||||
|
// them to search, it's not clear it's worthwhile to check.
|
||||||
|
let mut R = ExtendedPoint::identity();
|
||||||
|
for j in (0..255).rev() {
|
||||||
|
R = R.double();
|
||||||
|
|
||||||
|
for i in 0..dp {
|
||||||
|
let t_ij = dynamic_nafs[i][j];
|
||||||
|
if t_ij > 0 {
|
||||||
|
R = &R + &dynamic_lookup_tables[i].select(t_ij as usize);
|
||||||
|
} else if t_ij < 0 {
|
||||||
|
R = &R - &dynamic_lookup_tables[i].select(-t_ij as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..sp {
|
||||||
|
let t_ij = static_nafs[i][j];
|
||||||
|
if t_ij > 0 {
|
||||||
|
R = &R + &self.static_lookup_tables[i].select(t_ij as usize);
|
||||||
|
} else if t_ij < 0 {
|
||||||
|
R = &R - &self.static_lookup_tables[i].select(-t_ij as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(R.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -33,7 +33,6 @@ use prelude::*;
|
||||||
/// point representation on the fly.
|
/// point representation on the fly.
|
||||||
pub struct Straus {}
|
pub struct Straus {}
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
impl MultiscalarMul for Straus {
|
impl MultiscalarMul for Straus {
|
||||||
type Point = EdwardsPoint;
|
type Point = EdwardsPoint;
|
||||||
|
|
||||||
|
|
@ -71,7 +70,6 @@ impl MultiscalarMul for Straus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
impl VartimeMultiscalarMul for Straus {
|
impl VartimeMultiscalarMul for Straus {
|
||||||
type Point = EdwardsPoint;
|
type Point = EdwardsPoint;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -112,23 +112,23 @@ use scalar::Scalar;
|
||||||
|
|
||||||
use montgomery::MontgomeryPoint;
|
use montgomery::MontgomeryPoint;
|
||||||
|
|
||||||
use backend::serial::curve_models::ProjectivePoint;
|
|
||||||
use backend::serial::curve_models::CompletedPoint;
|
|
||||||
use backend::serial::curve_models::AffineNielsPoint;
|
use backend::serial::curve_models::AffineNielsPoint;
|
||||||
|
use backend::serial::curve_models::CompletedPoint;
|
||||||
use backend::serial::curve_models::ProjectiveNielsPoint;
|
use backend::serial::curve_models::ProjectiveNielsPoint;
|
||||||
|
use backend::serial::curve_models::ProjectivePoint;
|
||||||
|
|
||||||
use window::LookupTable;
|
use window::LookupTable;
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
|
|
||||||
use traits::{Identity, IsIdentity};
|
|
||||||
use traits::ValidityCheck;
|
use traits::ValidityCheck;
|
||||||
|
use traits::{Identity, IsIdentity};
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
use traits::MultiscalarMul;
|
use traits::MultiscalarMul;
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
use traits::VartimeMultiscalarMul;
|
use traits::{VartimeMultiscalarMul, VartimePrecomputedMultiscalarMul};
|
||||||
|
|
||||||
#[cfg(not(all(
|
#[cfg(not(all(
|
||||||
feature = "simd_backend",
|
feature = "simd_backend",
|
||||||
|
|
@ -677,6 +677,43 @@ impl VartimeMultiscalarMul for EdwardsPoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Precomputation for variable-time multiscalar multiplication with `EdwardsPoint`s.
|
||||||
|
// This wraps the inner implementation in a facade type so that we can
|
||||||
|
// decouple stability of the inner type from the stability of the
|
||||||
|
// outer type.
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub struct VartimeEdwardsPrecomputation(scalar_mul::precomputed_straus::VartimePrecomputedStraus);
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl VartimePrecomputedMultiscalarMul for VartimeEdwardsPrecomputation {
|
||||||
|
type Point = EdwardsPoint;
|
||||||
|
|
||||||
|
fn new<I>(static_points: I) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Self::Point>,
|
||||||
|
{
|
||||||
|
Self(scalar_mul::precomputed_straus::VartimePrecomputedStraus::new(static_points))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn optional_mixed_multiscalar_mul<I, J, K>(
|
||||||
|
&self,
|
||||||
|
static_scalars: I,
|
||||||
|
dynamic_scalars: J,
|
||||||
|
dynamic_points: K,
|
||||||
|
) -> Option<Self::Point>
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Scalar>,
|
||||||
|
J: IntoIterator,
|
||||||
|
J::Item: Borrow<Scalar>,
|
||||||
|
K: IntoIterator<Item = Option<Self::Point>>,
|
||||||
|
{
|
||||||
|
self.0
|
||||||
|
.optional_mixed_multiscalar_mul(static_scalars, dynamic_scalars, dynamic_points)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl EdwardsPoint {
|
impl EdwardsPoint {
|
||||||
/// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint.
|
/// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint.
|
||||||
#[cfg(feature = "stage2_build")]
|
#[cfg(feature = "stage2_build")]
|
||||||
|
|
@ -1207,6 +1244,49 @@ mod test {
|
||||||
assert!(P1.compress().to_bytes() == P2.compress().to_bytes());
|
assert!(P1.compress().to_bytes() == P2.compress().to_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vartime_precomputed_vs_nonprecomputed_multiscalar() {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
|
let B = &::constants::ED25519_BASEPOINT_TABLE;
|
||||||
|
|
||||||
|
let static_scalars = (0..128)
|
||||||
|
.map(|_| Scalar::random(&mut rng))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let dynamic_scalars = (0..128)
|
||||||
|
.map(|_| Scalar::random(&mut rng))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let check_scalar: Scalar = static_scalars
|
||||||
|
.iter()
|
||||||
|
.chain(dynamic_scalars.iter())
|
||||||
|
.map(|s| s * s)
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
let static_points = static_scalars.iter().map(|s| s * B).collect::<Vec<_>>();
|
||||||
|
let dynamic_points = dynamic_scalars.iter().map(|s| s * B).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let precomputation = VartimeEdwardsPrecomputation::new(static_points.iter());
|
||||||
|
|
||||||
|
let P = precomputation.vartime_mixed_multiscalar_mul(
|
||||||
|
&static_scalars,
|
||||||
|
&dynamic_scalars,
|
||||||
|
&dynamic_points,
|
||||||
|
);
|
||||||
|
|
||||||
|
use traits::VartimeMultiscalarMul;
|
||||||
|
let Q = EdwardsPoint::vartime_multiscalar_mul(
|
||||||
|
static_scalars.iter().chain(dynamic_scalars.iter()),
|
||||||
|
static_points.iter().chain(dynamic_points.iter()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let R = &check_scalar * B;
|
||||||
|
|
||||||
|
assert_eq!(P.compress(), R.compress());
|
||||||
|
assert_eq!(Q.compress(), R.compress());
|
||||||
|
}
|
||||||
|
|
||||||
mod vartime {
|
mod vartime {
|
||||||
use super::super::*;
|
use super::super::*;
|
||||||
use super::{A_SCALAR, B_SCALAR, A_TIMES_BASEPOINT, DOUBLE_SCALAR_MULT_RESULT};
|
use super::{A_SCALAR, B_SCALAR, A_TIMES_BASEPOINT, DOUBLE_SCALAR_MULT_RESULT};
|
||||||
|
|
|
||||||
105
src/ristretto.rs
105
src/ristretto.rs
|
|
@ -187,7 +187,18 @@ use scalar::Scalar;
|
||||||
|
|
||||||
use traits::Identity;
|
use traits::Identity;
|
||||||
#[cfg(any(feature = "alloc", feature = "std"))]
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
use traits::{MultiscalarMul, VartimeMultiscalarMul};
|
use traits::{MultiscalarMul, VartimeMultiscalarMul, VartimePrecomputedMultiscalarMul};
|
||||||
|
|
||||||
|
#[cfg(not(all(
|
||||||
|
feature = "simd_backend",
|
||||||
|
any(target_feature = "avx2", target_feature = "avx512ifma")
|
||||||
|
)))]
|
||||||
|
use backend::serial::scalar_mul;
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "simd_backend",
|
||||||
|
any(target_feature = "avx2", target_feature = "avx512ifma")
|
||||||
|
))]
|
||||||
|
use backend::vector::scalar_mul;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Compressed points
|
// Compressed points
|
||||||
|
|
@ -891,8 +902,53 @@ impl VartimeMultiscalarMul for RistrettoPoint {
|
||||||
{
|
{
|
||||||
let extended_points = points.into_iter().map(|opt_P| opt_P.map(|P| P.borrow().0));
|
let extended_points = points.into_iter().map(|opt_P| opt_P.map(|P| P.borrow().0));
|
||||||
|
|
||||||
EdwardsPoint::optional_multiscalar_mul(scalars, extended_points)
|
EdwardsPoint::optional_multiscalar_mul(scalars, extended_points).map(|P| RistrettoPoint(P))
|
||||||
.map(|P| RistrettoPoint(P))
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Precomputation for variable-time multiscalar multiplication with `RistrettoPoint`s.
|
||||||
|
// This wraps the inner implementation in a facade type so that we can
|
||||||
|
// decouple stability of the inner type from the stability of the
|
||||||
|
// outer type.
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub struct VartimeRistrettoPrecomputation(scalar_mul::precomputed_straus::VartimePrecomputedStraus);
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl VartimePrecomputedMultiscalarMul for VartimeRistrettoPrecomputation {
|
||||||
|
type Point = RistrettoPoint;
|
||||||
|
|
||||||
|
fn new<I>(static_points: I) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Self::Point>,
|
||||||
|
{
|
||||||
|
Self(
|
||||||
|
scalar_mul::precomputed_straus::VartimePrecomputedStraus::new(
|
||||||
|
static_points.into_iter().map(|P| P.borrow().0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn optional_mixed_multiscalar_mul<I, J, K>(
|
||||||
|
&self,
|
||||||
|
static_scalars: I,
|
||||||
|
dynamic_scalars: J,
|
||||||
|
dynamic_points: K,
|
||||||
|
) -> Option<Self::Point>
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Scalar>,
|
||||||
|
J: IntoIterator,
|
||||||
|
J::Item: Borrow<Scalar>,
|
||||||
|
K: IntoIterator<Item = Option<Self::Point>>,
|
||||||
|
{
|
||||||
|
self.0
|
||||||
|
.optional_mixed_multiscalar_mul(
|
||||||
|
static_scalars,
|
||||||
|
dynamic_scalars,
|
||||||
|
dynamic_points.into_iter().map(|P_opt| P_opt.map(|P| P.0)),
|
||||||
|
)
|
||||||
|
.map(|P_ed| RistrettoPoint(P_ed))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1263,4 +1319,47 @@ mod test {
|
||||||
P.compress();
|
P.compress();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vartime_precomputed_vs_nonprecomputed_multiscalar() {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
|
let B = &::constants::RISTRETTO_BASEPOINT_TABLE;
|
||||||
|
|
||||||
|
let static_scalars = (0..128)
|
||||||
|
.map(|_| Scalar::random(&mut rng))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let dynamic_scalars = (0..128)
|
||||||
|
.map(|_| Scalar::random(&mut rng))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let check_scalar: Scalar = static_scalars
|
||||||
|
.iter()
|
||||||
|
.chain(dynamic_scalars.iter())
|
||||||
|
.map(|s| s * s)
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
let static_points = static_scalars.iter().map(|s| s * B).collect::<Vec<_>>();
|
||||||
|
let dynamic_points = dynamic_scalars.iter().map(|s| s * B).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let precomputation = VartimeRistrettoPrecomputation::new(static_points.iter());
|
||||||
|
|
||||||
|
let P = precomputation.vartime_mixed_multiscalar_mul(
|
||||||
|
&static_scalars,
|
||||||
|
&dynamic_scalars,
|
||||||
|
&dynamic_points,
|
||||||
|
);
|
||||||
|
|
||||||
|
use traits::VartimeMultiscalarMul;
|
||||||
|
let Q = RistrettoPoint::vartime_multiscalar_mul(
|
||||||
|
static_scalars.iter().chain(dynamic_scalars.iter()),
|
||||||
|
static_points.iter().chain(dynamic_points.iter()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let R = &check_scalar * B;
|
||||||
|
|
||||||
|
assert_eq!(P.compress(), R.compress());
|
||||||
|
assert_eq!(Q.compress(), R.compress());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
142
src/traits.rs
142
src/traits.rs
|
|
@ -219,11 +219,149 @@ pub trait VartimeMultiscalarMul {
|
||||||
{
|
{
|
||||||
Self::optional_multiscalar_mul(
|
Self::optional_multiscalar_mul(
|
||||||
scalars,
|
scalars,
|
||||||
points.into_iter().map(|P| Some(P.borrow().clone()))
|
points.into_iter().map(|P| Some(P.borrow().clone())),
|
||||||
).unwrap()
|
)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A trait for variable-time multiscalar multiplication with precomputation.
|
||||||
|
///
|
||||||
|
/// A general multiscalar multiplication with precomputation can be written as
|
||||||
|
/// $$
|
||||||
|
/// Q = a_1 A_1 + \cdots + a_n A_n + b_1 B_1 + \cdots + b_m B_m,
|
||||||
|
/// $$
|
||||||
|
/// where the \\(B_i\\) are *static* points, for which precomputation
|
||||||
|
/// is possible, and the \\(A_j\\) are *dynamic* points, for which
|
||||||
|
/// precomputation is not possible.
|
||||||
|
///
|
||||||
|
/// This trait has three methods for performing this computation:
|
||||||
|
///
|
||||||
|
/// * [`vartime_multiscalar_mul`], which handles the special case
|
||||||
|
/// where \\(n = 0\\) and there are no dynamic points;
|
||||||
|
///
|
||||||
|
/// * [`vartime_mixed_multiscalar_mul`], which takes the dynamic
|
||||||
|
/// points as already-validated `Point`s and is infallible;
|
||||||
|
///
|
||||||
|
/// * [`optional_mixed_multiscalar_mul`], which takes the dynamic
|
||||||
|
/// points as `Option<Point>`s and returns an `Option<Point>`,
|
||||||
|
/// allowing decompression to be composed into the input iterators.
|
||||||
|
///
|
||||||
|
/// All methods require that the lengths of the input iterators be
|
||||||
|
/// known and matching, as if they were `ExactSizeIterator`s. (It
|
||||||
|
/// does not require `ExactSizeIterator` only because that trait is
|
||||||
|
/// broken).
|
||||||
|
pub trait VartimePrecomputedMultiscalarMul: Sized {
|
||||||
|
/// The type of point to be multiplied, e.g., `RistrettoPoint`.
|
||||||
|
type Point: Clone;
|
||||||
|
|
||||||
|
/// Given the static points \\( B_i \\), perform precomputation
|
||||||
|
/// and return the precomputation data.
|
||||||
|
fn new<I>(static_points: I) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Self::Point>;
|
||||||
|
|
||||||
|
/// Given `static_scalars`, an iterator of public scalars
|
||||||
|
/// \\(b_i\\), compute
|
||||||
|
/// $$
|
||||||
|
/// Q = b_1 B_1 + \cdots + b_m B_m,
|
||||||
|
/// $$
|
||||||
|
/// where the \\(B_j\\) are the points that were supplied to `new`.
|
||||||
|
///
|
||||||
|
/// It is an error to call this function with iterators of
|
||||||
|
/// inconsistent lengths.
|
||||||
|
///
|
||||||
|
/// The trait bound aims for maximum flexibility: the input must
|
||||||
|
/// be convertable to iterators (`I: IntoIter`), and the
|
||||||
|
/// iterator's items must be `Borrow<Scalar>`, to allow iterators
|
||||||
|
/// returning either `Scalar`s or `&Scalar`s.
|
||||||
|
fn vartime_multiscalar_mul<I>(&self, static_scalars: I) -> Self::Point
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Scalar>,
|
||||||
|
{
|
||||||
|
use core::iter;
|
||||||
|
|
||||||
|
Self::vartime_mixed_multiscalar_mul(
|
||||||
|
self,
|
||||||
|
static_scalars,
|
||||||
|
iter::empty::<Scalar>(),
|
||||||
|
iter::empty::<Self::Point>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given `static_scalars`, an iterator of public scalars
|
||||||
|
/// \\(b_i\\), `dynamic_scalars`, an iterator of public scalars
|
||||||
|
/// \\(a_i\\), and `dynamic_points`, an iterator of points
|
||||||
|
/// \\(A_i\\), compute
|
||||||
|
/// $$
|
||||||
|
/// Q = a_1 A_1 + \cdots + a_n A_n + b_1 B_1 + \cdots + b_m B_m,
|
||||||
|
/// $$
|
||||||
|
/// where the \\(B_j\\) are the points that were supplied to `new`.
|
||||||
|
///
|
||||||
|
/// It is an error to call this function with iterators of
|
||||||
|
/// inconsistent lengths.
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
fn vartime_mixed_multiscalar_mul<I, J, K>(
|
||||||
|
&self,
|
||||||
|
static_scalars: I,
|
||||||
|
dynamic_scalars: J,
|
||||||
|
dynamic_points: K,
|
||||||
|
) -> Self::Point
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Scalar>,
|
||||||
|
J: IntoIterator,
|
||||||
|
J::Item: Borrow<Scalar>,
|
||||||
|
K: IntoIterator,
|
||||||
|
K::Item: Borrow<Self::Point>,
|
||||||
|
{
|
||||||
|
Self::optional_mixed_multiscalar_mul(
|
||||||
|
self,
|
||||||
|
static_scalars,
|
||||||
|
dynamic_scalars,
|
||||||
|
dynamic_points.into_iter().map(|P| Some(P.borrow().clone())),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given `static_scalars`, an iterator of public scalars
|
||||||
|
/// \\(b_i\\), `dynamic_scalars`, an iterator of public scalars
|
||||||
|
/// \\(a_i\\), and `dynamic_points`, an iterator of points
|
||||||
|
/// \\(A_i\\), compute
|
||||||
|
/// $$
|
||||||
|
/// Q = a_1 A_1 + \cdots + a_n A_n + b_1 B_1 + \cdots + b_m B_m,
|
||||||
|
/// $$
|
||||||
|
/// where the \\(B_j\\) are the points that were supplied to `new`.
|
||||||
|
///
|
||||||
|
/// If any of the dynamic points were `None`, return `None`.
|
||||||
|
///
|
||||||
|
/// It is an error to call this function with iterators of
|
||||||
|
/// inconsistent lengths.
|
||||||
|
///
|
||||||
|
/// This function is particularly useful when verifying statements
|
||||||
|
/// involving compressed points. Accepting `Option<Point>` allows
|
||||||
|
/// inlining point decompression into the multiscalar call,
|
||||||
|
/// avoiding the need for temporary buffers.
|
||||||
|
fn optional_mixed_multiscalar_mul<I, J, K>(
|
||||||
|
&self,
|
||||||
|
static_scalars: I,
|
||||||
|
dynamic_scalars: J,
|
||||||
|
dynamic_points: K,
|
||||||
|
) -> Option<Self::Point>
|
||||||
|
where
|
||||||
|
I: IntoIterator,
|
||||||
|
I::Item: Borrow<Scalar>,
|
||||||
|
J: IntoIterator,
|
||||||
|
J::Item: Borrow<Scalar>,
|
||||||
|
K: IntoIterator<Item = Option<Self::Point>>;
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Private Traits
|
// Private Traits
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue