From 569e6d2c0a44303c00bf7c36b9ece81819c2bf6b Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sat, 27 Jul 2024 09:42:32 -0600 Subject: [PATCH 1/2] Split `Uint::mul_mod` and `Uint::mul_mod_vartime` The previous `mul_mod` was variable-time with respect to the modulus. This adds a proper constant-time version. --- Cargo.lock | 2 +- src/uint/mul_mod.rs | 38 +++++++++++++++++++++++++------------- tests/uint.rs | 2 +- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7b3f99f14..31171356a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -210,7 +210,7 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.6.0-rc.1" +version = "0.6.0-rc.2" dependencies = [ "bincode", "criterion", diff --git a/src/uint/mul_mod.rs b/src/uint/mul_mod.rs index b1a130f70..cae224339 100644 --- a/src/uint/mul_mod.rs +++ b/src/uint/mul_mod.rs @@ -3,28 +3,40 @@ use crate::{ modular::{MontyForm, MontyParams}, primitives::mul_rem, - Limb, MulMod, Uint, WideWord, Word, + Concat, Limb, MulMod, Odd, Split, Uint, WideWord, Word, }; impl Uint { /// Computes `self * rhs mod p` for odd `p`. - /// - /// Panics if `p` is even. - // TODO(tarcieri): support for even `p`? - pub fn mul_mod(&self, rhs: &Uint, p: &Uint) -> Uint { + pub fn mul_mod( + &self, + rhs: &Uint, + p: &Odd>, + ) -> Uint + where + Uint: Concat>, + Uint: Split>, + { // NOTE: the overhead of converting to Montgomery form to perform this operation and then // immediately converting out of Montgomery form after just a single operation is likely to // be higher than other possible implementations of this function, such as using a // Barrett reduction instead. // // It's worth potentially exploring other approaches to improve efficiency. - match p.to_odd().into() { - Some(odd_p) => { - let params = MontyParams::new_vartime(odd_p); - (MontyForm::new(self, params) * MontyForm::new(rhs, params)).retrieve() - } - None => todo!("even moduli are currently unsupported"), - } + let params = MontyParams::new(*p); + (MontyForm::new(self, params) * MontyForm::new(rhs, params)).retrieve() + } + + /// Computes `self * rhs mod p` for odd `p` in variable time with respect to `p`, + pub fn mul_mod_vartime(&self, rhs: &Uint, p: &Odd>) -> Uint { + // NOTE: the overhead of converting to Montgomery form to perform this operation and then + // immediately converting out of Montgomery form after just a single operation is likely to + // be higher than other possible implementations of this function, such as using a + // Barrett reduction instead. + // + // It's worth potentially exploring other approaches to improve efficiency. + let params = MontyParams::new_vartime(*p); + (MontyForm::new(self, params) * MontyForm::new(rhs, params)).retrieve() } /// Computes `self * rhs mod p` for the special modulus @@ -64,7 +76,7 @@ impl MulMod for Uint { type Output = Self; fn mul_mod(&self, rhs: &Self, p: &Self) -> Self { - self.mul_mod(rhs, p) + self.mul_mod_vartime(rhs, &p.to_odd().expect("only odd moduli supported")) } } diff --git a/tests/uint.rs b/tests/uint.rs index db8045e2c..4b1ce47b4 100644 --- a/tests/uint.rs +++ b/tests/uint.rs @@ -188,7 +188,7 @@ proptest! { let p_bi = to_biguint(&P); let expected = to_uint((a_bi * b_bi) % p_bi); - let actual = a.mul_mod(&b, &P); + let actual = a.mul_mod_vartime(&b, &P); assert!(expected < P); assert!(actual < P); From 0ea188ea581cc4ab88be43cfecc2072e538dc7d2 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sat, 27 Jul 2024 11:02:49 -0600 Subject: [PATCH 2/2] Use `NonZero` as the type `p` arg to `mul_mod` Was previously `Odd` --- src/uint/mul_mod.rs | 18 +++++++++++------- tests/uint.rs | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/uint/mul_mod.rs b/src/uint/mul_mod.rs index cae224339..103ef8d87 100644 --- a/src/uint/mul_mod.rs +++ b/src/uint/mul_mod.rs @@ -3,15 +3,17 @@ use crate::{ modular::{MontyForm, MontyParams}, primitives::mul_rem, - Concat, Limb, MulMod, Odd, Split, Uint, WideWord, Word, + Concat, Limb, MulMod, NonZero, Split, Uint, WideWord, Word, }; impl Uint { /// Computes `self * rhs mod p` for odd `p`. + /// + /// Panics if `p` is even. (TODO: support even `p`) pub fn mul_mod( &self, rhs: &Uint, - p: &Odd>, + p: &NonZero>, ) -> Uint where Uint: Concat>, @@ -23,19 +25,21 @@ impl Uint { // Barrett reduction instead. // // It's worth potentially exploring other approaches to improve efficiency. - let params = MontyParams::new(*p); + let params = MontyParams::new(p.to_odd().expect("p should be odd")); (MontyForm::new(self, params) * MontyForm::new(rhs, params)).retrieve() } - /// Computes `self * rhs mod p` for odd `p` in variable time with respect to `p`, - pub fn mul_mod_vartime(&self, rhs: &Uint, p: &Odd>) -> Uint { + /// Computes `self * rhs mod p` for odd `p` in variable time with respect to `p`. + /// + /// Panics if `p` is even. (TODO: support even `p`) + pub fn mul_mod_vartime(&self, rhs: &Uint, p: &NonZero>) -> Uint { // NOTE: the overhead of converting to Montgomery form to perform this operation and then // immediately converting out of Montgomery form after just a single operation is likely to // be higher than other possible implementations of this function, such as using a // Barrett reduction instead. // // It's worth potentially exploring other approaches to improve efficiency. - let params = MontyParams::new_vartime(*p); + let params = MontyParams::new_vartime(p.to_odd().expect("p should be odd")); (MontyForm::new(self, params) * MontyForm::new(rhs, params)).retrieve() } @@ -76,7 +80,7 @@ impl MulMod for Uint { type Output = Self; fn mul_mod(&self, rhs: &Self, p: &Self) -> Self { - self.mul_mod_vartime(rhs, &p.to_odd().expect("only odd moduli supported")) + self.mul_mod_vartime(rhs, &NonZero::new(*p).expect("p should be non-zero")) } } diff --git a/tests/uint.rs b/tests/uint.rs index 4b1ce47b4..341e7fdef 100644 --- a/tests/uint.rs +++ b/tests/uint.rs @@ -188,7 +188,7 @@ proptest! { let p_bi = to_biguint(&P); let expected = to_uint((a_bi * b_bi) % p_bi); - let actual = a.mul_mod_vartime(&b, &P); + let actual = a.mul_mod_vartime(&b, P.as_nz_ref()); assert!(expected < P); assert!(actual < P);