mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-04 20:24:10 +00:00
curve: Support MSM #static scalars <= #static points (#668)
This commit is contained in:
parent
83a57e591f
commit
0964f800ab
4 changed files with 163 additions and 11 deletions
|
|
@ -75,7 +75,7 @@ impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus {
|
||||||
|
|
||||||
let sp = self.static_lookup_tables.len();
|
let sp = self.static_lookup_tables.len();
|
||||||
let dp = dynamic_lookup_tables.len();
|
let dp = dynamic_lookup_tables.len();
|
||||||
assert_eq!(sp, static_nafs.len());
|
assert!(sp >= static_nafs.len());
|
||||||
assert_eq!(dp, dynamic_nafs.len());
|
assert_eq!(dp, dynamic_nafs.len());
|
||||||
|
|
||||||
// We could save some doublings by looking for the highest
|
// We could save some doublings by looking for the highest
|
||||||
|
|
@ -99,7 +99,7 @@ impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::needless_range_loop)]
|
#[allow(clippy::needless_range_loop)]
|
||||||
for i in 0..sp {
|
for i in 0..static_nafs.len() {
|
||||||
let t_ij = static_nafs[i][j];
|
let t_ij = static_nafs[i][j];
|
||||||
match t_ij.cmp(&0) {
|
match t_ij.cmp(&0) {
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ pub mod spec {
|
||||||
|
|
||||||
let sp = self.static_lookup_tables.len();
|
let sp = self.static_lookup_tables.len();
|
||||||
let dp = dynamic_lookup_tables.len();
|
let dp = dynamic_lookup_tables.len();
|
||||||
assert_eq!(sp, static_nafs.len());
|
assert!(sp >= static_nafs.len());
|
||||||
assert_eq!(dp, dynamic_nafs.len());
|
assert_eq!(dp, dynamic_nafs.len());
|
||||||
|
|
||||||
// We could save some doublings by looking for the highest
|
// We could save some doublings by looking for the highest
|
||||||
|
|
@ -107,7 +107,7 @@ pub mod spec {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::needless_range_loop)]
|
#[allow(clippy::needless_range_loop)]
|
||||||
for i in 0..sp {
|
for i in 0..static_nafs.len() {
|
||||||
let t_ij = static_nafs[i][j];
|
let t_ij = static_nafs[i][j];
|
||||||
match t_ij.cmp(&0) {
|
match t_ij.cmp(&0) {
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
|
|
|
||||||
|
|
@ -1869,4 +1869,148 @@ mod test {
|
||||||
assert_eq!(P.compress(), R.compress());
|
assert_eq!(P.compress(), R.compress());
|
||||||
assert_eq!(Q.compress(), R.compress());
|
assert_eq!(Q.compress(), R.compress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
fn partial_precomputed_mixed_multiscalar_empty() {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
|
let n_static = 16;
|
||||||
|
let n_dynamic = 8;
|
||||||
|
|
||||||
|
let static_points = (0..n_static)
|
||||||
|
.map(|_| RistrettoPoint::random(&mut rng))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Use zero scalars
|
||||||
|
let static_scalars = Vec::new();
|
||||||
|
|
||||||
|
let dynamic_points = (0..n_dynamic)
|
||||||
|
.map(|_| RistrettoPoint::random(&mut rng))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let dynamic_scalars = (0..n_dynamic)
|
||||||
|
.map(|_| Scalar::random(&mut rng))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Compute the linear combination using precomputed multiscalar multiplication
|
||||||
|
let precomputation = VartimeRistrettoPrecomputation::new(static_points.iter());
|
||||||
|
let result_multiscalar = precomputation.vartime_mixed_multiscalar_mul(
|
||||||
|
&static_scalars,
|
||||||
|
&dynamic_scalars,
|
||||||
|
&dynamic_points,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Compute the linear combination manually
|
||||||
|
let mut result_manual = RistrettoPoint::identity();
|
||||||
|
for i in 0..static_scalars.len() {
|
||||||
|
result_manual += static_points[i] * static_scalars[i];
|
||||||
|
}
|
||||||
|
for i in 0..n_dynamic {
|
||||||
|
result_manual += dynamic_points[i] * dynamic_scalars[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(result_multiscalar, result_manual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
fn partial_precomputed_mixed_multiscalar() {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
|
let n_static = 16;
|
||||||
|
let n_dynamic = 8;
|
||||||
|
|
||||||
|
let static_points = (0..n_static)
|
||||||
|
.map(|_| RistrettoPoint::random(&mut rng))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Use one fewer scalars
|
||||||
|
let static_scalars = (0..n_static - 1)
|
||||||
|
.map(|_| Scalar::random(&mut rng))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let dynamic_points = (0..n_dynamic)
|
||||||
|
.map(|_| RistrettoPoint::random(&mut rng))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let dynamic_scalars = (0..n_dynamic)
|
||||||
|
.map(|_| Scalar::random(&mut rng))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Compute the linear combination using precomputed multiscalar multiplication
|
||||||
|
let precomputation = VartimeRistrettoPrecomputation::new(static_points.iter());
|
||||||
|
let result_multiscalar = precomputation.vartime_mixed_multiscalar_mul(
|
||||||
|
&static_scalars,
|
||||||
|
&dynamic_scalars,
|
||||||
|
&dynamic_points,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Compute the linear combination manually
|
||||||
|
let mut result_manual = RistrettoPoint::identity();
|
||||||
|
for i in 0..static_scalars.len() {
|
||||||
|
result_manual += static_points[i] * static_scalars[i];
|
||||||
|
}
|
||||||
|
for i in 0..n_dynamic {
|
||||||
|
result_manual += dynamic_points[i] * dynamic_scalars[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(result_multiscalar, result_manual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
fn partial_precomputed_multiscalar() {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
|
let n_static = 16;
|
||||||
|
|
||||||
|
let static_points = (0..n_static)
|
||||||
|
.map(|_| RistrettoPoint::random(&mut rng))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Use one fewer scalars
|
||||||
|
let static_scalars = (0..n_static - 1)
|
||||||
|
.map(|_| Scalar::random(&mut rng))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Compute the linear combination using precomputed multiscalar multiplication
|
||||||
|
let precomputation = VartimeRistrettoPrecomputation::new(static_points.iter());
|
||||||
|
let result_multiscalar = precomputation.vartime_multiscalar_mul(&static_scalars);
|
||||||
|
|
||||||
|
// Compute the linear combination manually
|
||||||
|
let mut result_manual = RistrettoPoint::identity();
|
||||||
|
for i in 0..static_scalars.len() {
|
||||||
|
result_manual += static_points[i] * static_scalars[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(result_multiscalar, result_manual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
fn partial_precomputed_multiscalar_empty() {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
|
let n_static = 16;
|
||||||
|
|
||||||
|
let static_points = (0..n_static)
|
||||||
|
.map(|_| RistrettoPoint::random(&mut rng))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Use zero scalars
|
||||||
|
let static_scalars = Vec::new();
|
||||||
|
|
||||||
|
// Compute the linear combination using precomputed multiscalar multiplication
|
||||||
|
let precomputation = VartimeRistrettoPrecomputation::new(static_points.iter());
|
||||||
|
let result_multiscalar = precomputation.vartime_multiscalar_mul(&static_scalars);
|
||||||
|
|
||||||
|
// Compute the linear combination manually
|
||||||
|
let mut result_manual = RistrettoPoint::identity();
|
||||||
|
for i in 0..static_scalars.len() {
|
||||||
|
result_manual += static_points[i] * static_scalars[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(result_multiscalar, result_manual);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -285,7 +285,7 @@ pub trait VartimeMultiscalarMul {
|
||||||
/// to be composed into the input iterators.
|
/// to be composed into the input iterators.
|
||||||
///
|
///
|
||||||
/// All methods require that the lengths of the input iterators be
|
/// All methods require that the lengths of the input iterators be
|
||||||
/// known and matching, as if they were `ExactSizeIterator`s. (It
|
/// known, as if they were `ExactSizeIterator`s. (It
|
||||||
/// does not require `ExactSizeIterator` only because that trait is
|
/// does not require `ExactSizeIterator` only because that trait is
|
||||||
/// broken).
|
/// broken).
|
||||||
pub trait VartimePrecomputedMultiscalarMul: Sized {
|
pub trait VartimePrecomputedMultiscalarMul: Sized {
|
||||||
|
|
@ -306,8 +306,10 @@ pub trait VartimePrecomputedMultiscalarMul: Sized {
|
||||||
/// $$
|
/// $$
|
||||||
/// where the \\(B_j\\) are the points that were supplied to `new`.
|
/// where the \\(B_j\\) are the points that were supplied to `new`.
|
||||||
///
|
///
|
||||||
/// It is an error to call this function with iterators of
|
/// It is valid for \\(b_i\\) to have a shorter length than \\(B_j\\).
|
||||||
/// inconsistent lengths.
|
/// In this case, any "unused" points are ignored in the computation.
|
||||||
|
/// It is an error to call this function if \\(b_i\\) has a longer
|
||||||
|
/// length than \\(B_j\\).
|
||||||
///
|
///
|
||||||
/// The trait bound aims for maximum flexibility: the input must
|
/// The trait bound aims for maximum flexibility: the input must
|
||||||
/// be convertable to iterators (`I: IntoIter`), and the
|
/// be convertable to iterators (`I: IntoIter`), and the
|
||||||
|
|
@ -337,8 +339,11 @@ pub trait VartimePrecomputedMultiscalarMul: Sized {
|
||||||
/// $$
|
/// $$
|
||||||
/// where the \\(B_j\\) are the points that were supplied to `new`.
|
/// where the \\(B_j\\) are the points that were supplied to `new`.
|
||||||
///
|
///
|
||||||
/// It is an error to call this function with iterators of
|
/// It is valid for \\(b_i\\) to have a shorter length than \\(B_j\\).
|
||||||
/// inconsistent lengths.
|
/// In this case, any "unused" points are ignored in the computation.
|
||||||
|
/// It is an error to call this function if \\(b_i\\) has a longer
|
||||||
|
/// length than \\(B_j\\), or if \\(a_i\\) and \\(A_i\\) do not have
|
||||||
|
/// the same length.
|
||||||
///
|
///
|
||||||
/// The trait bound aims for maximum flexibility: the inputs must be
|
/// The trait bound aims for maximum flexibility: the inputs must be
|
||||||
/// convertable to iterators (`I: IntoIter`), and the iterator's items
|
/// convertable to iterators (`I: IntoIter`), and the iterator's items
|
||||||
|
|
@ -378,8 +383,11 @@ pub trait VartimePrecomputedMultiscalarMul: Sized {
|
||||||
///
|
///
|
||||||
/// If any of the dynamic points were `None`, return `None`.
|
/// If any of the dynamic points were `None`, return `None`.
|
||||||
///
|
///
|
||||||
/// It is an error to call this function with iterators of
|
/// It is valid for \\(b_i\\) to have a shorter length than \\(B_j\\).
|
||||||
/// inconsistent lengths.
|
/// In this case, any "unused" points are ignored in the computation.
|
||||||
|
/// It is an error to call this function if \\(b_i\\) has a longer
|
||||||
|
/// length than \\(B_j\\), or if \\(a_i\\) and \\(A_i\\) do not have
|
||||||
|
/// the same length.
|
||||||
///
|
///
|
||||||
/// This function is particularly useful when verifying statements
|
/// This function is particularly useful when verifying statements
|
||||||
/// involving compressed points. Accepting `Option<Point>` allows
|
/// involving compressed points. Accepting `Option<Point>` allows
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue