Tweak multiexp to Zeroize points when invoked in constant time, not just scalars

This commit is contained in:
Luke Parker
2025-08-19 22:28:59 -04:00
parent 8a1b56a928
commit 17c1d5cd6b
8 changed files with 18 additions and 12 deletions

View File

@@ -12,7 +12,7 @@ use crate::{multiexp, multiexp_vartime};
// Flatten the contained statements to a single Vec.
// Wrapped in Zeroizing in case any of the included statements contain private values.
#[allow(clippy::type_complexity)]
fn flat<Id: Copy + Zeroize, G: Group<Scalar: PrimeFieldBits + Zeroize> + Zeroize>(
fn flat<Id: Copy + Zeroize, G: Zeroize + Group<Scalar: Zeroize + PrimeFieldBits>>(
slice: &[(Id, Vec<(G::Scalar, G)>)],
) -> Zeroizing<Vec<(G::Scalar, G)>> {
Zeroizing::new(slice.iter().flat_map(|pairs| pairs.1.iter()).copied().collect::<Vec<_>>())
@@ -21,11 +21,11 @@ fn flat<Id: Copy + Zeroize, G: Group<Scalar: PrimeFieldBits + Zeroize> + Zeroize
/// A batch verifier intended to verify a series of statements are each equivalent to zero.
#[allow(clippy::type_complexity)]
#[derive(Clone, Zeroize)]
pub struct BatchVerifier<Id: Copy + Zeroize, G: Group<Scalar: PrimeFieldBits + Zeroize> + Zeroize>(
pub struct BatchVerifier<Id: Copy + Zeroize, G: Zeroize + Group<Scalar: Zeroize + PrimeFieldBits>>(
Zeroizing<Vec<(Id, Vec<(G::Scalar, G)>)>>,
);
impl<Id: Copy + Zeroize, G: Group<Scalar: PrimeFieldBits + Zeroize> + Zeroize>
impl<Id: Copy + Zeroize, G: Zeroize + Group<Scalar: Zeroize + PrimeFieldBits>>
BatchVerifier<Id, G>
{
/// Create a new batch verifier, expected to verify the following amount of statements.

View File

@@ -5,6 +5,8 @@
#[cfg(not(feature = "std"))]
#[macro_use]
extern crate alloc;
#[allow(unused_imports)]
use std_shims::prelude::*;
use std_shims::vec::Vec;
use zeroize::Zeroize;
@@ -175,7 +177,9 @@ fn algorithm(len: usize) -> Algorithm {
/// Performs a multiexponentiation, automatically selecting the optimal algorithm based on the
/// amount of pairs.
pub fn multiexp<G: Group<Scalar: PrimeFieldBits + Zeroize>>(pairs: &[(G::Scalar, G)]) -> G {
pub fn multiexp<G: Zeroize + Group<Scalar: Zeroize + PrimeFieldBits>>(
pairs: &[(G::Scalar, G)],
) -> G {
match algorithm(pairs.len()) {
Algorithm::Null => Group::identity(),
Algorithm::Single => pairs[0].1 * pairs[0].0,

View File

@@ -7,7 +7,7 @@ use crate::prep_bits;
// Pippenger's algorithm for multiexponentiation, as published in the SIAM Journal on Computing
// DOI: 10.1137/0209022
pub(crate) fn pippenger<G: Group<Scalar: PrimeFieldBits>>(
pub(crate) fn pippenger<G: Zeroize + Group<Scalar: PrimeFieldBits>>(
pairs: &[(G::Scalar, G)],
window: u8,
) -> G {
@@ -25,6 +25,7 @@ pub(crate) fn pippenger<G: Group<Scalar: PrimeFieldBits>>(
for p in 0 .. bits.len() {
buckets[usize::from(bits[p][n])] += pairs[p].1;
}
buckets.zeroize();
let mut intermediate_sum = G::identity();
for b in (1 .. buckets.len()).rev() {

View File

@@ -24,12 +24,12 @@ fn prep_tables<G: Group>(pairs: &[(G::Scalar, G)], window: u8) -> Vec<Vec<G>> {
// Straus's algorithm for multiexponentiation, as published in The American Mathematical Monthly
// DOI: 10.2307/2310929
pub(crate) fn straus<G: Group<Scalar: PrimeFieldBits + Zeroize>>(
pub(crate) fn straus<G: Zeroize + Group<Scalar: PrimeFieldBits>>(
pairs: &[(G::Scalar, G)],
window: u8,
) -> G {
let mut groupings = prep_bits(pairs, window);
let tables = prep_tables(pairs, window);
let mut tables = prep_tables(pairs, window);
let mut res = G::identity();
for b in (0 .. groupings[0].len()).rev() {
@@ -45,6 +45,7 @@ pub(crate) fn straus<G: Group<Scalar: PrimeFieldBits + Zeroize>>(
}
groupings.zeroize();
tables.zeroize();
res
}

View File

@@ -9,7 +9,7 @@ use group::Group;
use crate::BatchVerifier;
pub(crate) fn test_batch<G: Group<Scalar: PrimeFieldBits + Zeroize> + Zeroize>() {
pub(crate) fn test_batch<G: Zeroize + Group<Scalar: Zeroize + PrimeFieldBits>>() {
let valid = |batch: BatchVerifier<_, G>| {
assert!(batch.verify());
assert!(batch.verify_vartime());

View File

@@ -18,7 +18,7 @@ mod batch;
use batch::test_batch;
#[allow(dead_code)]
fn benchmark_internal<G: Group<Scalar: PrimeFieldBits + Zeroize>>(straus_bool: bool) {
fn benchmark_internal<G: Zeroize + Group<Scalar: Zeroize + PrimeFieldBits>>(straus_bool: bool) {
let runs: usize = 20;
let mut start = 0;
@@ -83,7 +83,7 @@ fn benchmark_internal<G: Group<Scalar: PrimeFieldBits + Zeroize>>(straus_bool: b
}
}
fn test_multiexp<G: Group<Scalar: PrimeFieldBits + Zeroize>>() {
fn test_multiexp<G: Zeroize + Group<Scalar: Zeroize + PrimeFieldBits>>() {
let test = |pairs: &[_], sum| {
// These should automatically determine the best algorithm
assert_eq!(multiexp(pairs), sum);