curve: Support MSM #static scalars <= #static points (#668)

This commit is contained in:
Aaron Feickert 2024-07-30 08:43:13 -05:00 committed by GitHub
parent 83a57e591f
commit 0964f800ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 163 additions and 11 deletions

View file

@ -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 => {

View file

@ -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 => {

View file

@ -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);
}
} }

View file

@ -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