From 062cd77a98fd426b4a2093ae623a6b26a57d2b85 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Sat, 13 Aug 2022 19:21:12 -0400 Subject: [PATCH] Close https://github.com/serai-dex/serai/issues/85 --- crypto/multiexp/src/batch.rs | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/crypto/multiexp/src/batch.rs b/crypto/multiexp/src/batch.rs index 63985fb2..d571dd98 100644 --- a/crypto/multiexp/src/batch.rs +++ b/crypto/multiexp/src/batch.rs @@ -2,7 +2,7 @@ use rand_core::{RngCore, CryptoRng}; use zeroize::Zeroize; -use ff::{Field, PrimeFieldBits}; +use ff::{Field, PrimeField, PrimeFieldBits}; use group::Group; use crate::{multiexp, multiexp_vartime}; @@ -31,9 +31,32 @@ where G::Scalar::one() } else { let mut weight; - // Ensure it's non-zero, as a zero scalar would cause this item to pass no matter what while { - weight = G::Scalar::random(&mut *rng); + // Generate a random scalar + let mut repr = G::Scalar::random(&mut *rng).to_repr(); + + // Calculate the amount of bytes to clear. We want to clear less than half + let repr_len = repr.as_ref().len(); + let unused_bits = (repr_len * 8) - usize::try_from(G::Scalar::CAPACITY).unwrap(); + // Don't clear any partial bytes + let to_clear = (repr_len / 2) - ((unused_bits + 7) / 8); + + // Clear a safe amount of bytes + for b in &mut repr.as_mut()[.. to_clear] { + *b = 0; + } + + // Ensure these bits are used as the low bits so low scalars multiplied by this don't + // become large scalars + weight = G::Scalar::from_repr(repr).unwrap(); + // Tests if any bit we supposedly just cleared is set, and if so, reverses it + // Not a security issue if this fails, just a minor performance hit at ~2^-120 odds + if weight.to_le_bits().iter().take(to_clear * 8).any(|bit| *bit) { + repr.as_mut().reverse(); + weight = G::Scalar::from_repr(repr).unwrap(); + } + + // Ensure it's non-zero, as a zero scalar would cause this item to pass no matter what weight.is_zero().into() } {} weight