From 213e7968a63b1aa7a9e5d61a638fc82a8e66deee Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Tue, 11 Oct 2022 13:21:17 +0200 Subject: [PATCH 01/23] Implement Modular with add and mul --- src/uint.rs | 2 + src/uint/add.rs | 11 +- src/uint/inv_mod.rs | 54 ++++++++- src/uint/modular/add.rs | 47 ++++++++ src/uint/modular/mod.rs | 249 ++++++++++++++++++++++++++++++++++++++++ src/uint/modular/mul.rs | 18 +++ src/uint/neg.rs | 21 ++++ src/uint/shl.rs | 14 +++ src/uint/shr.rs | 14 +++ src/uint/sub.rs | 11 +- 10 files changed, 438 insertions(+), 3 deletions(-) create mode 100644 src/uint/modular/add.rs create mode 100644 src/uint/modular/mod.rs create mode 100644 src/uint/modular/mul.rs create mode 100644 src/uint/neg.rs diff --git a/src/uint.rs b/src/uint.rs index 566e2a4b..798ab371 100644 --- a/src/uint.rs +++ b/src/uint.rs @@ -23,8 +23,10 @@ mod div; mod encoding; mod from; mod inv_mod; +mod modular; mod mul; mod mul_mod; +mod neg; mod neg_mod; mod shl; mod shr; diff --git a/src/uint/add.rs b/src/uint/add.rs index 2822e9e6..dd514209 100644 --- a/src/uint/add.rs +++ b/src/uint/add.rs @@ -2,7 +2,7 @@ use crate::{Checked, CheckedAdd, Limb, UInt, Wrapping, Zero}; use core::ops::{Add, AddAssign}; -use subtle::CtOption; +use subtle::{Choice, ConditionallySelectable, CtOption}; impl UInt { /// Computes `a + b + carry`, returning the result along with the new carry. @@ -36,6 +36,15 @@ impl UInt { pub const fn wrapping_add(&self, rhs: &Self) -> Self { self.adc(rhs, Limb::ZERO).0 } + + /// Perform wrapping addition, returning the overflow bit as a `Choice`. + pub fn conditional_wrapping_add(&mut self, rhs: &Self, choice: Choice) -> Choice { + let actual_rhs = UInt::conditional_select(&UInt::ZERO, rhs, choice); + let (sum, carry) = self.adc(&actual_rhs, Limb::ZERO); + *self = sum; + + Choice::from(carry.0 as u8) + } } impl CheckedAdd<&UInt> for UInt { diff --git a/src/uint/inv_mod.rs b/src/uint/inv_mod.rs index a1140856..da8ce41c 100644 --- a/src/uint/inv_mod.rs +++ b/src/uint/inv_mod.rs @@ -1,5 +1,7 @@ +use subtle::ConditionallySelectable; + use super::UInt; -use crate::Limb; +use crate::{Integer, Limb, Wrapping}; impl UInt { /// Computes 1/`self` mod 2^k as specified in Algorithm 4 from @@ -25,6 +27,56 @@ impl UInt { } x } + + pub fn inv_mod(mut self, modulus: &UInt) -> Option> { + debug_assert!(modulus.is_odd().unwrap_u8() == 1); + + let mut u = UInt::ONE; + let mut v = UInt::ZERO; + + let mut b = *modulus; + + // TODO: This can be lower if `self` is known to be small. + let bit_size = 2 * LIMBS * 64; + + let mut m1hp = modulus.clone(); + let carry = m1hp.shr_1(); + debug_assert!(carry.unwrap_u8() == 1); + let mut m1hp = Wrapping(m1hp); + m1hp += &Wrapping(UInt::ONE); + + for _ in 0..bit_size { + debug_assert!(b.is_odd().unwrap_u8() == 1); + + let self_odd = self.is_odd(); + + // Set `self -= b` if `self` is odd. + let swap = self.conditional_wrapping_sub(&b, self_odd); + // Set `b += self` if `swap` is true. + b = UInt::conditional_select(&b, &(b.wrapping_add(&self)), swap); + // Negate `self` if `swap` is true. + self = self.conditional_wrapping_neg(swap); + + UInt::conditional_swap(&mut u, &mut v, swap); + let cy = u.conditional_wrapping_sub(&v, self_odd); + let cyy = u.conditional_wrapping_add(modulus, cy); + debug_assert_eq!(cy.unwrap_u8(), cyy.unwrap_u8()); + + let overflow = self.shr_1(); + debug_assert!(overflow.unwrap_u8() == 0); + let cy = u.shr_1(); + let cy = u.conditional_wrapping_add(&m1hp.0, cy); + debug_assert!(cy.unwrap_u8() == 0); + } + + debug_assert_eq!(self, UInt::ZERO); + + if b != UInt::ONE { + None + } else { + Some(v) + } + } } #[cfg(test)] diff --git a/src/uint/modular/add.rs b/src/uint/modular/add.rs new file mode 100644 index 00000000..beaee538 --- /dev/null +++ b/src/uint/modular/add.rs @@ -0,0 +1,47 @@ +use core::ops::AddAssign; + +use crate::UInt; + +use super::Modular; + +impl AddAssign<&UInt> for Modular { + fn add_assign(&mut self, rhs: &UInt) { + *self += &Modular::new(*rhs, self.modulus_params); + } +} + +impl AddAssign<&Self> for Modular { + fn add_assign(&mut self, rhs: &Self) { + // TODO: Can we easily verify that these have the same MontgomeryParams? (e.g. using a debug_assert) + self.value = self.value.add_mod(&rhs.value, &self.modulus_params.modulus); + } +} + +#[cfg(test)] +mod tests { + use crate::{ + uint::modular::{Modular, MontgomeryParams}, + U256, + }; + + #[test] + fn add_overflow() { + let modulus = + U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); + let modulus_params = MontgomeryParams::new(modulus); + + let x = + U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); + let mut x_mod = Modular::new(x, modulus_params); + + let y = + U256::from_be_hex("d5777c45019673125ad240f83094d4252d829516fac8601ed01979ec1ec1a251"); + + x_mod += &y; + + let expected = + U256::from_be_hex("1a2472fde50286541d97ca6a3592dd75beb9c9646e40c511b82496cfc3926956"); + + assert_eq!(expected, x_mod.retrieve()); + } +} diff --git a/src/uint/modular/mod.rs b/src/uint/modular/mod.rs new file mode 100644 index 00000000..a57b75fa --- /dev/null +++ b/src/uint/modular/mod.rs @@ -0,0 +1,249 @@ +use subtle::{Choice, ConditionallySelectable}; + +use crate::{CheckedAdd, Concat, Limb, Split, UInt, WideWord, Word}; + +mod add; +mod mul; + +#[derive(Debug, Clone, Copy)] +pub struct MontgomeryParams { + modulus: UInt, + montgomery_r: UInt, + montgomery_r2: UInt, + // We only need the LSB because during reduction this value is multiplied modulo 2**64. + modulus_neg_inv: Limb, +} + +impl MontgomeryParams +where + UInt: Concat>, + UInt: Split>, +{ + /// Note that the modulus must be tight (i.e. it should be at least somewhat close in size to `LIMB_COUNT`). + pub fn new(modulus: UInt) -> Self { + let montgomery_r = UInt::MAX + .reduce(&modulus) + .unwrap() + .checked_add(&UInt::ONE) + .unwrap(); + + let double_modulus = (UInt::::ZERO).concat(&modulus); + let (_, montgomery_r2) = montgomery_r + .square() + .reduce(&double_modulus) + .unwrap() + .split(); + + let modulus_neg_inv = + Limb(0u64.wrapping_sub(modulus.inv_mod2k(Word::BITS as usize).limbs[0].0)); + + MontgomeryParams { + modulus, + montgomery_r, + montgomery_r2, + modulus_neg_inv, + } + } +} + +// TODO: We should consider taking modulus_params as a reference +pub struct Modular { + value: UInt, + modulus_params: MontgomeryParams, +} + +impl Modular { + pub fn new(integer: UInt, modulus_params: MontgomeryParams) -> Self { + let mut modular_integer = Modular { + value: integer, + modulus_params, + }; + + modular_integer *= &modulus_params.montgomery_r2; + modular_integer + } + + pub fn retrieve(&self) -> UInt { + montgomery_reduction((self.value, UInt::ZERO), &self.modulus_params) + } +} + +/// Algorithm 14.32 in Handbook of Applied Cryptography (https://cacr.uwaterloo.ca/hac/about/chap14.pdf) +fn montgomery_reduction( + lower_upper: (UInt, UInt), + modulus_params: &MontgomeryParams, +) -> UInt { + let (mut lower, mut upper) = lower_upper; + + let mut meta_carry = 0; + for i in 0..LIMBS { + let u = (lower.limbs[i] + .0 + .wrapping_mul(modulus_params.modulus_neg_inv.0)) as WideWord; + + let new_limb = (u * modulus_params.modulus.limbs[0].0 as WideWord) + .wrapping_add(lower.limbs[i].0 as WideWord); + let mut carry = new_limb >> Word::BITS; + + for j in 1..(LIMBS - i) { + let new_limb = (u * modulus_params.modulus.limbs[j].0 as WideWord) + .wrapping_add(lower.limbs[i + j].0 as WideWord) + .wrapping_add(carry); + carry = new_limb >> Word::BITS; + lower.limbs[i + j] = Limb(new_limb as Word); + } + for j in (LIMBS - i)..LIMBS { + let new_limb = (u * modulus_params.modulus.limbs[j].0 as WideWord) + .wrapping_add(upper.limbs[i + j - LIMBS].0 as WideWord) + .wrapping_add(carry); + carry = new_limb >> Word::BITS; + upper.limbs[i + j - LIMBS] = Limb(new_limb as Word); + } + + let new_sum = (upper.limbs[i].0 as WideWord) + .wrapping_add(carry) + .wrapping_add(meta_carry); + meta_carry = new_sum >> Word::BITS; + upper.limbs[i] = Limb(new_sum as Word); + } + + // Division is simply taking the upper half of the limbs + // Final reduction (at this point, the value is at most 2 * modulus) + let must_reduce = Choice::from(meta_carry as u8); + upper = upper.wrapping_sub(&UInt::conditional_select( + &UInt::ZERO, + &modulus_params.modulus, + must_reduce, + )); + + upper +} + +#[cfg(test)] +mod tests { + use crate::{ + uint::modular::{montgomery_reduction, Modular, MontgomeryParams}, + Limb, UInt, U256, + }; + + #[test] + fn test_montgomery_params() { + let modulus = U256::new([ + Limb(0xffffffff00000001), + Limb(0x53bda402fffe5bfe), + Limb(0x3339d80809a1d805), + Limb(0x73eda753299d7d48), + ]); + let modulus_params = MontgomeryParams::new(modulus); + + assert_eq!( + modulus_params.montgomery_r.limbs, + [ + Limb(0x00000001fffffffe), + Limb(0x5884b7fa00034802), + Limb(0x998c4fefecbc4ff5), + Limb(0x1824b159acc5056f) + ] + ); + assert_eq!( + modulus_params.montgomery_r2.limbs, + [ + Limb(0xc999e990f3f29c6d), + Limb(0x2b6cedcb87925c23), + Limb(0x05d314967254398f), + Limb(0x0748d9d99f59ff11) + ] + ); + assert_eq!(modulus_params.modulus_neg_inv, Limb(0xfffffffeffffffff)); + } + + #[test] + fn test_reducing_r() { + let modulus = + U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); + let modulus_params = MontgomeryParams::new(modulus); + + // Divide the value R by R, which should equal 1 + assert_eq!( + montgomery_reduction((modulus_params.montgomery_r, UInt::ZERO), &modulus_params), + UInt::ONE + ); + } + + #[test] + fn test_reducing_r2() { + let modulus = + U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); + let modulus_params = MontgomeryParams::new(modulus); + + // Divide the value R^2 by R, which should equal R + assert_eq!( + montgomery_reduction((modulus_params.montgomery_r2, UInt::ZERO), &modulus_params), + modulus_params.montgomery_r + ); + } + + #[test] + fn test_reducing_r2_wide() { + let modulus = + U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); + let modulus_params = MontgomeryParams::new(modulus); + + // Divide the value R^2 by R, which should equal R + let (hi, lo) = modulus_params.montgomery_r.square().split(); + assert_eq!( + montgomery_reduction((lo, hi), &modulus_params), + modulus_params.montgomery_r + ); + } + + #[test] + fn test_reducing_xr_wide() { + let modulus = + U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); + let modulus_params = MontgomeryParams::new(modulus); + + // Reducing xR should return x + let x = + U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); + let product = x.mul_wide(&modulus_params.montgomery_r); + assert_eq!(montgomery_reduction(product, &modulus_params), x); + } + + #[test] + fn test_reducing_xr2_wide() { + let modulus = + U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); + let modulus_params = MontgomeryParams::new(modulus); + + // Reducing xR^2 should return xR + let x = + U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); + let product = x.mul_wide(&modulus_params.montgomery_r2); + + // Computing xR mod modulus without Montgomery reduction + let (lo, hi) = x.mul_wide(&modulus_params.montgomery_r); + let c = hi.concat(&lo); + let red = c.reduce(&UInt::<4>::ZERO.concat(&modulus)).unwrap(); + let (hi, lo) = red.split(); + assert_eq!(hi, UInt::ZERO); + + assert_eq!(montgomery_reduction(product, &modulus_params), lo); + } + + #[test] + fn test_new_retrieve() { + let modulus = + U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); + + //let modulus = U256::new([Limb(0xffffffff00000001), Limb(0x53bda402fffe5bfe), Limb(0x3339d80809a1d805), Limb(0x73eda753299d7d48)]); + let modulus_params = MontgomeryParams::new(modulus); + + let x = + U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); + let x_mod = Modular::new(x, modulus_params); + + // Confirm that when creating a Modular and retrieving the value, that it equals the original + assert_eq!(x, x_mod.retrieve()); + } +} diff --git a/src/uint/modular/mul.rs b/src/uint/modular/mul.rs new file mode 100644 index 00000000..713828bf --- /dev/null +++ b/src/uint/modular/mul.rs @@ -0,0 +1,18 @@ +use core::ops::MulAssign; + +use crate::UInt; + +use super::{montgomery_reduction, Modular}; + +impl MulAssign<&UInt> for Modular { + fn mul_assign(&mut self, rhs: &UInt) { + let product = self.value.mul_wide(rhs); + self.value = montgomery_reduction(product, &self.modulus_params); + } +} + +impl MulAssign for Modular { + fn mul_assign(&mut self, rhs: Self) { + self.mul_assign(&rhs.value); + } +} diff --git a/src/uint/neg.rs b/src/uint/neg.rs new file mode 100644 index 00000000..5d3fff29 --- /dev/null +++ b/src/uint/neg.rs @@ -0,0 +1,21 @@ +use core::ops::Neg; + +use subtle::{Choice, ConditionallySelectable}; + +use crate::{UInt, Wrapping}; + +impl Neg for Wrapping> { + type Output = Self; + + fn neg(self) -> Self::Output { + let shifted = Wrapping(self.0.shl_vartime(1)); + &self - shifted + } +} + +impl UInt { + /// Negates based on `choice` by wrapping the integer. + pub fn conditional_wrapping_neg(self, choice: Choice) -> UInt { + UInt::conditional_select(&self, &(-Wrapping(self)).0, choice) + } +} diff --git a/src/uint/shl.rs b/src/uint/shl.rs index 9d466913..951ef71a 100644 --- a/src/uint/shl.rs +++ b/src/uint/shl.rs @@ -1,9 +1,23 @@ //! [`UInt`] bitwise left shift operations. +use subtle::Choice; + use crate::{Limb, UInt, Word}; use core::ops::{Shl, ShlAssign}; impl UInt { + pub fn shl_1(&mut self) -> Choice { + let shifted_bits = self.limbs.map(|x| x << 1); + let carry_bits = self.limbs.map(|x| x >> 63); + + self.limbs[0] = shifted_bits[0]; + for i in 1..LIMBS { + self.limbs[i] = shifted_bits[i] | carry_bits[i - 1] + } + + Choice::from(carry_bits[LIMBS - 1].0 as u8) + } + /// Computes `self << shift`. /// /// NOTE: this operation is variable time with respect to `n` *ONLY*. diff --git a/src/uint/shr.rs b/src/uint/shr.rs index 54375ae7..9ed4c4c9 100644 --- a/src/uint/shr.rs +++ b/src/uint/shr.rs @@ -1,10 +1,24 @@ //! [`UInt`] bitwise right shift operations. +use subtle::Choice; + use super::UInt; use crate::Limb; use core::ops::{Shr, ShrAssign}; impl UInt { + pub fn shr_1(&mut self) -> Choice { + let shifted_bits = self.limbs.map(|x| x >> 1); + let carry_bits = self.limbs.map(|x| x << 63); + + for i in 0..(LIMBS - 1) { + self.limbs[i] = shifted_bits[i] | carry_bits[i + 1] + } + self.limbs[LIMBS - 1] = shifted_bits[LIMBS - 1]; + + Choice::from((carry_bits[0] >> 63).0 as u8) + } + /// Computes `self >> n`. /// /// NOTE: this operation is variable time with respect to `n` *ONLY*. diff --git a/src/uint/sub.rs b/src/uint/sub.rs index 102f6b97..6103a4bc 100644 --- a/src/uint/sub.rs +++ b/src/uint/sub.rs @@ -3,7 +3,7 @@ use super::UInt; use crate::{Checked, CheckedSub, Limb, Wrapping, Zero}; use core::ops::{Sub, SubAssign}; -use subtle::CtOption; +use subtle::{Choice, ConditionallySelectable, CtOption}; impl UInt { /// Computes `a - (b + borrow)`, returning the result along with the new borrow. @@ -38,6 +38,15 @@ impl UInt { pub const fn wrapping_sub(&self, rhs: &Self) -> Self { self.sbb(rhs, Limb::ZERO).0 } + + /// Perform wrapping subtraction, returning the underflow bit as a `Choice`. + pub fn conditional_wrapping_sub(&mut self, rhs: &Self, choice: Choice) -> Choice { + let actual_rhs = UInt::conditional_select(&UInt::ZERO, rhs, choice); + let (sum, borrow) = self.sbb(&actual_rhs, Limb::ZERO); + *self = sum; + + Choice::from((borrow.0 != 0) as u8) + } } impl CheckedSub<&UInt> for UInt { From f13f399742bd8917af6b2b4f50596d783400874d Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Tue, 11 Oct 2022 14:44:33 +0200 Subject: [PATCH 02/23] Implement modular exponentiation --- src/uint.rs | 1 + src/uint/modular/mod.rs | 22 +++++++- src/uint/modular/mul.rs | 17 +++--- src/uint/modular/pow.rs | 114 ++++++++++++++++++++++++++++++++++++++++ src/uint/pow_mod.rs | 40 ++++++++++++++ 5 files changed, 187 insertions(+), 7 deletions(-) create mode 100644 src/uint/modular/pow.rs create mode 100644 src/uint/pow_mod.rs diff --git a/src/uint.rs b/src/uint.rs index 798ab371..943a20a1 100644 --- a/src/uint.rs +++ b/src/uint.rs @@ -28,6 +28,7 @@ mod mul; mod mul_mod; mod neg; mod neg_mod; +mod pow_mod; mod shl; mod shr; mod sqrt; diff --git a/src/uint/modular/mod.rs b/src/uint/modular/mod.rs index a57b75fa..69d2e8d2 100644 --- a/src/uint/modular/mod.rs +++ b/src/uint/modular/mod.rs @@ -4,6 +4,7 @@ use crate::{CheckedAdd, Concat, Limb, Split, UInt, WideWord, Word}; mod add; mod mul; +mod pow; #[derive(Debug, Clone, Copy)] pub struct MontgomeryParams { @@ -47,6 +48,7 @@ where } // TODO: We should consider taking modulus_params as a reference +#[derive(Debug, Clone, Copy)] pub struct Modular { value: UInt, modulus_params: MontgomeryParams, @@ -59,13 +61,22 @@ impl Modular { modulus_params, }; - modular_integer *= &modulus_params.montgomery_r2; + let product = integer.mul_wide(&modulus_params.montgomery_r2); + modular_integer.value = montgomery_reduction(product, &modulus_params); + modular_integer } pub fn retrieve(&self) -> UInt { montgomery_reduction((self.value, UInt::ZERO), &self.modulus_params) } + + pub fn one(modulus_params: MontgomeryParams) -> Self { + Modular { + value: modulus_params.montgomery_r, + modulus_params, + } + } } /// Algorithm 14.32 in Handbook of Applied Cryptography (https://cacr.uwaterloo.ca/hac/about/chap14.pdf) @@ -119,6 +130,15 @@ fn montgomery_reduction( upper } +impl ConditionallySelectable for Modular { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + Modular { + value: UInt::conditional_select(&a.value, &b.value, choice), + modulus_params: a.modulus_params, + } + } +} + #[cfg(test)] mod tests { use crate::{ diff --git a/src/uint/modular/mul.rs b/src/uint/modular/mul.rs index 713828bf..7ee58778 100644 --- a/src/uint/modular/mul.rs +++ b/src/uint/modular/mul.rs @@ -1,18 +1,23 @@ use core::ops::MulAssign; -use crate::UInt; +use crate::{Concat, Split, UInt}; use super::{montgomery_reduction, Modular}; -impl MulAssign<&UInt> for Modular { - fn mul_assign(&mut self, rhs: &UInt) { - let product = self.value.mul_wide(rhs); - self.value = montgomery_reduction(product, &self.modulus_params); +impl Modular +where + UInt: Concat>, + UInt: Split>, +{ + pub fn square(&mut self) { + let (hi, lo) = self.value.square().split(); + self.value = montgomery_reduction((lo, hi), &self.modulus_params); } } impl MulAssign for Modular { fn mul_assign(&mut self, rhs: Self) { - self.mul_assign(&rhs.value); + let product = self.value.mul_wide(&rhs.value); + self.value = montgomery_reduction(product, &self.modulus_params); } } diff --git a/src/uint/modular/pow.rs b/src/uint/modular/pow.rs new file mode 100644 index 00000000..548b3594 --- /dev/null +++ b/src/uint/modular/pow.rs @@ -0,0 +1,114 @@ +use subtle::ConditionallySelectable; + +use crate::{Concat, Split, UInt, Word}; + +use super::Modular; + +impl Modular +where + UInt: Concat>, + UInt: Split>, +{ + pub fn pow(&self, exponent: &UInt) -> Modular { + self.pow_specific(exponent, LIMBS * Word::BITS as usize) + } + + /// Perform modular exponentiation using Montgomery's ladder. `exponent_bits` represents the number of bits to take into account for the exponent. Note that this value is leaked in the time pattern. + pub fn pow_specific(&self, exponent: &UInt, exponent_bits: usize) -> Modular { + let mut x1: Modular = Modular::one(self.modulus_params); + let mut x2: Modular = self.clone(); + + // Shift the exponent all the way to the left so the leftmost bit is the MSB of the `UInt` + let mut n: UInt = + exponent.shl_vartime((LIMBS * Word::BITS as usize) - exponent_bits); + + for _ in 0..exponent_bits { + // TODO: Remove one of the squares and instead conditionally select x1 or x2 to square + // Peel off one bit at a time from the left side + let overflow = n.shl_1(); + + let mut product: Modular = x1.clone(); + product *= x2; + + let mut square = Modular::conditional_select(&x1, &x2, overflow); + square.square(); + + x1 = Modular::conditional_select(&square, &product, overflow); + x2 = Modular::conditional_select(&product, &square, overflow); + } + + x1 + } +} + +#[cfg(test)] +mod tests { + use crate::{ + uint::modular::{Modular, MontgomeryParams}, + UInt, U1024, + }; + + #[test] + fn test_powmod_specific_mini() { + let modulus = UInt::<1>::from(11u64); + let modulus_params = MontgomeryParams::new(modulus); + + let base = UInt::from(3u64); + let base_mod = Modular::new(base, modulus_params); + + let exponent = UInt::from(7u64); + + let res = base_mod.pow_specific(&exponent, 3); + + let expected = UInt::from(9u64); + assert_eq!(res.retrieve(), expected); + } + + #[test] + fn test_powmod_mini() { + let modulus = UInt::<1>::from(11u64); + let modulus_params = MontgomeryParams::new(modulus); + + let base = UInt::from(3u64); + let base_mod = Modular::new(base, modulus_params); + + let exponent = UInt::from(7u64); + + let res = base_mod.pow(&exponent); + + let expected = UInt::from(9u64); + assert_eq!(res.retrieve(), expected); + } + + #[test] + fn test_powmod_small_base() { + let modulus = U1024::from_be_hex("D509E7854ABDC81921F669F1DC6F61359523F3949803E58ED4EA8BC16483DC6F37BFE27A9AC9EEA2969B357ABC5C0EE214BE16A7D4C58FC620D5B5A20AFF001AD198D3155E5799DC4EA76652D64983A7E130B5EACEBAC768D28D589C36EC749C558D0B64E37CD0775C0D0104AE7D98BA23C815185DD43CD8B16292FD94156767"); + let modulus_params = MontgomeryParams::new(modulus); + + let base = U1024::from(105u64); + let base_mod = Modular::new(base, modulus_params); + + let exponent = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); + + let res = base_mod.pow(&exponent); + + let expected = UInt::from_be_hex("84FADE244D8A183FD9D209B07312E017F53BBDF4377108EDB4FEAD2AEB1DFF83B6CE604A2DACF49E52574E69055C6E2D30980938CF259421E17AB277C67663712B185C565C97D3200659D83B287C1D8325BFD258C7DBA4BB2766A57C5F2A7EE3FA784A8669C54839F3D29E73215E7939B16E8293524D871D56F67759D553A242"); + assert_eq!(res.retrieve(), expected); + } + + #[test] + fn test_powmod_small_exponent() { + let modulus = U1024::from_be_hex("D509E7854ABDC81921F669F1DC6F61359523F3949803E58ED4EA8BC16483DC6F37BFE27A9AC9EEA2969B357ABC5C0EE214BE16A7D4C58FC620D5B5A20AFF001AD198D3155E5799DC4EA76652D64983A7E130B5EACEBAC768D28D589C36EC749C558D0B64E37CD0775C0D0104AE7D98BA23C815185DD43CD8B16292FD94156767"); + let modulus_params = MontgomeryParams::new(modulus); + + let base = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); + let base_mod = Modular::new(base, modulus_params); + + let exponent = U1024::from(105u64); + + let res = base_mod.pow(&exponent); + + let expected = UInt::from_be_hex("6B717DC3571BEC59C1E370780630280B05F13D9BB69E192DA75EAE2A817B270840881034B0AB9EE6B47382C58424AE00F90B88DFA7DFF7C9417B28E4C9DF170BCDFC4689140E9BA067FDB224831A33E2E18232655D15EA985EC0C8FB774BFA967B734A60DD8FC1F9214B7C262050F269C248F3255D5D1E7BD63626707232FF72"); + assert_eq!(res.retrieve(), expected); + } +} diff --git a/src/uint/pow_mod.rs b/src/uint/pow_mod.rs new file mode 100644 index 00000000..bcf9f6d4 --- /dev/null +++ b/src/uint/pow_mod.rs @@ -0,0 +1,40 @@ +use crate::{Concat, Split, UInt}; + +use super::modular::{Modular, MontgomeryParams}; + +impl UInt +where + UInt: Concat>, + UInt: Split>, +{ + pub fn pow_mod(&self, exponent: &Self, modulus: &Self) -> Self { + let modulus_params = MontgomeryParams::new(*modulus); + let base_mod = Modular::new(*self, modulus_params); + + base_mod.pow(exponent).retrieve() + } + + pub fn pow_mod_specific(&self, exponent: &Self, modulus: &Self, exponent_bits: usize) -> Self { + let modulus_params = MontgomeryParams::new(*modulus); + let base_mod = Modular::new(*self, modulus_params); + + base_mod.pow_specific(exponent, exponent_bits).retrieve() + } +} + +#[cfg(test)] +mod tests { + use crate::UInt; + + #[test] + fn test_powmod_mini() { + let b = UInt::<1>::from(3u64); + let e = UInt::from(7u64); + let m = UInt::from(11u64); + + let res = b.pow_mod(&e, &m); + + let expected = UInt::from(9u64); + assert_eq!(res, expected); + } +} From d3887f0b6bdf3c3ce5daa1189d4215f6c7ac975a Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Tue, 11 Oct 2022 15:12:44 +0200 Subject: [PATCH 03/23] Fix obvious issues and add documentation --- src/uint/bit_and.rs | 1 + src/uint/bit_not.rs | 1 + src/uint/bit_or.rs | 1 + src/uint/bit_xor.rs | 1 + src/uint/inv_mod.rs | 36 ++++++++++++++++++++++++++++++++++-- src/uint/modular/mod.rs | 3 ++- src/uint/modular/pow.rs | 4 ++-- src/uint/neg.rs | 2 +- src/uint/pow_mod.rs | 2 ++ src/uint/shl.rs | 1 + src/uint/shr.rs | 1 + 11 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/uint/bit_and.rs b/src/uint/bit_and.rs index cab89a42..e6eab65f 100644 --- a/src/uint/bit_and.rs +++ b/src/uint/bit_and.rs @@ -46,6 +46,7 @@ impl BitAnd for UInt { impl BitAnd<&UInt> for UInt { type Output = UInt; + #[allow(clippy::needless_borrow)] fn bitand(self, rhs: &UInt) -> UInt { (&self).bitand(rhs) } diff --git a/src/uint/bit_not.rs b/src/uint/bit_not.rs index 747d3b49..914774a8 100644 --- a/src/uint/bit_not.rs +++ b/src/uint/bit_not.rs @@ -23,6 +23,7 @@ impl UInt { impl Not for UInt { type Output = Self; + #[allow(clippy::needless_borrow)] fn not(self) -> ::Output { (&self).not() } diff --git a/src/uint/bit_or.rs b/src/uint/bit_or.rs index 4a01a834..8f6b84db 100644 --- a/src/uint/bit_or.rs +++ b/src/uint/bit_or.rs @@ -46,6 +46,7 @@ impl BitOr for UInt { impl BitOr<&UInt> for UInt { type Output = UInt; + #[allow(clippy::needless_borrow)] fn bitor(self, rhs: &UInt) -> UInt { (&self).bitor(rhs) } diff --git a/src/uint/bit_xor.rs b/src/uint/bit_xor.rs index 16d78ad3..0e886ca2 100644 --- a/src/uint/bit_xor.rs +++ b/src/uint/bit_xor.rs @@ -46,6 +46,7 @@ impl BitXor for UInt { impl BitXor<&UInt> for UInt { type Output = UInt; + #[allow(clippy::needless_borrow)] fn bitxor(self, rhs: &UInt) -> UInt { (&self).bitxor(rhs) } diff --git a/src/uint/inv_mod.rs b/src/uint/inv_mod.rs index da8ce41c..677604b1 100644 --- a/src/uint/inv_mod.rs +++ b/src/uint/inv_mod.rs @@ -28,6 +28,7 @@ impl UInt { x } + /// Computes the multiplicative inverse of `self` mod `modulus`. In other words `self^-1 mod modulus`. Returns `None` if an inverse does not exist. The algorithm is the same as in GMP 6.2.1's `mpn_sec_invert`. pub fn inv_mod(mut self, modulus: &UInt) -> Option> { debug_assert!(modulus.is_odd().unwrap_u8() == 1); @@ -39,7 +40,7 @@ impl UInt { // TODO: This can be lower if `self` is known to be small. let bit_size = 2 * LIMBS * 64; - let mut m1hp = modulus.clone(); + let mut m1hp = *modulus; let carry = m1hp.shr_1(); debug_assert!(carry.unwrap_u8() == 1); let mut m1hp = Wrapping(m1hp); @@ -81,7 +82,7 @@ impl UInt { #[cfg(test)] mod tests { - use crate::U256; + use crate::{UInt, U1024, U256}; #[test] fn inv_mod2k() { @@ -111,4 +112,35 @@ mod tests { let a = v.inv_mod2k(256); assert_eq!(e, a); } + + #[test] + fn test_invert() { + let a = U1024::from_be_hex("000225E99153B467A5B451979A3F451DAEF3BF8D6C6521D2FA24BBB17F29544E347A412B065B75A351EA9719E2430D2477B11CC9CF9C1AD6EDEE26CB15F463F8BCC72EF87EA30288E95A48AA792226CEC959DCB0672D8F9D80A54CBBEA85CAD8382EC224DEB2F5784E62D0CC2F81C2E6AD14EBABE646D6764B30C32B87688985"); + let m = U1024::from_be_hex("D509E7854ABDC81921F669F1DC6F61359523F3949803E58ED4EA8BC16483DC6F37BFE27A9AC9EEA2969B357ABC5C0EE214BE16A7D4C58FC620D5B5A20AFF001AD198D3155E5799DC4EA76652D64983A7E130B5EACEBAC768D28D589C36EC749C558D0B64E37CD0775C0D0104AE7D98BA23C815185DD43CD8B16292FD94156767"); + + let res = a.inv_mod(&m); + + let expected = U1024::from_be_hex("B03623284B0EBABCABD5C5881893320281460C0A8E7BF4BFDCFFCBCCBF436A55D364235C8171E46C7D21AAD0680676E57274A8FDA6D12768EF961CACDD2DAE5788D93DA5EB8EDC391EE3726CDCF4613C539F7D23E8702200CB31B5ED5B06E5CA3E520968399B4017BF98A864FABA2B647EFC4998B56774D4F2CB026BC024A336"); + assert_eq!(res.unwrap(), expected); + } + + #[test] + fn test_invert_small() { + let a = UInt::<1>::from(3u64); + let m = UInt::from(13u64); + + let res = a.inv_mod(&m); + + assert_eq!(UInt::from(9u64), res.unwrap()); + } + + #[test] + fn test_no_inverse_small() { + let a = UInt::<1>::from(14u64); + let m = UInt::from(49u64); + + let res = a.inv_mod(&m); + + assert!(res.is_none()); + } } diff --git a/src/uint/modular/mod.rs b/src/uint/modular/mod.rs index 69d2e8d2..07e8f250 100644 --- a/src/uint/modular/mod.rs +++ b/src/uint/modular/mod.rs @@ -120,7 +120,8 @@ fn montgomery_reduction( // Division is simply taking the upper half of the limbs // Final reduction (at this point, the value is at most 2 * modulus) - let must_reduce = Choice::from(meta_carry as u8); + let must_reduce = + Choice::from(meta_carry as u8) | Choice::from((upper >= modulus_params.modulus) as u8); upper = upper.wrapping_sub(&UInt::conditional_select( &UInt::ZERO, &modulus_params.modulus, diff --git a/src/uint/modular/pow.rs b/src/uint/modular/pow.rs index 548b3594..0c445eb4 100644 --- a/src/uint/modular/pow.rs +++ b/src/uint/modular/pow.rs @@ -16,7 +16,7 @@ where /// Perform modular exponentiation using Montgomery's ladder. `exponent_bits` represents the number of bits to take into account for the exponent. Note that this value is leaked in the time pattern. pub fn pow_specific(&self, exponent: &UInt, exponent_bits: usize) -> Modular { let mut x1: Modular = Modular::one(self.modulus_params); - let mut x2: Modular = self.clone(); + let mut x2: Modular = *self; // Shift the exponent all the way to the left so the leftmost bit is the MSB of the `UInt` let mut n: UInt = @@ -27,7 +27,7 @@ where // Peel off one bit at a time from the left side let overflow = n.shl_1(); - let mut product: Modular = x1.clone(); + let mut product: Modular = x1; product *= x2; let mut square = Modular::conditional_select(&x1, &x2, overflow); diff --git a/src/uint/neg.rs b/src/uint/neg.rs index 5d3fff29..f24a06dd 100644 --- a/src/uint/neg.rs +++ b/src/uint/neg.rs @@ -9,7 +9,7 @@ impl Neg for Wrapping> { fn neg(self) -> Self::Output { let shifted = Wrapping(self.0.shl_vartime(1)); - &self - shifted + self - shifted } } diff --git a/src/uint/pow_mod.rs b/src/uint/pow_mod.rs index bcf9f6d4..7c175c1f 100644 --- a/src/uint/pow_mod.rs +++ b/src/uint/pow_mod.rs @@ -7,6 +7,7 @@ where UInt: Concat>, UInt: Split>, { + /// Computes `self^exponent mod modulus` using Montgomery's ladder. If you are also performing other modular operations, consider using `Modular` and the associated `pow` function. pub fn pow_mod(&self, exponent: &Self, modulus: &Self) -> Self { let modulus_params = MontgomeryParams::new(*modulus); let base_mod = Modular::new(*self, modulus_params); @@ -14,6 +15,7 @@ where base_mod.pow(exponent).retrieve() } + /// Computes `self^exponent mod modulus` using Montgomery's ladder, but only considering the first `exponent_bits` bits of the exponent. This number is revealed from the timing pattern. If you are also performing other modular operations, consider using `Modular` and the associated `pow` function. pub fn pow_mod_specific(&self, exponent: &Self, modulus: &Self, exponent_bits: usize) -> Self { let modulus_params = MontgomeryParams::new(*modulus); let base_mod = Modular::new(*self, modulus_params); diff --git a/src/uint/shl.rs b/src/uint/shl.rs index 951ef71a..8cfd163f 100644 --- a/src/uint/shl.rs +++ b/src/uint/shl.rs @@ -6,6 +6,7 @@ use crate::{Limb, UInt, Word}; use core::ops::{Shl, ShlAssign}; impl UInt { + /// Computes `self << 1` in constant-time, returning the overflowing bit as a `Choice`. pub fn shl_1(&mut self) -> Choice { let shifted_bits = self.limbs.map(|x| x << 1); let carry_bits = self.limbs.map(|x| x >> 63); diff --git a/src/uint/shr.rs b/src/uint/shr.rs index 9ed4c4c9..23d72c41 100644 --- a/src/uint/shr.rs +++ b/src/uint/shr.rs @@ -7,6 +7,7 @@ use crate::Limb; use core::ops::{Shr, ShrAssign}; impl UInt { + /// Computes `self >> 1` in constant-time, returning the overflowing bit as a `Choice`. pub fn shr_1(&mut self) -> Choice { let shifted_bits = self.limbs.map(|x| x >> 1); let carry_bits = self.limbs.map(|x| x << 63); From 4177420d39f5c0fcac33f54da7f807fd5d13a5b8 Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Tue, 11 Oct 2022 15:18:41 +0200 Subject: [PATCH 04/23] Use Word instead of u64 --- src/uint/modular/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uint/modular/mod.rs b/src/uint/modular/mod.rs index 07e8f250..63edd3b1 100644 --- a/src/uint/modular/mod.rs +++ b/src/uint/modular/mod.rs @@ -36,7 +36,7 @@ where .split(); let modulus_neg_inv = - Limb(0u64.wrapping_sub(modulus.inv_mod2k(Word::BITS as usize).limbs[0].0)); + Limb(Word::MIN.wrapping_sub(modulus.inv_mod2k(Word::BITS as usize).limbs[0].0)); MontgomeryParams { modulus, From df3c8987b4c7161ed16e29990d39be49d7f28778 Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Tue, 11 Oct 2022 15:36:25 +0200 Subject: [PATCH 05/23] Use e.g. U64 instead of UInt::<1> --- src/uint/inv_mod.rs | 12 ++++++------ src/uint/modular/mod.rs | 35 ++++++++++++----------------------- src/uint/modular/pow.rs | 22 +++++++++++----------- src/uint/pow_mod.rs | 10 +++++----- 4 files changed, 34 insertions(+), 45 deletions(-) diff --git a/src/uint/inv_mod.rs b/src/uint/inv_mod.rs index 677604b1..916cca73 100644 --- a/src/uint/inv_mod.rs +++ b/src/uint/inv_mod.rs @@ -82,7 +82,7 @@ impl UInt { #[cfg(test)] mod tests { - use crate::{UInt, U1024, U256}; + use crate::{U1024, U256, U64}; #[test] fn inv_mod2k() { @@ -126,18 +126,18 @@ mod tests { #[test] fn test_invert_small() { - let a = UInt::<1>::from(3u64); - let m = UInt::from(13u64); + let a = U64::from(3u64); + let m = U64::from(13u64); let res = a.inv_mod(&m); - assert_eq!(UInt::from(9u64), res.unwrap()); + assert_eq!(U64::from(9u64), res.unwrap()); } #[test] fn test_no_inverse_small() { - let a = UInt::<1>::from(14u64); - let m = UInt::from(49u64); + let a = U64::from(14u64); + let m = U64::from(49u64); let res = a.inv_mod(&m); diff --git a/src/uint/modular/mod.rs b/src/uint/modular/mod.rs index 63edd3b1..38ef701b 100644 --- a/src/uint/modular/mod.rs +++ b/src/uint/modular/mod.rs @@ -144,38 +144,27 @@ impl ConditionallySelectable for Modular { mod tests { use crate::{ uint::modular::{montgomery_reduction, Modular, MontgomeryParams}, - Limb, UInt, U256, + UInt, U256, U64, }; #[test] fn test_montgomery_params() { - let modulus = U256::new([ - Limb(0xffffffff00000001), - Limb(0x53bda402fffe5bfe), - Limb(0x3339d80809a1d805), - Limb(0x73eda753299d7d48), - ]); + let modulus = + U256::from_be_hex("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); let modulus_params = MontgomeryParams::new(modulus); assert_eq!( - modulus_params.montgomery_r.limbs, - [ - Limb(0x00000001fffffffe), - Limb(0x5884b7fa00034802), - Limb(0x998c4fefecbc4ff5), - Limb(0x1824b159acc5056f) - ] + modulus_params.montgomery_r, + U256::from_be_hex("1824b159acc5056f998c4fefecbc4ff55884b7fa0003480200000001fffffffe") + ); + assert_eq!( + modulus_params.montgomery_r2, + U256::from_be_hex("0748d9d99f59ff1105d314967254398f2b6cedcb87925c23c999e990f3f29c6d") ); assert_eq!( - modulus_params.montgomery_r2.limbs, - [ - Limb(0xc999e990f3f29c6d), - Limb(0x2b6cedcb87925c23), - Limb(0x05d314967254398f), - Limb(0x0748d9d99f59ff11) - ] + modulus_params.modulus_neg_inv, + U64::from_be_hex("fffffffeffffffff").limbs[0] ); - assert_eq!(modulus_params.modulus_neg_inv, Limb(0xfffffffeffffffff)); } #[test] @@ -245,7 +234,7 @@ mod tests { // Computing xR mod modulus without Montgomery reduction let (lo, hi) = x.mul_wide(&modulus_params.montgomery_r); let c = hi.concat(&lo); - let red = c.reduce(&UInt::<4>::ZERO.concat(&modulus)).unwrap(); + let red = c.reduce(&U256::ZERO.concat(&modulus)).unwrap(); let (hi, lo) = red.split(); assert_eq!(hi, UInt::ZERO); diff --git a/src/uint/modular/pow.rs b/src/uint/modular/pow.rs index 0c445eb4..3b2a620b 100644 --- a/src/uint/modular/pow.rs +++ b/src/uint/modular/pow.rs @@ -45,38 +45,38 @@ where mod tests { use crate::{ uint::modular::{Modular, MontgomeryParams}, - UInt, U1024, + U1024, U64, }; #[test] fn test_powmod_specific_mini() { - let modulus = UInt::<1>::from(11u64); + let modulus = U64::from(11u64); let modulus_params = MontgomeryParams::new(modulus); - let base = UInt::from(3u64); + let base = U64::from(3u64); let base_mod = Modular::new(base, modulus_params); - let exponent = UInt::from(7u64); + let exponent = U64::from(7u64); let res = base_mod.pow_specific(&exponent, 3); - let expected = UInt::from(9u64); + let expected = U64::from(9u64); assert_eq!(res.retrieve(), expected); } #[test] fn test_powmod_mini() { - let modulus = UInt::<1>::from(11u64); + let modulus = U64::from(11u64); let modulus_params = MontgomeryParams::new(modulus); - let base = UInt::from(3u64); + let base = U64::from(3u64); let base_mod = Modular::new(base, modulus_params); - let exponent = UInt::from(7u64); + let exponent = U64::from(7u64); let res = base_mod.pow(&exponent); - let expected = UInt::from(9u64); + let expected = U64::from(9u64); assert_eq!(res.retrieve(), expected); } @@ -92,7 +92,7 @@ mod tests { let res = base_mod.pow(&exponent); - let expected = UInt::from_be_hex("84FADE244D8A183FD9D209B07312E017F53BBDF4377108EDB4FEAD2AEB1DFF83B6CE604A2DACF49E52574E69055C6E2D30980938CF259421E17AB277C67663712B185C565C97D3200659D83B287C1D8325BFD258C7DBA4BB2766A57C5F2A7EE3FA784A8669C54839F3D29E73215E7939B16E8293524D871D56F67759D553A242"); + let expected = U1024::from_be_hex("84FADE244D8A183FD9D209B07312E017F53BBDF4377108EDB4FEAD2AEB1DFF83B6CE604A2DACF49E52574E69055C6E2D30980938CF259421E17AB277C67663712B185C565C97D3200659D83B287C1D8325BFD258C7DBA4BB2766A57C5F2A7EE3FA784A8669C54839F3D29E73215E7939B16E8293524D871D56F67759D553A242"); assert_eq!(res.retrieve(), expected); } @@ -108,7 +108,7 @@ mod tests { let res = base_mod.pow(&exponent); - let expected = UInt::from_be_hex("6B717DC3571BEC59C1E370780630280B05F13D9BB69E192DA75EAE2A817B270840881034B0AB9EE6B47382C58424AE00F90B88DFA7DFF7C9417B28E4C9DF170BCDFC4689140E9BA067FDB224831A33E2E18232655D15EA985EC0C8FB774BFA967B734A60DD8FC1F9214B7C262050F269C248F3255D5D1E7BD63626707232FF72"); + let expected = U1024::from_be_hex("6B717DC3571BEC59C1E370780630280B05F13D9BB69E192DA75EAE2A817B270840881034B0AB9EE6B47382C58424AE00F90B88DFA7DFF7C9417B28E4C9DF170BCDFC4689140E9BA067FDB224831A33E2E18232655D15EA985EC0C8FB774BFA967B734A60DD8FC1F9214B7C262050F269C248F3255D5D1E7BD63626707232FF72"); assert_eq!(res.retrieve(), expected); } } diff --git a/src/uint/pow_mod.rs b/src/uint/pow_mod.rs index 7c175c1f..e65d00db 100644 --- a/src/uint/pow_mod.rs +++ b/src/uint/pow_mod.rs @@ -26,17 +26,17 @@ where #[cfg(test)] mod tests { - use crate::UInt; + use crate::U64; #[test] fn test_powmod_mini() { - let b = UInt::<1>::from(3u64); - let e = UInt::from(7u64); - let m = UInt::from(11u64); + let b = U64::from(3u64); + let e = U64::from(7u64); + let m = U64::from(11u64); let res = b.pow_mod(&e, &m); - let expected = UInt::from(9u64); + let expected = U64::from(9u64); assert_eq!(res, expected); } } From bf7778831975d0557fde2e385722926beb02465f Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Wed, 12 Oct 2022 15:39:04 +0200 Subject: [PATCH 06/23] Rewrite uint operations to const fn --- src/uint/add.rs | 13 ++++--- src/uint/cmp.rs | 12 +++++++ src/uint/inv_mod.rs | 76 +++++++++++++++++++++++------------------ src/uint/modular/mod.rs | 11 ++++++ src/uint/modular/pow.rs | 11 +++--- src/uint/neg.rs | 11 +++--- src/uint/shl.rs | 40 +++++++++++++++------- src/uint/shr.rs | 38 +++++++++++++++------ src/uint/sub.rs | 16 ++++----- 9 files changed, 146 insertions(+), 82 deletions(-) diff --git a/src/uint/add.rs b/src/uint/add.rs index dd514209..8a7f929c 100644 --- a/src/uint/add.rs +++ b/src/uint/add.rs @@ -1,8 +1,8 @@ //! [`UInt`] addition operations. -use crate::{Checked, CheckedAdd, Limb, UInt, Wrapping, Zero}; +use crate::{Checked, CheckedAdd, Limb, UInt, Word, Wrapping, Zero}; use core::ops::{Add, AddAssign}; -use subtle::{Choice, ConditionallySelectable, CtOption}; +use subtle::CtOption; impl UInt { /// Computes `a + b + carry`, returning the result along with the new carry. @@ -37,13 +37,12 @@ impl UInt { self.adc(rhs, Limb::ZERO).0 } - /// Perform wrapping addition, returning the overflow bit as a `Choice`. - pub fn conditional_wrapping_add(&mut self, rhs: &Self, choice: Choice) -> Choice { - let actual_rhs = UInt::conditional_select(&UInt::ZERO, rhs, choice); + /// Perform wrapping addition, returning the overflow bit as a `Word` that is either 0...0 or 1...1. + pub(crate) const fn conditional_wrapping_add(&self, rhs: &Self, choice: Word) -> (Self, Word) { + let actual_rhs = UInt::ct_select(UInt::ZERO, *rhs, choice); let (sum, carry) = self.adc(&actual_rhs, Limb::ZERO); - *self = sum; - Choice::from(carry.0 as u8) + (sum, carry.0.wrapping_mul(Word::MAX)) } } diff --git a/src/uint/cmp.rs b/src/uint/cmp.rs index 19046df9..676622ce 100644 --- a/src/uint/cmp.rs +++ b/src/uint/cmp.rs @@ -24,6 +24,14 @@ impl UInt { UInt { limbs } } + #[inline] + pub(crate) const fn ct_swap(a: UInt, b: UInt, c: Word) -> (Self, Self) { + let new_a = Self::ct_select(a, b, c); + let new_b = Self::ct_select(b, a, c); + + (new_a, new_b) + } + /// Returns all 1's if `self`!=0 or 0 if `self`==0. /// /// Const-friendly: we can't yet use `subtle` in `const fn` contexts. @@ -38,6 +46,10 @@ impl UInt { Limb::is_nonzero(Limb(b)) } + pub(crate) const fn ct_is_odd(&self) -> Word { + (self.limbs[0].0 & 1).wrapping_mul(Word::MAX) + } + /// Returns -1 if self < rhs /// 0 if self == rhs /// 1 if self > rhs diff --git a/src/uint/inv_mod.rs b/src/uint/inv_mod.rs index 916cca73..712fbf2c 100644 --- a/src/uint/inv_mod.rs +++ b/src/uint/inv_mod.rs @@ -1,7 +1,7 @@ -use subtle::ConditionallySelectable; +use subtle::{Choice, CtOption}; use super::UInt; -use crate::{Integer, Limb, Wrapping}; +use crate::{Limb, Word}; impl UInt { /// Computes 1/`self` mod 2^k as specified in Algorithm 4 from @@ -28,9 +28,11 @@ impl UInt { x } - /// Computes the multiplicative inverse of `self` mod `modulus`. In other words `self^-1 mod modulus`. Returns `None` if an inverse does not exist. The algorithm is the same as in GMP 6.2.1's `mpn_sec_invert`. - pub fn inv_mod(mut self, modulus: &UInt) -> Option> { - debug_assert!(modulus.is_odd().unwrap_u8() == 1); + /// Computes the multiplicative inverse of `self` mod `modulus`. In other words `self^-1 mod modulus`. Returns `(inverse, 1...1)` if an inverse exists, otherwise `(undefined, 0...0)`. The algorithm is the same as in GMP 6.2.1's `mpn_sec_invert`. + pub const fn ct_inv_mod(self, modulus: &UInt) -> (Self, Word) { + debug_assert!(modulus.ct_is_odd() == Word::MAX); + + let mut a = self; let mut u = UInt::ONE; let mut v = UInt::ZERO; @@ -41,42 +43,50 @@ impl UInt { let bit_size = 2 * LIMBS * 64; let mut m1hp = *modulus; - let carry = m1hp.shr_1(); - debug_assert!(carry.unwrap_u8() == 1); - let mut m1hp = Wrapping(m1hp); - m1hp += &Wrapping(UInt::ONE); + let (m1hp_new, carry) = m1hp.shr_1(); + debug_assert!(carry == Word::MAX); + m1hp = m1hp_new.wrapping_add(&UInt::ONE); - for _ in 0..bit_size { - debug_assert!(b.is_odd().unwrap_u8() == 1); + let mut i = 0; + while i < bit_size { + debug_assert!(b.ct_is_odd() == Word::MAX); - let self_odd = self.is_odd(); + let self_odd = a.ct_is_odd(); // Set `self -= b` if `self` is odd. - let swap = self.conditional_wrapping_sub(&b, self_odd); + let (new_a, swap) = a.conditional_wrapping_sub(&b, self_odd); // Set `b += self` if `swap` is true. - b = UInt::conditional_select(&b, &(b.wrapping_add(&self)), swap); + b = UInt::ct_select(b, b.wrapping_add(&new_a), swap); // Negate `self` if `swap` is true. - self = self.conditional_wrapping_neg(swap); - - UInt::conditional_swap(&mut u, &mut v, swap); - let cy = u.conditional_wrapping_sub(&v, self_odd); - let cyy = u.conditional_wrapping_add(modulus, cy); - debug_assert_eq!(cy.unwrap_u8(), cyy.unwrap_u8()); - - let overflow = self.shr_1(); - debug_assert!(overflow.unwrap_u8() == 0); - let cy = u.shr_1(); - let cy = u.conditional_wrapping_add(&m1hp.0, cy); - debug_assert!(cy.unwrap_u8() == 0); - } + a = new_a.conditional_wrapping_neg(swap); + + let (new_u, new_v) = UInt::ct_swap(u, v, swap); + let (new_u, cy) = new_u.conditional_wrapping_sub(&new_v, self_odd); + let (new_u, cyy) = new_u.conditional_wrapping_add(modulus, cy); + debug_assert!(cy == cyy); + + let (new_a, overflow) = a.shr_1(); + debug_assert!(overflow == 0); + let (new_u, cy) = new_u.shr_1(); + let (new_u, cy) = new_u.conditional_wrapping_add(&m1hp, cy); + debug_assert!(cy == 0); - debug_assert_eq!(self, UInt::ZERO); + a = new_a; + u = new_u; + v = new_v; - if b != UInt::ONE { - None - } else { - Some(v) + i += 1; } + + debug_assert!(a.ct_cmp(&UInt::ZERO) == 0); + + (v, b.ct_not_eq(&UInt::ONE) ^ Word::MAX) + } + + /// Computes the multiplicative inverse of `self` mod `modulus`. In other words `self^-1 mod modulus`. Returns `None` if the inverse does not exist. The algorithm is the same as in GMP 6.2.1's `mpn_sec_invert`. + pub fn inv_mod(self, modulus: &UInt) -> CtOption { + let (inverse, exists) = self.ct_inv_mod(modulus); + CtOption::new(inverse, Choice::from((exists == Word::MAX) as u8)) } } @@ -141,6 +151,6 @@ mod tests { let res = a.inv_mod(&m); - assert!(res.is_none()); + assert!(res.is_none().unwrap_u8() == 1); } } diff --git a/src/uint/modular/mod.rs b/src/uint/modular/mod.rs index 38ef701b..4fbecf44 100644 --- a/src/uint/modular/mod.rs +++ b/src/uint/modular/mod.rs @@ -77,6 +77,17 @@ impl Modular { modulus_params, } } + + /// Return `a` if `c`==0 or `b` if `c`==`Word::MAX`. + /// + /// Const-friendly: we can't yet use `subtle` in `const fn` contexts. + #[inline] + pub(crate) const fn ct_select(a: Self, b: Self, c: Word) -> Self { + Modular { + value: UInt::ct_select(a.value, b.value, c), + modulus_params: a.modulus_params, + } + } } /// Algorithm 14.32 in Handbook of Applied Cryptography (https://cacr.uwaterloo.ca/hac/about/chap14.pdf) diff --git a/src/uint/modular/pow.rs b/src/uint/modular/pow.rs index 3b2a620b..bd0001ce 100644 --- a/src/uint/modular/pow.rs +++ b/src/uint/modular/pow.rs @@ -1,5 +1,3 @@ -use subtle::ConditionallySelectable; - use crate::{Concat, Split, UInt, Word}; use super::Modular; @@ -25,16 +23,17 @@ where for _ in 0..exponent_bits { // TODO: Remove one of the squares and instead conditionally select x1 or x2 to square // Peel off one bit at a time from the left side - let overflow = n.shl_1(); + let (next_n, overflow) = n.shl_1(); + n = next_n; let mut product: Modular = x1; product *= x2; - let mut square = Modular::conditional_select(&x1, &x2, overflow); + let mut square = Modular::ct_select(x1, x2, overflow); square.square(); - x1 = Modular::conditional_select(&square, &product, overflow); - x2 = Modular::conditional_select(&product, &square, overflow); + x1 = Modular::ct_select(square, product, overflow); + x2 = Modular::ct_select(product, square, overflow); } x1 diff --git a/src/uint/neg.rs b/src/uint/neg.rs index f24a06dd..0af1601c 100644 --- a/src/uint/neg.rs +++ b/src/uint/neg.rs @@ -1,8 +1,6 @@ use core::ops::Neg; -use subtle::{Choice, ConditionallySelectable}; - -use crate::{UInt, Wrapping}; +use crate::{UInt, Word, Wrapping}; impl Neg for Wrapping> { type Output = Self; @@ -15,7 +13,10 @@ impl Neg for Wrapping> { impl UInt { /// Negates based on `choice` by wrapping the integer. - pub fn conditional_wrapping_neg(self, choice: Choice) -> UInt { - UInt::conditional_select(&self, &(-Wrapping(self)).0, choice) + pub(crate) const fn conditional_wrapping_neg(self, choice: Word) -> UInt { + let (shifted, _) = self.shl_1(); + let negated_self = self.wrapping_sub(&shifted); + + UInt::ct_select(self, negated_self, choice) } } diff --git a/src/uint/shl.rs b/src/uint/shl.rs index 8cfd163f..f923e960 100644 --- a/src/uint/shl.rs +++ b/src/uint/shl.rs @@ -1,22 +1,38 @@ //! [`UInt`] bitwise left shift operations. -use subtle::Choice; - -use crate::{Limb, UInt, Word}; +use crate::{limb::HI_BIT, Limb, UInt, Word}; use core::ops::{Shl, ShlAssign}; impl UInt { - /// Computes `self << 1` in constant-time, returning the overflowing bit as a `Choice`. - pub fn shl_1(&mut self) -> Choice { - let shifted_bits = self.limbs.map(|x| x << 1); - let carry_bits = self.limbs.map(|x| x >> 63); - - self.limbs[0] = shifted_bits[0]; - for i in 1..LIMBS { - self.limbs[i] = shifted_bits[i] | carry_bits[i - 1] + /// Computes `self << 1` in constant-time, returning the overflowing bit as a `Word` that is either 0...0 or 1...1. + pub(crate) const fn shl_1(&self) -> (Self, Word) { + let mut shifted_bits = [0; LIMBS]; + let mut i = 0; + while i < LIMBS { + shifted_bits[i] = self.limbs[i].0 << 1; + i += 1; + } + + let mut carry_bits = [0; LIMBS]; + let mut i = 0; + while i < LIMBS { + carry_bits[i] = self.limbs[i].0 >> HI_BIT; + i += 1; + } + + let mut limbs = [Limb(0); LIMBS]; + + limbs[0] = Limb(shifted_bits[0]); + let mut i = 1; + while i < LIMBS { + limbs[i] = Limb(shifted_bits[i] | carry_bits[i - 1]); + i += 1; } - Choice::from(carry_bits[LIMBS - 1].0 as u8) + ( + UInt::new(limbs), + carry_bits[LIMBS - 1].wrapping_mul(Word::MAX), + ) } /// Computes `self << shift`. diff --git a/src/uint/shr.rs b/src/uint/shr.rs index 23d72c41..610ab0bc 100644 --- a/src/uint/shr.rs +++ b/src/uint/shr.rs @@ -1,23 +1,39 @@ //! [`UInt`] bitwise right shift operations. -use subtle::Choice; - use super::UInt; -use crate::Limb; +use crate::{limb::HI_BIT, Limb, Word}; use core::ops::{Shr, ShrAssign}; impl UInt { - /// Computes `self >> 1` in constant-time, returning the overflowing bit as a `Choice`. - pub fn shr_1(&mut self) -> Choice { - let shifted_bits = self.limbs.map(|x| x >> 1); - let carry_bits = self.limbs.map(|x| x << 63); + /// Computes `self >> 1` in constant-time, returning the overflowing bit as a `Word` that is either 0...0 or 1...1. + pub(crate) const fn shr_1(&self) -> (Self, Word) { + let mut shifted_bits = [0; LIMBS]; + let mut i = 0; + while i < LIMBS { + shifted_bits[i] = self.limbs[i].0 >> 1; + i += 1; + } + + let mut carry_bits = [0; LIMBS]; + let mut i = 0; + while i < LIMBS { + carry_bits[i] = self.limbs[i].0 << HI_BIT; + i += 1; + } - for i in 0..(LIMBS - 1) { - self.limbs[i] = shifted_bits[i] | carry_bits[i + 1] + let mut limbs = [Limb(0); LIMBS]; + + let mut i = 0; + while i < (LIMBS - 1) { + limbs[i] = Limb(shifted_bits[i] | carry_bits[i + 1]); + i += 1; } - self.limbs[LIMBS - 1] = shifted_bits[LIMBS - 1]; + limbs[LIMBS - 1] = Limb(shifted_bits[LIMBS - 1]); - Choice::from((carry_bits[0] >> 63).0 as u8) + ( + UInt::new(limbs), + (carry_bits[0] >> HI_BIT).wrapping_mul(Word::MAX), + ) } /// Computes `self >> n`. diff --git a/src/uint/sub.rs b/src/uint/sub.rs index 6103a4bc..ad695bbc 100644 --- a/src/uint/sub.rs +++ b/src/uint/sub.rs @@ -1,9 +1,9 @@ //! [`UInt`] addition operations. use super::UInt; -use crate::{Checked, CheckedSub, Limb, Wrapping, Zero}; +use crate::{Checked, CheckedSub, Limb, Word, Wrapping, Zero}; use core::ops::{Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable, CtOption}; +use subtle::CtOption; impl UInt { /// Computes `a - (b + borrow)`, returning the result along with the new borrow. @@ -39,13 +39,13 @@ impl UInt { self.sbb(rhs, Limb::ZERO).0 } - /// Perform wrapping subtraction, returning the underflow bit as a `Choice`. - pub fn conditional_wrapping_sub(&mut self, rhs: &Self, choice: Choice) -> Choice { - let actual_rhs = UInt::conditional_select(&UInt::ZERO, rhs, choice); - let (sum, borrow) = self.sbb(&actual_rhs, Limb::ZERO); - *self = sum; + /// Perform wrapping subtraction, returning the underflow bit as a `Word` that is either 0...0 or 1...1. + pub(crate) const fn conditional_wrapping_sub(&self, rhs: &Self, choice: Word) -> (Self, Word) { + let actual_rhs = UInt::ct_select(UInt::ZERO, *rhs, choice); + let (res, borrow) = self.sbb(&actual_rhs, Limb::ZERO); - Choice::from((borrow.0 != 0) as u8) + // Here we do not use a multiplication because the result is already 0...0 or 1...1 + (res, borrow.0) } } From 154d82ee6145ce1566fcc03a56514dba2f08bbdc Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Fri, 14 Oct 2022 15:07:15 +0200 Subject: [PATCH 07/23] Checkpoint: working modulus macro --- src/uint/div.rs | 49 +++++ src/uint/modular/add.rs | 42 ++--- src/uint/modular/macros.rs | 20 ++ src/uint/modular/mod.rs | 365 ++++++++++++++++++++----------------- src/uint/modular/mul.rs | 14 +- src/uint/modular/pow.rs | 110 +++++------ src/uint/mul.rs | 7 +- src/uint/pow_mod.rs | 84 ++++----- src/uint/shl.rs | 37 +++- src/uint/shr.rs | 37 +++- src/uint/sub.rs | 4 +- 11 files changed, 468 insertions(+), 301 deletions(-) create mode 100644 src/uint/modular/macros.rs diff --git a/src/uint/div.rs b/src/uint/div.rs index f7d9d6bf..fa8020f2 100644 --- a/src/uint/div.rs +++ b/src/uint/div.rs @@ -68,6 +68,42 @@ impl UInt { (rem, (is_some & 1) as u8) } + /// Computes `self` % `rhs`, returns the remainder and + /// and 1 for is_some or 0 for is_none. The results can be wrapped in [`CtOption`]. + /// NOTE: Use only if you need to access const fn. Otherwise use `reduce` + /// This is variable only with respect to `rhs`. + /// + /// When used with a fixed `rhs`, this function is constant-time with respect + /// to `self`. + pub(crate) const fn ct_reduce_wide(lower_upper: (Self, Self), rhs: &Self) -> (Self, u8) { + let mb = rhs.bits_vartime(); + + // The number of bits to consider is two sets of limbs * BIT_SIZE - mb (modulus bitcount) + let mut bd = (2 * LIMBS * Limb::BIT_SIZE) - mb; + + // The wide integer to reduce, split into two halves + let (mut lower, mut upper) = lower_upper; + + // Factor of the modulus, split into two halves + let mut c = Self::shl_vartime_wide((*rhs, UInt::ZERO), bd); + + loop { + let (lower_sub, borrow) = lower.sbb(&c.0, Limb::ZERO); + let (upper_sub, borrow) = upper.sbb(&c.1, borrow); + + lower = Self::ct_select(lower_sub, lower, borrow.0); + upper = Self::ct_select(upper_sub, upper, borrow.0); + if bd == 0 { + break; + } + bd -= 1; + c = Self::shr_vartime_wide(c, 1); + } + + let is_some = Limb(mb as Word).is_nonzero(); + (lower, (is_some & 1) as u8) + } + /// Computes `self` % 2^k. Faster than reduce since its a power of 2. /// Limited to 2^16-1 since UInt doesn't support higher. pub const fn reduce2k(&self, k: usize) -> Self { @@ -466,6 +502,19 @@ mod tests { assert_eq!(r, U256::from(3u8)); } + #[test] + fn reduce_tests_wide_zero_padded() { + let (r, is_some) = U256::ct_reduce_wide((U256::from(10u8), U256::ZERO), &U256::from(2u8)); + assert_eq!(is_some, 1); + assert_eq!(r, U256::ZERO); + let (r, is_some) = U256::ct_reduce_wide((U256::from(10u8), U256::ZERO), &U256::from(3u8)); + assert_eq!(is_some, 1); + assert_eq!(r, U256::ONE); + let (r, is_some) = U256::ct_reduce_wide((U256::from(10u8), U256::ZERO), &U256::from(7u8)); + assert_eq!(is_some, 1); + assert_eq!(r, U256::from(3u8)); + } + #[test] fn reduce_max() { let mut a = U256::ZERO; diff --git a/src/uint/modular/add.rs b/src/uint/modular/add.rs index beaee538..16851593 100644 --- a/src/uint/modular/add.rs +++ b/src/uint/modular/add.rs @@ -2,46 +2,46 @@ use core::ops::AddAssign; use crate::UInt; -use super::Modular; +use super::{Residue, ResidueParams}; -impl AddAssign<&UInt> for Modular { +impl, const LIMBS: usize> AddAssign<&UInt> for Residue { fn add_assign(&mut self, rhs: &UInt) { - *self += &Modular::new(*rhs, self.modulus_params); + *self += &Residue::new(*rhs); } } -impl AddAssign<&Self> for Modular { +impl, const LIMBS: usize> AddAssign<&Self> for Residue { fn add_assign(&mut self, rhs: &Self) { // TODO: Can we easily verify that these have the same MontgomeryParams? (e.g. using a debug_assert) - self.value = self.value.add_mod(&rhs.value, &self.modulus_params.modulus); + self.montgomery_form = self.montgomery_form.add_mod(&rhs.montgomery_form, &MOD::MODULUS); } } #[cfg(test)] mod tests { use crate::{ - uint::modular::{Modular, MontgomeryParams}, + uint::modular::{Residue, ResidueParams}, U256, }; - #[test] - fn add_overflow() { - let modulus = - U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); - let modulus_params = MontgomeryParams::new(modulus); + // #[test] + // fn add_overflow() { + // let modulus = + // U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); + // let modulus_params = ResidueParams::new(modulus); - let x = - U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); - let mut x_mod = Modular::new(x, modulus_params); + // let x = + // U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); + // let mut x_mod = Residue::new(x); - let y = - U256::from_be_hex("d5777c45019673125ad240f83094d4252d829516fac8601ed01979ec1ec1a251"); + // let y = + // U256::from_be_hex("d5777c45019673125ad240f83094d4252d829516fac8601ed01979ec1ec1a251"); - x_mod += &y; + // x_mod += &y; - let expected = - U256::from_be_hex("1a2472fde50286541d97ca6a3592dd75beb9c9646e40c511b82496cfc3926956"); + // let expected = + // U256::from_be_hex("1a2472fde50286541d97ca6a3592dd75beb9c9646e40c511b82496cfc3926956"); - assert_eq!(expected, x_mod.retrieve()); - } + // assert_eq!(expected, x_mod.retrieve()); + // } } diff --git a/src/uint/modular/macros.rs b/src/uint/modular/macros.rs new file mode 100644 index 00000000..5bf6425e --- /dev/null +++ b/src/uint/modular/macros.rs @@ -0,0 +1,20 @@ +// TODO: Use `adt_const_params` once stabilized to make a `Residue` generic around a modulus rather than having to implement a ZST + trait +macro_rules! impl_modulus { + ($name:ident, $uint_type:ty, $value:expr) => { + use crate::traits::{Encoding, Concat, Split}; + use crate::{Limb, Word}; + + #[derive(Clone, Copy)] + pub struct $name {} + impl ResidueParams<{nlimbs!(<$uint_type>::BIT_SIZE)}> for $name + where + UInt<4>: Concat>, + UInt: Split, + { + const MODULUS: UInt<{nlimbs!(<$uint_type>::BIT_SIZE)}> = <$uint_type>::from_be_hex($value); + const R: UInt<{nlimbs!(<$uint_type>::BIT_SIZE)}> = UInt::MAX.ct_reduce(&Self::MODULUS).0.wrapping_add(&UInt::ONE); + const R2: UInt<{nlimbs!(<$uint_type>::BIT_SIZE)}> = UInt::ct_reduce_wide(Self::R.square_wide(), &Self::MODULUS).0; + const MOD_NEG_INV: Limb = Limb(Word::MIN.wrapping_sub(Self::MODULUS.inv_mod2k(Word::BITS as usize).limbs[0].0)); + } + }; +} diff --git a/src/uint/modular/mod.rs b/src/uint/modular/mod.rs index 4fbecf44..0d659d0f 100644 --- a/src/uint/modular/mod.rs +++ b/src/uint/modular/mod.rs @@ -1,81 +1,98 @@ +use core::marker::PhantomData; + use subtle::{Choice, ConditionallySelectable}; -use crate::{CheckedAdd, Concat, Limb, Split, UInt, WideWord, Word}; +use crate::{CheckedAdd, Concat, Limb, Split, UInt, WideWord, Word, U256}; + +#[macro_use] +mod macros; mod add; mod mul; mod pow; -#[derive(Debug, Clone, Copy)] -pub struct MontgomeryParams { - modulus: UInt, - montgomery_r: UInt, - montgomery_r2: UInt, +pub trait ResidueParams: Copy { + const MODULUS: UInt; + const R: UInt; + const R2: UInt; // We only need the LSB because during reduction this value is multiplied modulo 2**64. - modulus_neg_inv: Limb, + const MOD_NEG_INV: Limb; } -impl MontgomeryParams -where - UInt: Concat>, - UInt: Split>, -{ - /// Note that the modulus must be tight (i.e. it should be at least somewhat close in size to `LIMB_COUNT`). - pub fn new(modulus: UInt) -> Self { - let montgomery_r = UInt::MAX - .reduce(&modulus) - .unwrap() - .checked_add(&UInt::ONE) - .unwrap(); - - let double_modulus = (UInt::::ZERO).concat(&modulus); - let (_, montgomery_r2) = montgomery_r - .square() - .reduce(&double_modulus) - .unwrap() - .split(); - - let modulus_neg_inv = - Limb(Word::MIN.wrapping_sub(modulus.inv_mod2k(Word::BITS as usize).limbs[0].0)); - - MontgomeryParams { - modulus, - montgomery_r, - montgomery_r2, - modulus_neg_inv, - } - } -} +// impl ResidueParams +// where +// UInt: Concat>, +// UInt: Split>, +// { +// /// Note that the modulus must be tight (i.e. it should be at least somewhat close in size to `LIMB_COUNT`). +// pub fn new(modulus: UInt) -> Self { +// let montgomery_r = UInt::MAX +// .reduce(&modulus) +// .unwrap() +// .checked_add(&UInt::ONE) +// .unwrap(); + +// let double_modulus = (UInt::::ZERO).concat(&modulus); +// let (_, montgomery_r2) = montgomery_r +// .square() +// .reduce(&double_modulus) +// .unwrap() +// .split(); + +// let modulus_neg_inv = +// Limb(Word::MIN.wrapping_sub(modulus.inv_mod2k(Word::BITS as usize).limbs[0].0)); + +// ResidueParams { +// modulus, +// montgomery_r, +// montgomery_r2, +// modulus_neg_inv, +// } +// } +// } + +// #[derive(Clone, Copy)] +// pub struct ThisModulus {} +// impl ResidueParams<4> for ThisModulus +// where +// UInt<4>: Concat>, +// UInt: Split>, +// { +// const MODULUS: UInt<4> = UInt::<4>::from_be_hex("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); +// const R: UInt<4> = compute_montgomery_r(Self::MODULUS); +// const R2: UInt<4> = compute_montgomery_r2(Self::MODULUS, Self::R); +// const MOD_NEG_INV: Limb = compute_montgomery_mod_neg_inv(Self::MODULUS); +// } // TODO: We should consider taking modulus_params as a reference #[derive(Debug, Clone, Copy)] -pub struct Modular { - value: UInt, - modulus_params: MontgomeryParams, +pub struct Residue +where MOD: ResidueParams +{ + montgomery_form: UInt, + phantom: PhantomData, } -impl Modular { - pub fn new(integer: UInt, modulus_params: MontgomeryParams) -> Self { - let mut modular_integer = Modular { - value: integer, - modulus_params, +impl, const LIMBS: usize> Residue { + pub const ONE: Self = Self { + montgomery_form: MOD::R, + phantom: PhantomData, + }; + + pub fn new(integer: UInt) -> Self { + let mut modular_integer = Residue { + montgomery_form: integer, + phantom: PhantomData }; - let product = integer.mul_wide(&modulus_params.montgomery_r2); - modular_integer.value = montgomery_reduction(product, &modulus_params); + let product = integer.mul_wide(&MOD::R2); + modular_integer.montgomery_form = montgomery_reduction::(product); modular_integer } pub fn retrieve(&self) -> UInt { - montgomery_reduction((self.value, UInt::ZERO), &self.modulus_params) - } - - pub fn one(modulus_params: MontgomeryParams) -> Self { - Modular { - value: modulus_params.montgomery_r, - modulus_params, - } + montgomery_reduction::((self.montgomery_form, UInt::ZERO)) } /// Return `a` if `c`==0 or `b` if `c`==`Word::MAX`. @@ -83,43 +100,49 @@ impl Modular { /// Const-friendly: we can't yet use `subtle` in `const fn` contexts. #[inline] pub(crate) const fn ct_select(a: Self, b: Self, c: Word) -> Self { - Modular { - value: UInt::ct_select(a.value, b.value, c), - modulus_params: a.modulus_params, + Residue { + montgomery_form: UInt::ct_select(a.montgomery_form, b.montgomery_form, c), + phantom: PhantomData } } } /// Algorithm 14.32 in Handbook of Applied Cryptography (https://cacr.uwaterloo.ca/hac/about/chap14.pdf) -fn montgomery_reduction( +fn montgomery_reduction, const LIMBS: usize>( lower_upper: (UInt, UInt), - modulus_params: &MontgomeryParams, ) -> UInt { let (mut lower, mut upper) = lower_upper; let mut meta_carry = 0; - for i in 0..LIMBS { + + let mut i = 0; + while i < LIMBS { let u = (lower.limbs[i] .0 - .wrapping_mul(modulus_params.modulus_neg_inv.0)) as WideWord; + .wrapping_mul(MOD::MOD_NEG_INV.0)) as WideWord; - let new_limb = (u * modulus_params.modulus.limbs[0].0 as WideWord) + let new_limb = (u * MOD::MODULUS.limbs[0].0 as WideWord) .wrapping_add(lower.limbs[i].0 as WideWord); let mut carry = new_limb >> Word::BITS; - for j in 1..(LIMBS - i) { - let new_limb = (u * modulus_params.modulus.limbs[j].0 as WideWord) + let mut j = 1; + while j < (LIMBS - i) { + let new_limb = (u * MOD::MODULUS.limbs[j].0 as WideWord) .wrapping_add(lower.limbs[i + j].0 as WideWord) .wrapping_add(carry); carry = new_limb >> Word::BITS; lower.limbs[i + j] = Limb(new_limb as Word); + + j += 1; } - for j in (LIMBS - i)..LIMBS { - let new_limb = (u * modulus_params.modulus.limbs[j].0 as WideWord) + while j < LIMBS { + let new_limb = (u * MOD::MODULUS.limbs[j].0 as WideWord) .wrapping_add(upper.limbs[i + j - LIMBS].0 as WideWord) .wrapping_add(carry); carry = new_limb >> Word::BITS; upper.limbs[i + j - LIMBS] = Limb(new_limb as Word); + + j += 1; } let new_sum = (upper.limbs[i].0 as WideWord) @@ -127,26 +150,28 @@ fn montgomery_reduction( .wrapping_add(meta_carry); meta_carry = new_sum >> Word::BITS; upper.limbs[i] = Limb(new_sum as Word); + + i += 1; } // Division is simply taking the upper half of the limbs // Final reduction (at this point, the value is at most 2 * modulus) let must_reduce = - Choice::from(meta_carry as u8) | Choice::from((upper >= modulus_params.modulus) as u8); + Choice::from(meta_carry as u8) | Choice::from((upper >= MOD::MODULUS) as u8); upper = upper.wrapping_sub(&UInt::conditional_select( &UInt::ZERO, - &modulus_params.modulus, + &MOD::MODULUS, must_reduce, )); upper } -impl ConditionallySelectable for Modular { +impl + Copy, const LIMBS: usize> ConditionallySelectable for Residue { fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Modular { - value: UInt::conditional_select(&a.value, &b.value, choice), - modulus_params: a.modulus_params, + Residue { + montgomery_form: UInt::conditional_select(&a.montgomery_form, &b.montgomery_form, choice), + phantom: PhantomData, } } } @@ -154,117 +179,115 @@ impl ConditionallySelectable for Modular { #[cfg(test)] mod tests { use crate::{ - uint::modular::{montgomery_reduction, Modular, MontgomeryParams}, + uint::modular::{montgomery_reduction, Residue, ResidueParams}, UInt, U256, U64, }; + impl_modulus!(ThisModulus, U256, "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); + #[test] fn test_montgomery_params() { - let modulus = - U256::from_be_hex("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); - let modulus_params = MontgomeryParams::new(modulus); - assert_eq!( - modulus_params.montgomery_r, + ThisModulus::R, U256::from_be_hex("1824b159acc5056f998c4fefecbc4ff55884b7fa0003480200000001fffffffe") ); assert_eq!( - modulus_params.montgomery_r2, + ThisModulus::R2, U256::from_be_hex("0748d9d99f59ff1105d314967254398f2b6cedcb87925c23c999e990f3f29c6d") ); assert_eq!( - modulus_params.modulus_neg_inv, + ThisModulus::MOD_NEG_INV, U64::from_be_hex("fffffffeffffffff").limbs[0] ); } - #[test] - fn test_reducing_r() { - let modulus = - U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); - let modulus_params = MontgomeryParams::new(modulus); - - // Divide the value R by R, which should equal 1 - assert_eq!( - montgomery_reduction((modulus_params.montgomery_r, UInt::ZERO), &modulus_params), - UInt::ONE - ); - } - - #[test] - fn test_reducing_r2() { - let modulus = - U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); - let modulus_params = MontgomeryParams::new(modulus); - - // Divide the value R^2 by R, which should equal R - assert_eq!( - montgomery_reduction((modulus_params.montgomery_r2, UInt::ZERO), &modulus_params), - modulus_params.montgomery_r - ); - } - - #[test] - fn test_reducing_r2_wide() { - let modulus = - U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); - let modulus_params = MontgomeryParams::new(modulus); - - // Divide the value R^2 by R, which should equal R - let (hi, lo) = modulus_params.montgomery_r.square().split(); - assert_eq!( - montgomery_reduction((lo, hi), &modulus_params), - modulus_params.montgomery_r - ); - } - - #[test] - fn test_reducing_xr_wide() { - let modulus = - U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); - let modulus_params = MontgomeryParams::new(modulus); - - // Reducing xR should return x - let x = - U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); - let product = x.mul_wide(&modulus_params.montgomery_r); - assert_eq!(montgomery_reduction(product, &modulus_params), x); - } - - #[test] - fn test_reducing_xr2_wide() { - let modulus = - U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); - let modulus_params = MontgomeryParams::new(modulus); - - // Reducing xR^2 should return xR - let x = - U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); - let product = x.mul_wide(&modulus_params.montgomery_r2); - - // Computing xR mod modulus without Montgomery reduction - let (lo, hi) = x.mul_wide(&modulus_params.montgomery_r); - let c = hi.concat(&lo); - let red = c.reduce(&U256::ZERO.concat(&modulus)).unwrap(); - let (hi, lo) = red.split(); - assert_eq!(hi, UInt::ZERO); - - assert_eq!(montgomery_reduction(product, &modulus_params), lo); - } - - #[test] - fn test_new_retrieve() { - let modulus = - U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); - - //let modulus = U256::new([Limb(0xffffffff00000001), Limb(0x53bda402fffe5bfe), Limb(0x3339d80809a1d805), Limb(0x73eda753299d7d48)]); - let modulus_params = MontgomeryParams::new(modulus); - - let x = - U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); - let x_mod = Modular::new(x, modulus_params); - - // Confirm that when creating a Modular and retrieving the value, that it equals the original - assert_eq!(x, x_mod.retrieve()); - } + // #[test] + // fn test_reducing_r() { + // let modulus = + // U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); + // let modulus_params = ResidueParams::new(modulus); + + // // Divide the value R by R, which should equal 1 + // assert_eq!( + // montgomery_reduction((modulus_params.montgomery_r, UInt::ZERO)), + // UInt::ONE + // ); + // } + + // #[test] + // fn test_reducing_r2() { + // let modulus = + // U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); + // let modulus_params = ResidueParams::new(modulus); + + // // Divide the value R^2 by R, which should equal R + // assert_eq!( + // montgomery_reduction((modulus_params.montgomery_r2, UInt::ZERO)), + // modulus_params.montgomery_r + // ); + // } + + // #[test] + // fn test_reducing_r2_wide() { + // let modulus = + // U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); + // let modulus_params = ResidueParams::new(modulus); + + // // Divide the value R^2 by R, which should equal R + // let (hi, lo) = modulus_params.montgomery_r.square().split(); + // assert_eq!( + // montgomery_reduction((lo, hi)), + // modulus_params.montgomery_r + // ); + // } + + // #[test] + // fn test_reducing_xr_wide() { + // let modulus = + // U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); + // let modulus_params = ResidueParams::new(modulus); + + // // Reducing xR should return x + // let x = + // U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); + // let product = x.mul_wide(&modulus_params.montgomery_r); + // assert_eq!(montgomery_reduction(product), x); + // } + + // #[test] + // fn test_reducing_xr2_wide() { + // let modulus = + // U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); + // let modulus_params = ResidueParams::new(modulus); + + // // Reducing xR^2 should return xR + // let x = + // U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); + // let product = x.mul_wide(&modulus_params.montgomery_r2); + + // // Computing xR mod modulus without Montgomery reduction + // let (lo, hi) = x.mul_wide(&modulus_params.montgomery_r); + // let c = hi.concat(&lo); + // let red = c.reduce(&U256::ZERO.concat(&modulus)).unwrap(); + // let (hi, lo) = red.split(); + // assert_eq!(hi, UInt::ZERO); + + // assert_eq!(montgomery_reduction(product), lo); + // } + + // #[test] + // fn test_new_retrieve() { + // let modulus = + // U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); + + // //let modulus = U256::new([Limb(0xffffffff00000001), Limb(0x53bda402fffe5bfe), Limb(0x3339d80809a1d805), Limb(0x73eda753299d7d48)]); + // let modulus_params = ResidueParams::new(modulus); + + // let x = + // U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); + // let x_mod = Residue::new(x); + + // // Confirm that when creating a Modular and retrieving the value, that it equals the original + // assert_eq!(x, x_mod.retrieve()); + // } } diff --git a/src/uint/modular/mul.rs b/src/uint/modular/mul.rs index 7ee58778..ad7c196e 100644 --- a/src/uint/modular/mul.rs +++ b/src/uint/modular/mul.rs @@ -2,22 +2,22 @@ use core::ops::MulAssign; use crate::{Concat, Split, UInt}; -use super::{montgomery_reduction, Modular}; +use super::{montgomery_reduction, Residue, ResidueParams}; -impl Modular +impl, const LIMBS: usize, const DLIMBS: usize> Residue where UInt: Concat>, UInt: Split>, { pub fn square(&mut self) { - let (hi, lo) = self.value.square().split(); - self.value = montgomery_reduction((lo, hi), &self.modulus_params); + let (hi, lo) = self.montgomery_form.square().split(); + self.montgomery_form = montgomery_reduction::((lo, hi)); } } -impl MulAssign for Modular { +impl, const LIMBS: usize> MulAssign for Residue { fn mul_assign(&mut self, rhs: Self) { - let product = self.value.mul_wide(&rhs.value); - self.value = montgomery_reduction(product, &self.modulus_params); + let product = self.montgomery_form.mul_wide(&rhs.montgomery_form); + self.montgomery_form = montgomery_reduction::(product); } } diff --git a/src/uint/modular/pow.rs b/src/uint/modular/pow.rs index bd0001ce..dc691f5f 100644 --- a/src/uint/modular/pow.rs +++ b/src/uint/modular/pow.rs @@ -1,20 +1,20 @@ use crate::{Concat, Split, UInt, Word}; -use super::Modular; +use super::{Residue, ResidueParams}; -impl Modular +impl, const LIMBS: usize, const DLIMBS: usize> Residue where UInt: Concat>, UInt: Split>, { - pub fn pow(&self, exponent: &UInt) -> Modular { + pub fn pow(&self, exponent: &UInt) -> Residue { self.pow_specific(exponent, LIMBS * Word::BITS as usize) } /// Perform modular exponentiation using Montgomery's ladder. `exponent_bits` represents the number of bits to take into account for the exponent. Note that this value is leaked in the time pattern. - pub fn pow_specific(&self, exponent: &UInt, exponent_bits: usize) -> Modular { - let mut x1: Modular = Modular::one(self.modulus_params); - let mut x2: Modular = *self; + pub fn pow_specific(&self, exponent: &UInt, exponent_bits: usize) -> Residue { + let mut x1: Residue = Residue::ONE; + let mut x2: Residue = *self; // Shift the exponent all the way to the left so the leftmost bit is the MSB of the `UInt` let mut n: UInt = @@ -26,14 +26,14 @@ where let (next_n, overflow) = n.shl_1(); n = next_n; - let mut product: Modular = x1; + let mut product: Residue = x1; product *= x2; - let mut square = Modular::ct_select(x1, x2, overflow); + let mut square = Residue::ct_select(x1, x2, overflow); square.square(); - x1 = Modular::ct_select(square, product, overflow); - x2 = Modular::ct_select(product, square, overflow); + x1 = Residue::ct_select(square, product, overflow); + x2 = Residue::ct_select(product, square, overflow); } x1 @@ -43,71 +43,71 @@ where #[cfg(test)] mod tests { use crate::{ - uint::modular::{Modular, MontgomeryParams}, + uint::modular::{Residue, ResidueParams}, U1024, U64, }; - #[test] - fn test_powmod_specific_mini() { - let modulus = U64::from(11u64); - let modulus_params = MontgomeryParams::new(modulus); + // #[test] + // fn test_powmod_specific_mini() { + // let modulus = U64::from(11u64); + // let modulus_params = ResidueParams::new(modulus); - let base = U64::from(3u64); - let base_mod = Modular::new(base, modulus_params); + // let base = U64::from(3u64); + // let base_mod = Residue::new(base); - let exponent = U64::from(7u64); + // let exponent = U64::from(7u64); - let res = base_mod.pow_specific(&exponent, 3); + // let res = base_mod.pow_specific(&exponent, 3); - let expected = U64::from(9u64); - assert_eq!(res.retrieve(), expected); - } + // let expected = U64::from(9u64); + // assert_eq!(res.retrieve(), expected); + // } - #[test] - fn test_powmod_mini() { - let modulus = U64::from(11u64); - let modulus_params = MontgomeryParams::new(modulus); + // #[test] + // fn test_powmod_mini() { + // let modulus = U64::from(11u64); + // let modulus_params = ResidueParams::new(modulus); - let base = U64::from(3u64); - let base_mod = Modular::new(base, modulus_params); + // let base = U64::from(3u64); + // let base_mod = Residue::new(base); - let exponent = U64::from(7u64); + // let exponent = U64::from(7u64); - let res = base_mod.pow(&exponent); + // let res = base_mod.pow(&exponent); - let expected = U64::from(9u64); - assert_eq!(res.retrieve(), expected); - } + // let expected = U64::from(9u64); + // assert_eq!(res.retrieve(), expected); + // } - #[test] - fn test_powmod_small_base() { - let modulus = U1024::from_be_hex("D509E7854ABDC81921F669F1DC6F61359523F3949803E58ED4EA8BC16483DC6F37BFE27A9AC9EEA2969B357ABC5C0EE214BE16A7D4C58FC620D5B5A20AFF001AD198D3155E5799DC4EA76652D64983A7E130B5EACEBAC768D28D589C36EC749C558D0B64E37CD0775C0D0104AE7D98BA23C815185DD43CD8B16292FD94156767"); - let modulus_params = MontgomeryParams::new(modulus); + // #[test] + // fn test_powmod_small_base() { + // let modulus = U1024::from_be_hex("D509E7854ABDC81921F669F1DC6F61359523F3949803E58ED4EA8BC16483DC6F37BFE27A9AC9EEA2969B357ABC5C0EE214BE16A7D4C58FC620D5B5A20AFF001AD198D3155E5799DC4EA76652D64983A7E130B5EACEBAC768D28D589C36EC749C558D0B64E37CD0775C0D0104AE7D98BA23C815185DD43CD8B16292FD94156767"); + // let modulus_params = ResidueParams::new(modulus); - let base = U1024::from(105u64); - let base_mod = Modular::new(base, modulus_params); + // let base = U1024::from(105u64); + // let base_mod = Residue::new(base); - let exponent = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); + // let exponent = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); - let res = base_mod.pow(&exponent); + // let res = base_mod.pow(&exponent); - let expected = U1024::from_be_hex("84FADE244D8A183FD9D209B07312E017F53BBDF4377108EDB4FEAD2AEB1DFF83B6CE604A2DACF49E52574E69055C6E2D30980938CF259421E17AB277C67663712B185C565C97D3200659D83B287C1D8325BFD258C7DBA4BB2766A57C5F2A7EE3FA784A8669C54839F3D29E73215E7939B16E8293524D871D56F67759D553A242"); - assert_eq!(res.retrieve(), expected); - } + // let expected = U1024::from_be_hex("84FADE244D8A183FD9D209B07312E017F53BBDF4377108EDB4FEAD2AEB1DFF83B6CE604A2DACF49E52574E69055C6E2D30980938CF259421E17AB277C67663712B185C565C97D3200659D83B287C1D8325BFD258C7DBA4BB2766A57C5F2A7EE3FA784A8669C54839F3D29E73215E7939B16E8293524D871D56F67759D553A242"); + // assert_eq!(res.retrieve(), expected); + // } - #[test] - fn test_powmod_small_exponent() { - let modulus = U1024::from_be_hex("D509E7854ABDC81921F669F1DC6F61359523F3949803E58ED4EA8BC16483DC6F37BFE27A9AC9EEA2969B357ABC5C0EE214BE16A7D4C58FC620D5B5A20AFF001AD198D3155E5799DC4EA76652D64983A7E130B5EACEBAC768D28D589C36EC749C558D0B64E37CD0775C0D0104AE7D98BA23C815185DD43CD8B16292FD94156767"); - let modulus_params = MontgomeryParams::new(modulus); + // #[test] + // fn test_powmod_small_exponent() { + // let modulus = U1024::from_be_hex("D509E7854ABDC81921F669F1DC6F61359523F3949803E58ED4EA8BC16483DC6F37BFE27A9AC9EEA2969B357ABC5C0EE214BE16A7D4C58FC620D5B5A20AFF001AD198D3155E5799DC4EA76652D64983A7E130B5EACEBAC768D28D589C36EC749C558D0B64E37CD0775C0D0104AE7D98BA23C815185DD43CD8B16292FD94156767"); + // let modulus_params = ResidueParams::new(modulus); - let base = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); - let base_mod = Modular::new(base, modulus_params); + // let base = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); + // let base_mod = Residue::new(base); - let exponent = U1024::from(105u64); + // let exponent = U1024::from(105u64); - let res = base_mod.pow(&exponent); + // let res = base_mod.pow(&exponent); - let expected = U1024::from_be_hex("6B717DC3571BEC59C1E370780630280B05F13D9BB69E192DA75EAE2A817B270840881034B0AB9EE6B47382C58424AE00F90B88DFA7DFF7C9417B28E4C9DF170BCDFC4689140E9BA067FDB224831A33E2E18232655D15EA985EC0C8FB774BFA967B734A60DD8FC1F9214B7C262050F269C248F3255D5D1E7BD63626707232FF72"); - assert_eq!(res.retrieve(), expected); - } + // let expected = U1024::from_be_hex("6B717DC3571BEC59C1E370780630280B05F13D9BB69E192DA75EAE2A817B270840881034B0AB9EE6B47382C58424AE00F90B88DFA7DFF7C9417B28E4C9DF170BCDFC4689140E9BA067FDB224831A33E2E18232655D15EA985EC0C8FB774BFA967B734A60DD8FC1F9214B7C262050F269C248F3255D5D1E7BD63626707232FF72"); + // assert_eq!(res.retrieve(), expected); + // } } diff --git a/src/uint/mul.rs b/src/uint/mul.rs index ecb32fd1..916dca9a 100644 --- a/src/uint/mul.rs +++ b/src/uint/mul.rs @@ -75,7 +75,7 @@ impl UInt { self.mul_wide(rhs).0 } - /// Square self, returning a "wide" result. + /// Square self, returning a concatenated "wide" result. pub fn square(&self) -> ::Output where Self: Concat, @@ -83,6 +83,11 @@ impl UInt { let (lo, hi) = self.mul_wide(self); hi.concat(&lo) } + + /// Square self, returning a "wide" result in two parts as (lo, hi). + pub const fn square_wide(&self) -> (Self, Self) { + self.mul_wide(self) + } } impl CheckedMul<&UInt> for UInt { diff --git a/src/uint/pow_mod.rs b/src/uint/pow_mod.rs index e65d00db..d7028980 100644 --- a/src/uint/pow_mod.rs +++ b/src/uint/pow_mod.rs @@ -1,42 +1,42 @@ -use crate::{Concat, Split, UInt}; - -use super::modular::{Modular, MontgomeryParams}; - -impl UInt -where - UInt: Concat>, - UInt: Split>, -{ - /// Computes `self^exponent mod modulus` using Montgomery's ladder. If you are also performing other modular operations, consider using `Modular` and the associated `pow` function. - pub fn pow_mod(&self, exponent: &Self, modulus: &Self) -> Self { - let modulus_params = MontgomeryParams::new(*modulus); - let base_mod = Modular::new(*self, modulus_params); - - base_mod.pow(exponent).retrieve() - } - - /// Computes `self^exponent mod modulus` using Montgomery's ladder, but only considering the first `exponent_bits` bits of the exponent. This number is revealed from the timing pattern. If you are also performing other modular operations, consider using `Modular` and the associated `pow` function. - pub fn pow_mod_specific(&self, exponent: &Self, modulus: &Self, exponent_bits: usize) -> Self { - let modulus_params = MontgomeryParams::new(*modulus); - let base_mod = Modular::new(*self, modulus_params); - - base_mod.pow_specific(exponent, exponent_bits).retrieve() - } -} - -#[cfg(test)] -mod tests { - use crate::U64; - - #[test] - fn test_powmod_mini() { - let b = U64::from(3u64); - let e = U64::from(7u64); - let m = U64::from(11u64); - - let res = b.pow_mod(&e, &m); - - let expected = U64::from(9u64); - assert_eq!(res, expected); - } -} +// use crate::{Concat, Split, UInt}; + +// use super::modular::{Residue, ResidueParams}; + +// impl UInt +// where +// UInt: Concat>, +// UInt: Split>, +// { +// /// Computes `self^exponent mod modulus` using Montgomery's ladder. If you are also performing other modular operations, consider using `Modular` and the associated `pow` function. +// pub fn pow_mod(&self, exponent: &Self, modulus: &Self) -> Self { +// let modulus_params = ResidueParams::new(*modulus); +// let base_mod = Residue::new(*self, modulus_params); + +// base_mod.pow(exponent).retrieve() +// } + +// /// Computes `self^exponent mod modulus` using Montgomery's ladder, but only considering the first `exponent_bits` bits of the exponent. This number is revealed from the timing pattern. If you are also performing other modular operations, consider using `Modular` and the associated `pow` function. +// pub fn pow_mod_specific(&self, exponent: &Self, modulus: &Self, exponent_bits: usize) -> Self { +// let modulus_params = ResidueParams::new(*modulus); +// let base_mod = Residue::new(*self, modulus_params); + +// base_mod.pow_specific(exponent, exponent_bits).retrieve() +// } +// } + +// #[cfg(test)] +// mod tests { +// use crate::U64; + +// #[test] +// fn test_powmod_mini() { +// let b = U64::from(3u64); +// let e = U64::from(7u64); +// let m = U64::from(11u64); + +// let res = b.pow_mod(&e, &m); + +// let expected = U64::from(9u64); +// assert_eq!(res, expected); +// } +// } diff --git a/src/uint/shl.rs b/src/uint/shl.rs index f923e960..9c43e03a 100644 --- a/src/uint/shl.rs +++ b/src/uint/shl.rs @@ -67,6 +67,26 @@ impl UInt { Self { limbs } } + + /// Computes a left shift on a wide input as `(lo, hi)`. + /// + /// NOTE: this operation is variable time with respect to `n` *ONLY*. + /// + /// When used with a fixed `n`, this function is constant-time with respect + /// to `self`. + #[inline(always)] + pub const fn shl_vartime_wide(lower_upper: (Self, Self), n: usize) -> (Self, Self) { + let (lower, mut upper) = lower_upper; + let new_lower = lower.shl_vartime(n); + upper = upper.shl_vartime(n); + if n >= LIMBS * Limb::BIT_SIZE { + upper = upper.bitor(&lower.shl_vartime(n - LIMBS * Limb::BIT_SIZE)); + } else { + upper = upper.bitor(&lower.shr_vartime(LIMBS * Limb::BIT_SIZE - n)); + } + + (new_lower, upper) + } } impl Shl for UInt { @@ -105,7 +125,7 @@ impl ShlAssign for UInt { #[cfg(test)] mod tests { - use crate::U256; + use crate::{U256, UInt, Limb, U128}; const N: U256 = U256::from_be_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"); @@ -162,4 +182,19 @@ mod tests { fn shl64() { assert_eq!(N << 64, SIXTY_FOUR); } + + #[test] + fn shl_wide_1_1_128() { + assert_eq!(UInt::shl_vartime_wide((U128::ONE, U128::ONE), 128), (U128::ZERO, U128::ONE)); + } + + #[test] + fn shl_wide_max_0_1() { + assert_eq!(UInt::shl_vartime_wide((U128::MAX, U128::ZERO), 1), (U128::MAX.sbb(&U128::ONE, Limb::ZERO).0, U128::ONE)); + } + + #[test] + fn shl_wide_max_max_256() { + assert_eq!(UInt::shl_vartime_wide((U128::MAX, U128::MAX), 256), (U128::ZERO, U128::ZERO)); + } } diff --git a/src/uint/shr.rs b/src/uint/shr.rs index 610ab0bc..ca7f30f0 100644 --- a/src/uint/shr.rs +++ b/src/uint/shr.rs @@ -75,6 +75,26 @@ impl UInt { Self { limbs } } + + /// Computes a right shift on a wide input as `(lo, hi)`. + /// + /// NOTE: this operation is variable time with respect to `n` *ONLY*. + /// + /// When used with a fixed `n`, this function is constant-time with respect + /// to `self`. + #[inline(always)] + pub const fn shr_vartime_wide(lower_upper: (Self, Self), n: usize) -> (Self, Self) { + let (mut lower, upper) = lower_upper; + let new_upper = upper.shr_vartime(n); + lower = lower.shr_vartime(n); + if n >= LIMBS * Limb::BIT_SIZE { + lower = lower.bitor(&upper.shr_vartime(n - LIMBS * Limb::BIT_SIZE)); + } else { + lower = lower.bitor(&upper.shl_vartime(LIMBS * Limb::BIT_SIZE - n)); + } + + (lower, new_upper) + } } impl Shr for UInt { @@ -109,7 +129,7 @@ impl ShrAssign for UInt { #[cfg(test)] mod tests { - use crate::U256; + use crate::{U256, UInt, U128}; const N: U256 = U256::from_be_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"); @@ -121,4 +141,19 @@ mod tests { fn shr1() { assert_eq!(N >> 1, N_2); } + + #[test] + fn shr_wide_1_1_128() { + assert_eq!(UInt::shr_vartime_wide((U128::ONE, U128::ONE), 128), (U128::ONE, U128::ZERO)); + } + + #[test] + fn shr_wide_0_max_1() { + assert_eq!(UInt::shr_vartime_wide((U128::ZERO, U128::MAX), 1), (U128::ONE << 127, U128::MAX >> 1)); + } + + #[test] + fn shr_wide_max_max_256() { + assert_eq!(UInt::shr_vartime_wide((U128::MAX, U128::MAX), 256), (U128::ZERO, U128::ZERO)); + } } diff --git a/src/uint/sub.rs b/src/uint/sub.rs index ad695bbc..b0d1f49c 100644 --- a/src/uint/sub.rs +++ b/src/uint/sub.rs @@ -44,8 +44,8 @@ impl UInt { let actual_rhs = UInt::ct_select(UInt::ZERO, *rhs, choice); let (res, borrow) = self.sbb(&actual_rhs, Limb::ZERO); - // Here we do not use a multiplication because the result is already 0...0 or 1...1 - (res, borrow.0) + // Here we use a saturating multiplication to get the result to 0...0 or 1...1 + (res, borrow.0.saturating_mul(Word::MAX)) } } From 1814a0d6820f811bf5c76047a342ea81acd68455 Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Fri, 14 Oct 2022 16:59:53 +0200 Subject: [PATCH 08/23] Add tests and make remaining fn const --- src/lib.rs | 3 + src/uint/modular/add.rs | 54 ++++--- src/uint/modular/macros.rs | 40 ++++-- src/uint/modular/mod.rs | 281 ++++++++++++++++--------------------- src/uint/modular/pow.rs | 85 ++++------- src/uint/shl.rs | 17 ++- src/uint/shr.rs | 17 ++- 7 files changed, 237 insertions(+), 260 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4d376421..e0a81fe7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,9 @@ trivial_numeric_casts, unused_qualifications )] +// FIXME: This does not work in stable rust +#![feature(const_eval_limit)] +#![const_eval_limit = "0"] //! ## Usage //! diff --git a/src/uint/modular/add.rs b/src/uint/modular/add.rs index 16851593..0ed6fe4f 100644 --- a/src/uint/modular/add.rs +++ b/src/uint/modular/add.rs @@ -4,7 +4,20 @@ use crate::UInt; use super::{Residue, ResidueParams}; -impl, const LIMBS: usize> AddAssign<&UInt> for Residue { +impl, const LIMBS: usize> Residue { + pub fn add(&self, rhs: &Self) -> Self { + Residue { + montgomery_form: self + .montgomery_form + .add_mod(&rhs.montgomery_form, &MOD::MODULUS), + phantom: core::marker::PhantomData, + } + } +} + +impl, const LIMBS: usize> AddAssign<&UInt> + for Residue +{ fn add_assign(&mut self, rhs: &UInt) { *self += &Residue::new(*rhs); } @@ -13,35 +26,34 @@ impl, const LIMBS: usize> AddAssign<&UInt> for impl, const LIMBS: usize> AddAssign<&Self> for Residue { fn add_assign(&mut self, rhs: &Self) { // TODO: Can we easily verify that these have the same MontgomeryParams? (e.g. using a debug_assert) - self.montgomery_form = self.montgomery_form.add_mod(&rhs.montgomery_form, &MOD::MODULUS); + *self = self.add(rhs); } } #[cfg(test)] mod tests { - use crate::{ - uint::modular::{Residue, ResidueParams}, - U256, - }; + use crate::{traits::Encoding, uint::modular::ResidueParams, U256}; - // #[test] - // fn add_overflow() { - // let modulus = - // U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); - // let modulus_params = ResidueParams::new(modulus); + impl_modulus!( + Modulus, + U256, + "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551" + ); - // let x = - // U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); - // let mut x_mod = Residue::new(x); + #[test] + fn add_overflow() { + let x = + U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); + let mut x_mod = residue!(x, Modulus); - // let y = - // U256::from_be_hex("d5777c45019673125ad240f83094d4252d829516fac8601ed01979ec1ec1a251"); + let y = + U256::from_be_hex("d5777c45019673125ad240f83094d4252d829516fac8601ed01979ec1ec1a251"); - // x_mod += &y; + x_mod += &y; - // let expected = - // U256::from_be_hex("1a2472fde50286541d97ca6a3592dd75beb9c9646e40c511b82496cfc3926956"); + let expected = + U256::from_be_hex("1a2472fde50286541d97ca6a3592dd75beb9c9646e40c511b82496cfc3926956"); - // assert_eq!(expected, x_mod.retrieve()); - // } + assert_eq!(expected, x_mod.retrieve()); + } } diff --git a/src/uint/modular/macros.rs b/src/uint/modular/macros.rs index 5bf6425e..28711089 100644 --- a/src/uint/modular/macros.rs +++ b/src/uint/modular/macros.rs @@ -1,20 +1,38 @@ // TODO: Use `adt_const_params` once stabilized to make a `Residue` generic around a modulus rather than having to implement a ZST + trait +#[macro_export] macro_rules! impl_modulus { ($name:ident, $uint_type:ty, $value:expr) => { - use crate::traits::{Encoding, Concat, Split}; - use crate::{Limb, Word}; + // use $crate::traits::{Encoding, Concat, Split}; + // use $crate::{Limb, Word}; - #[derive(Clone, Copy)] + #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct $name {} - impl ResidueParams<{nlimbs!(<$uint_type>::BIT_SIZE)}> for $name + impl ResidueParams<{ nlimbs!(<$uint_type>::BIT_SIZE) }> for $name where - UInt<4>: Concat>, - UInt: Split, + $crate::UInt<{ nlimbs!(<$uint_type>::BIT_SIZE) }>: + $crate::traits::Concat>, + $crate::UInt: $crate::traits::Split, { - const MODULUS: UInt<{nlimbs!(<$uint_type>::BIT_SIZE)}> = <$uint_type>::from_be_hex($value); - const R: UInt<{nlimbs!(<$uint_type>::BIT_SIZE)}> = UInt::MAX.ct_reduce(&Self::MODULUS).0.wrapping_add(&UInt::ONE); - const R2: UInt<{nlimbs!(<$uint_type>::BIT_SIZE)}> = UInt::ct_reduce_wide(Self::R.square_wide(), &Self::MODULUS).0; - const MOD_NEG_INV: Limb = Limb(Word::MIN.wrapping_sub(Self::MODULUS.inv_mod2k(Word::BITS as usize).limbs[0].0)); + const LIMBS: usize = { nlimbs!(<$uint_type>::BIT_SIZE) }; + const MODULUS: $crate::UInt<{ nlimbs!(<$uint_type>::BIT_SIZE) }> = + <$uint_type>::from_be_hex($value); + const R: $crate::UInt<{ nlimbs!(<$uint_type>::BIT_SIZE) }> = $crate::UInt::MAX + .ct_reduce(&Self::MODULUS) + .0 + .wrapping_add(&$crate::UInt::ONE); + const R2: $crate::UInt<{ nlimbs!(<$uint_type>::BIT_SIZE) }> = + $crate::UInt::ct_reduce_wide(Self::R.square_wide(), &Self::MODULUS).0; + const MOD_NEG_INV: $crate::Limb = $crate::Limb( + $crate::Word::MIN + .wrapping_sub(Self::MODULUS.inv_mod2k($crate::Word::BITS as usize).limbs[0].0), + ); } - }; + }; +} + +#[macro_export] +macro_rules! residue { + ($variable:ident, $modulus:ident) => { + $crate::uint::modular::Residue::<$modulus, { $modulus::LIMBS }>::new($variable) + }; } diff --git a/src/uint/modular/mod.rs b/src/uint/modular/mod.rs index 0d659d0f..e276b0c9 100644 --- a/src/uint/modular/mod.rs +++ b/src/uint/modular/mod.rs @@ -2,7 +2,7 @@ use core::marker::PhantomData; use subtle::{Choice, ConditionallySelectable}; -use crate::{CheckedAdd, Concat, Limb, Split, UInt, WideWord, Word, U256}; +use crate::{Limb, UInt, WideWord, Word}; #[macro_use] mod macros; @@ -11,7 +11,11 @@ mod add; mod mul; mod pow; +/// The parameters to efficiently go to and from the Montgomery form for a given modulus. An easy way to generate these parameters is using the `impl_modulus!` macro. +/// +/// Unfortunately, `LIMBS` must be generic for now until const generics are stabilized. pub trait ResidueParams: Copy { + const LIMBS: usize; const MODULUS: UInt; const R: UInt; const R2: UInt; @@ -19,55 +23,11 @@ pub trait ResidueParams: Copy { const MOD_NEG_INV: Limb; } -// impl ResidueParams -// where -// UInt: Concat>, -// UInt: Split>, -// { -// /// Note that the modulus must be tight (i.e. it should be at least somewhat close in size to `LIMB_COUNT`). -// pub fn new(modulus: UInt) -> Self { -// let montgomery_r = UInt::MAX -// .reduce(&modulus) -// .unwrap() -// .checked_add(&UInt::ONE) -// .unwrap(); - -// let double_modulus = (UInt::::ZERO).concat(&modulus); -// let (_, montgomery_r2) = montgomery_r -// .square() -// .reduce(&double_modulus) -// .unwrap() -// .split(); - -// let modulus_neg_inv = -// Limb(Word::MIN.wrapping_sub(modulus.inv_mod2k(Word::BITS as usize).limbs[0].0)); - -// ResidueParams { -// modulus, -// montgomery_r, -// montgomery_r2, -// modulus_neg_inv, -// } -// } -// } - -// #[derive(Clone, Copy)] -// pub struct ThisModulus {} -// impl ResidueParams<4> for ThisModulus -// where -// UInt<4>: Concat>, -// UInt: Split>, -// { -// const MODULUS: UInt<4> = UInt::<4>::from_be_hex("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); -// const R: UInt<4> = compute_montgomery_r(Self::MODULUS); -// const R2: UInt<4> = compute_montgomery_r2(Self::MODULUS, Self::R); -// const MOD_NEG_INV: Limb = compute_montgomery_mod_neg_inv(Self::MODULUS); -// } - // TODO: We should consider taking modulus_params as a reference -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Residue -where MOD: ResidueParams +where + MOD: ResidueParams, { montgomery_form: UInt, phantom: PhantomData, @@ -79,10 +39,10 @@ impl, const LIMBS: usize> Residue { phantom: PhantomData, }; - pub fn new(integer: UInt) -> Self { + pub const fn new(integer: UInt) -> Self { let mut modular_integer = Residue { montgomery_form: integer, - phantom: PhantomData + phantom: PhantomData, }; let product = integer.mul_wide(&MOD::R2); @@ -91,7 +51,7 @@ impl, const LIMBS: usize> Residue { modular_integer } - pub fn retrieve(&self) -> UInt { + pub const fn retrieve(&self) -> UInt { montgomery_reduction::((self.montgomery_form, UInt::ZERO)) } @@ -102,13 +62,13 @@ impl, const LIMBS: usize> Residue { pub(crate) const fn ct_select(a: Self, b: Self, c: Word) -> Self { Residue { montgomery_form: UInt::ct_select(a.montgomery_form, b.montgomery_form, c), - phantom: PhantomData + phantom: PhantomData, } } } /// Algorithm 14.32 in Handbook of Applied Cryptography (https://cacr.uwaterloo.ca/hac/about/chap14.pdf) -fn montgomery_reduction, const LIMBS: usize>( +const fn montgomery_reduction, const LIMBS: usize>( lower_upper: (UInt, UInt), ) -> UInt { let (mut lower, mut upper) = lower_upper; @@ -117,12 +77,10 @@ fn montgomery_reduction, const LIMBS: usize>( let mut i = 0; while i < LIMBS { - let u = (lower.limbs[i] - .0 - .wrapping_mul(MOD::MOD_NEG_INV.0)) as WideWord; + let u = (lower.limbs[i].0.wrapping_mul(MOD::MOD_NEG_INV.0)) as WideWord; - let new_limb = (u * MOD::MODULUS.limbs[0].0 as WideWord) - .wrapping_add(lower.limbs[i].0 as WideWord); + let new_limb = + (u * MOD::MODULUS.limbs[0].0 as WideWord).wrapping_add(lower.limbs[i].0 as WideWord); let mut carry = new_limb >> Word::BITS; let mut j = 1; @@ -156,21 +114,23 @@ fn montgomery_reduction, const LIMBS: usize>( // Division is simply taking the upper half of the limbs // Final reduction (at this point, the value is at most 2 * modulus) - let must_reduce = - Choice::from(meta_carry as u8) | Choice::from((upper >= MOD::MODULUS) as u8); - upper = upper.wrapping_sub(&UInt::conditional_select( - &UInt::ZERO, - &MOD::MODULUS, - must_reduce, - )); + let must_reduce = (meta_carry as Word).saturating_mul(Word::MAX) + | ((upper.ct_cmp(&MOD::MODULUS) != -1) as Word).saturating_mul(Word::MAX); + upper = upper.wrapping_sub(&UInt::ct_select(UInt::ZERO, MOD::MODULUS, must_reduce)); upper } -impl + Copy, const LIMBS: usize> ConditionallySelectable for Residue { +impl + Copy, const LIMBS: usize> ConditionallySelectable + for Residue +{ fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { Residue { - montgomery_form: UInt::conditional_select(&a.montgomery_form, &b.montgomery_form, choice), + montgomery_form: UInt::conditional_select( + &a.montgomery_form, + &b.montgomery_form, + choice, + ), phantom: PhantomData, } } @@ -179,115 +139,116 @@ impl + Copy, const LIMBS: usize> ConditionallySelectab #[cfg(test)] mod tests { use crate::{ + traits::Encoding, uint::modular::{montgomery_reduction, Residue, ResidueParams}, UInt, U256, U64, }; - impl_modulus!(ThisModulus, U256, "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); + impl_modulus!( + Modulus1, + U256, + "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001" + ); #[test] fn test_montgomery_params() { assert_eq!( - ThisModulus::R, + Modulus1::R, U256::from_be_hex("1824b159acc5056f998c4fefecbc4ff55884b7fa0003480200000001fffffffe") ); assert_eq!( - ThisModulus::R2, + Modulus1::R2, U256::from_be_hex("0748d9d99f59ff1105d314967254398f2b6cedcb87925c23c999e990f3f29c6d") ); assert_eq!( - ThisModulus::MOD_NEG_INV, + Modulus1::MOD_NEG_INV, U64::from_be_hex("fffffffeffffffff").limbs[0] ); } - // #[test] - // fn test_reducing_r() { - // let modulus = - // U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); - // let modulus_params = ResidueParams::new(modulus); - - // // Divide the value R by R, which should equal 1 - // assert_eq!( - // montgomery_reduction((modulus_params.montgomery_r, UInt::ZERO)), - // UInt::ONE - // ); - // } - - // #[test] - // fn test_reducing_r2() { - // let modulus = - // U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); - // let modulus_params = ResidueParams::new(modulus); - - // // Divide the value R^2 by R, which should equal R - // assert_eq!( - // montgomery_reduction((modulus_params.montgomery_r2, UInt::ZERO)), - // modulus_params.montgomery_r - // ); - // } - - // #[test] - // fn test_reducing_r2_wide() { - // let modulus = - // U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); - // let modulus_params = ResidueParams::new(modulus); - - // // Divide the value R^2 by R, which should equal R - // let (hi, lo) = modulus_params.montgomery_r.square().split(); - // assert_eq!( - // montgomery_reduction((lo, hi)), - // modulus_params.montgomery_r - // ); - // } - - // #[test] - // fn test_reducing_xr_wide() { - // let modulus = - // U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); - // let modulus_params = ResidueParams::new(modulus); - - // // Reducing xR should return x - // let x = - // U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); - // let product = x.mul_wide(&modulus_params.montgomery_r); - // assert_eq!(montgomery_reduction(product), x); - // } - - // #[test] - // fn test_reducing_xr2_wide() { - // let modulus = - // U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); - // let modulus_params = ResidueParams::new(modulus); - - // // Reducing xR^2 should return xR - // let x = - // U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); - // let product = x.mul_wide(&modulus_params.montgomery_r2); - - // // Computing xR mod modulus without Montgomery reduction - // let (lo, hi) = x.mul_wide(&modulus_params.montgomery_r); - // let c = hi.concat(&lo); - // let red = c.reduce(&U256::ZERO.concat(&modulus)).unwrap(); - // let (hi, lo) = red.split(); - // assert_eq!(hi, UInt::ZERO); - - // assert_eq!(montgomery_reduction(product), lo); - // } - - // #[test] - // fn test_new_retrieve() { - // let modulus = - // U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); - - // //let modulus = U256::new([Limb(0xffffffff00000001), Limb(0x53bda402fffe5bfe), Limb(0x3339d80809a1d805), Limb(0x73eda753299d7d48)]); - // let modulus_params = ResidueParams::new(modulus); - - // let x = - // U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); - // let x_mod = Residue::new(x); - - // // Confirm that when creating a Modular and retrieving the value, that it equals the original - // assert_eq!(x, x_mod.retrieve()); - // } + impl_modulus!( + Modulus2, + U256, + "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551" + ); + + #[test] + fn test_reducing_r() { + // Divide the value R by R, which should equal 1 + assert_eq!( + montgomery_reduction::((Modulus2::R, UInt::ZERO)), + UInt::ONE + ); + } + + #[test] + fn test_reducing_r2() { + // Divide the value R^2 by R, which should equal R + assert_eq!( + montgomery_reduction::((Modulus2::R2, UInt::ZERO)), + Modulus2::R + ); + } + + #[test] + fn test_reducing_r2_wide() { + // Divide the value R^2 by R, which should equal R + let (hi, lo) = Modulus2::R.square().split(); + assert_eq!( + montgomery_reduction::((lo, hi)), + Modulus2::R + ); + } + + #[test] + fn test_reducing_xr_wide() { + // Reducing xR should return x + let x = + U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); + let product = x.mul_wide(&Modulus2::R); + assert_eq!( + montgomery_reduction::(product), + x + ); + } + + #[test] + fn test_reducing_xr2_wide() { + // Reducing xR^2 should return xR + let x = + U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); + let product = x.mul_wide(&Modulus2::R2); + + // Computing xR mod modulus without Montgomery reduction + let (lo, hi) = x.mul_wide(&Modulus2::R); + let c = hi.concat(&lo); + let red = c.reduce(&U256::ZERO.concat(&Modulus2::MODULUS)).unwrap(); + let (hi, lo) = red.split(); + assert_eq!(hi, UInt::ZERO); + + assert_eq!( + montgomery_reduction::(product), + lo + ); + } + + #[test] + fn test_new_retrieve() { + let x = + U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); + let x_mod = Residue::::new(x); + + // Confirm that when creating a Modular and retrieving the value, that it equals the original + assert_eq!(x, x_mod.retrieve()); + } + + #[test] + fn test_residue_macro() { + let x = + U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); + assert_eq!( + Residue::::new(x), + residue!(x, Modulus2) + ); + } } diff --git a/src/uint/modular/pow.rs b/src/uint/modular/pow.rs index dc691f5f..e8e5aef4 100644 --- a/src/uint/modular/pow.rs +++ b/src/uint/modular/pow.rs @@ -12,7 +12,11 @@ where } /// Perform modular exponentiation using Montgomery's ladder. `exponent_bits` represents the number of bits to take into account for the exponent. Note that this value is leaked in the time pattern. - pub fn pow_specific(&self, exponent: &UInt, exponent_bits: usize) -> Residue { + pub fn pow_specific( + &self, + exponent: &UInt, + exponent_bits: usize, + ) -> Residue { let mut x1: Residue = Residue::ONE; let mut x2: Residue = *self; @@ -42,72 +46,33 @@ where #[cfg(test)] mod tests { - use crate::{ - uint::modular::{Residue, ResidueParams}, - U1024, U64, - }; + use crate::{traits::Encoding, uint::modular::ResidueParams, U1024}; - // #[test] - // fn test_powmod_specific_mini() { - // let modulus = U64::from(11u64); - // let modulus_params = ResidueParams::new(modulus); + impl_modulus!(Modulus, U1024, "D509E7854ABDC81921F669F1DC6F61359523F3949803E58ED4EA8BC16483DC6F37BFE27A9AC9EEA2969B357ABC5C0EE214BE16A7D4C58FC620D5B5A20AFF001AD198D3155E5799DC4EA76652D64983A7E130B5EACEBAC768D28D589C36EC749C558D0B64E37CD0775C0D0104AE7D98BA23C815185DD43CD8B16292FD94156767"); - // let base = U64::from(3u64); - // let base_mod = Residue::new(base); + #[test] + fn test_powmod_small_base() { + let base = U1024::from(105u64); + let base_mod = residue!(base, Modulus); - // let exponent = U64::from(7u64); + let exponent = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); - // let res = base_mod.pow_specific(&exponent, 3); + let res = base_mod.pow(&exponent); - // let expected = U64::from(9u64); - // assert_eq!(res.retrieve(), expected); - // } - - // #[test] - // fn test_powmod_mini() { - // let modulus = U64::from(11u64); - // let modulus_params = ResidueParams::new(modulus); - - // let base = U64::from(3u64); - // let base_mod = Residue::new(base); - - // let exponent = U64::from(7u64); - - // let res = base_mod.pow(&exponent); - - // let expected = U64::from(9u64); - // assert_eq!(res.retrieve(), expected); - // } - - // #[test] - // fn test_powmod_small_base() { - // let modulus = U1024::from_be_hex("D509E7854ABDC81921F669F1DC6F61359523F3949803E58ED4EA8BC16483DC6F37BFE27A9AC9EEA2969B357ABC5C0EE214BE16A7D4C58FC620D5B5A20AFF001AD198D3155E5799DC4EA76652D64983A7E130B5EACEBAC768D28D589C36EC749C558D0B64E37CD0775C0D0104AE7D98BA23C815185DD43CD8B16292FD94156767"); - // let modulus_params = ResidueParams::new(modulus); - - // let base = U1024::from(105u64); - // let base_mod = Residue::new(base); - - // let exponent = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); - - // let res = base_mod.pow(&exponent); - - // let expected = U1024::from_be_hex("84FADE244D8A183FD9D209B07312E017F53BBDF4377108EDB4FEAD2AEB1DFF83B6CE604A2DACF49E52574E69055C6E2D30980938CF259421E17AB277C67663712B185C565C97D3200659D83B287C1D8325BFD258C7DBA4BB2766A57C5F2A7EE3FA784A8669C54839F3D29E73215E7939B16E8293524D871D56F67759D553A242"); - // assert_eq!(res.retrieve(), expected); - // } - - // #[test] - // fn test_powmod_small_exponent() { - // let modulus = U1024::from_be_hex("D509E7854ABDC81921F669F1DC6F61359523F3949803E58ED4EA8BC16483DC6F37BFE27A9AC9EEA2969B357ABC5C0EE214BE16A7D4C58FC620D5B5A20AFF001AD198D3155E5799DC4EA76652D64983A7E130B5EACEBAC768D28D589C36EC749C558D0B64E37CD0775C0D0104AE7D98BA23C815185DD43CD8B16292FD94156767"); - // let modulus_params = ResidueParams::new(modulus); + let expected = U1024::from_be_hex("84FADE244D8A183FD9D209B07312E017F53BBDF4377108EDB4FEAD2AEB1DFF83B6CE604A2DACF49E52574E69055C6E2D30980938CF259421E17AB277C67663712B185C565C97D3200659D83B287C1D8325BFD258C7DBA4BB2766A57C5F2A7EE3FA784A8669C54839F3D29E73215E7939B16E8293524D871D56F67759D553A242"); + assert_eq!(res.retrieve(), expected); + } - // let base = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); - // let base_mod = Residue::new(base); + #[test] + fn test_powmod_small_exponent() { + let base = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); + let base_mod = residue!(base, Modulus); - // let exponent = U1024::from(105u64); + let exponent = U1024::from(105u64); - // let res = base_mod.pow(&exponent); + let res = base_mod.pow(&exponent); - // let expected = U1024::from_be_hex("6B717DC3571BEC59C1E370780630280B05F13D9BB69E192DA75EAE2A817B270840881034B0AB9EE6B47382C58424AE00F90B88DFA7DFF7C9417B28E4C9DF170BCDFC4689140E9BA067FDB224831A33E2E18232655D15EA985EC0C8FB774BFA967B734A60DD8FC1F9214B7C262050F269C248F3255D5D1E7BD63626707232FF72"); - // assert_eq!(res.retrieve(), expected); - // } + let expected = U1024::from_be_hex("6B717DC3571BEC59C1E370780630280B05F13D9BB69E192DA75EAE2A817B270840881034B0AB9EE6B47382C58424AE00F90B88DFA7DFF7C9417B28E4C9DF170BCDFC4689140E9BA067FDB224831A33E2E18232655D15EA985EC0C8FB774BFA967B734A60DD8FC1F9214B7C262050F269C248F3255D5D1E7BD63626707232FF72"); + assert_eq!(res.retrieve(), expected); + } } diff --git a/src/uint/shl.rs b/src/uint/shl.rs index 9c43e03a..1dbe0e79 100644 --- a/src/uint/shl.rs +++ b/src/uint/shl.rs @@ -125,7 +125,7 @@ impl ShlAssign for UInt { #[cfg(test)] mod tests { - use crate::{U256, UInt, Limb, U128}; + use crate::{Limb, UInt, U128, U256}; const N: U256 = U256::from_be_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"); @@ -185,16 +185,25 @@ mod tests { #[test] fn shl_wide_1_1_128() { - assert_eq!(UInt::shl_vartime_wide((U128::ONE, U128::ONE), 128), (U128::ZERO, U128::ONE)); + assert_eq!( + UInt::shl_vartime_wide((U128::ONE, U128::ONE), 128), + (U128::ZERO, U128::ONE) + ); } #[test] fn shl_wide_max_0_1() { - assert_eq!(UInt::shl_vartime_wide((U128::MAX, U128::ZERO), 1), (U128::MAX.sbb(&U128::ONE, Limb::ZERO).0, U128::ONE)); + assert_eq!( + UInt::shl_vartime_wide((U128::MAX, U128::ZERO), 1), + (U128::MAX.sbb(&U128::ONE, Limb::ZERO).0, U128::ONE) + ); } #[test] fn shl_wide_max_max_256() { - assert_eq!(UInt::shl_vartime_wide((U128::MAX, U128::MAX), 256), (U128::ZERO, U128::ZERO)); + assert_eq!( + UInt::shl_vartime_wide((U128::MAX, U128::MAX), 256), + (U128::ZERO, U128::ZERO) + ); } } diff --git a/src/uint/shr.rs b/src/uint/shr.rs index ca7f30f0..f24dea46 100644 --- a/src/uint/shr.rs +++ b/src/uint/shr.rs @@ -129,7 +129,7 @@ impl ShrAssign for UInt { #[cfg(test)] mod tests { - use crate::{U256, UInt, U128}; + use crate::{UInt, U128, U256}; const N: U256 = U256::from_be_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"); @@ -144,16 +144,25 @@ mod tests { #[test] fn shr_wide_1_1_128() { - assert_eq!(UInt::shr_vartime_wide((U128::ONE, U128::ONE), 128), (U128::ONE, U128::ZERO)); + assert_eq!( + UInt::shr_vartime_wide((U128::ONE, U128::ONE), 128), + (U128::ONE, U128::ZERO) + ); } #[test] fn shr_wide_0_max_1() { - assert_eq!(UInt::shr_vartime_wide((U128::ZERO, U128::MAX), 1), (U128::ONE << 127, U128::MAX >> 1)); + assert_eq!( + UInt::shr_vartime_wide((U128::ZERO, U128::MAX), 1), + (U128::ONE << 127, U128::MAX >> 1) + ); } #[test] fn shr_wide_max_max_256() { - assert_eq!(UInt::shr_vartime_wide((U128::MAX, U128::MAX), 256), (U128::ZERO, U128::ZERO)); + assert_eq!( + UInt::shr_vartime_wide((U128::MAX, U128::MAX), 256), + (U128::ZERO, U128::ZERO) + ); } } From daf3299f7580643eb5ee70bfe1212ebe58dad84d Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Fri, 14 Oct 2022 17:25:15 +0200 Subject: [PATCH 09/23] Make pow a const fn --- src/lib.rs | 4 ++-- src/uint/modular/mul.rs | 17 ++++++++++++++--- src/uint/modular/pow.rs | 21 ++++++++++----------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e0a81fe7..1d92498c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,8 +17,8 @@ unused_qualifications )] // FIXME: This does not work in stable rust -#![feature(const_eval_limit)] -#![const_eval_limit = "0"] +// #![feature(const_eval_limit)] +// #![const_eval_limit = "0"] //! ## Usage //! diff --git a/src/uint/modular/mul.rs b/src/uint/modular/mul.rs index ad7c196e..f4a4d846 100644 --- a/src/uint/modular/mul.rs +++ b/src/uint/modular/mul.rs @@ -1,9 +1,21 @@ -use core::ops::MulAssign; +use core::{marker::PhantomData, ops::MulAssign}; use crate::{Concat, Split, UInt}; use super::{montgomery_reduction, Residue, ResidueParams}; +impl, const LIMBS: usize> Residue { + pub const fn mul(&self, rhs: &Self) -> Self { + let product = self.montgomery_form.mul_wide(&rhs.montgomery_form); + let montgomery_form = montgomery_reduction::(product); + + Self { + montgomery_form, + phantom: PhantomData, + } + } +} + impl, const LIMBS: usize, const DLIMBS: usize> Residue where UInt: Concat>, @@ -17,7 +29,6 @@ where impl, const LIMBS: usize> MulAssign for Residue { fn mul_assign(&mut self, rhs: Self) { - let product = self.montgomery_form.mul_wide(&rhs.montgomery_form); - self.montgomery_form = montgomery_reduction::(product); + *self = self.mul(&rhs) } } diff --git a/src/uint/modular/pow.rs b/src/uint/modular/pow.rs index e8e5aef4..20447fc8 100644 --- a/src/uint/modular/pow.rs +++ b/src/uint/modular/pow.rs @@ -1,18 +1,14 @@ -use crate::{Concat, Split, UInt, Word}; +use crate::{UInt, Word}; use super::{Residue, ResidueParams}; -impl, const LIMBS: usize, const DLIMBS: usize> Residue -where - UInt: Concat>, - UInt: Split>, -{ - pub fn pow(&self, exponent: &UInt) -> Residue { +impl, const LIMBS: usize> Residue { + pub const fn pow(&self, exponent: &UInt) -> Residue { self.pow_specific(exponent, LIMBS * Word::BITS as usize) } /// Perform modular exponentiation using Montgomery's ladder. `exponent_bits` represents the number of bits to take into account for the exponent. Note that this value is leaked in the time pattern. - pub fn pow_specific( + pub const fn pow_specific( &self, exponent: &UInt, exponent_bits: usize, @@ -24,20 +20,23 @@ where let mut n: UInt = exponent.shl_vartime((LIMBS * Word::BITS as usize) - exponent_bits); - for _ in 0..exponent_bits { + let mut i = 0; + while i < exponent_bits { // TODO: Remove one of the squares and instead conditionally select x1 or x2 to square // Peel off one bit at a time from the left side let (next_n, overflow) = n.shl_1(); n = next_n; let mut product: Residue = x1; - product *= x2; + product = product.mul(&x2); let mut square = Residue::ct_select(x1, x2, overflow); - square.square(); + square = square.mul(&square); x1 = Residue::ct_select(square, product, overflow); x2 = Residue::ct_select(product, square, overflow); + + i += 1; } x1 From 515d78dad66447b0901f6131d71ea31b7125a70f Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Mon, 17 Oct 2022 15:56:05 +0200 Subject: [PATCH 10/23] Add missing documentation --- src/lib.rs | 13 +++++-- src/uint.rs | 4 ++- src/uint/div.rs | 1 + src/uint/modular/add.rs | 1 + src/uint/modular/macros.rs | 7 ++-- src/uint/modular/mod.rs | 18 ++++++++-- src/uint/modular/mul.rs | 2 ++ src/uint/modular/pow.rs | 70 +++++++++++++++++++++++++++++++++----- 8 files changed, 97 insertions(+), 19 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1d92498c..6046fc68 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,9 +16,6 @@ trivial_numeric_casts, unused_qualifications )] -// FIXME: This does not work in stable rust -// #![feature(const_eval_limit)] -// #![const_eval_limit = "0"] //! ## Usage //! @@ -50,6 +47,15 @@ //! pub const MODULUS_SHR1: U256 = MODULUS.shr_vartime(1); //! ``` //! +//! Note that large constant computations may accidentally trigger a the `const_eval_limit` of the compiler. +//! The current way to deal with this problem is to either simplify this computation, +//! or increase the compiler's limit (currently a nightly feature). +//! One can completely remove the compiler's limit using: +//! ``` +//! #![feature(const_eval_limit)] +//! #![const_eval_limit = "0"] +//! ``` +//! //! ### Trait-based usage //! //! The [`UInt`] type itself does not implement the standard arithmetic traits @@ -87,6 +93,7 @@ //! This library has initial support for modular arithmetic in the form of the //! [`AddMod`], [`SubMod`], [`NegMod`], and [`MulMod`] traits, as well as the //! support for the [`Rem`] trait when used with a [`NonZero`] operand. +//! It also supports modular arithmetic over constant moduli using `Residue`. //! //! ``` //! use crypto_bigint::{AddMod, U256}; diff --git a/src/uint.rs b/src/uint.rs index 9e92a7db..e2ccae30 100644 --- a/src/uint.rs +++ b/src/uint.rs @@ -23,7 +23,6 @@ mod div; mod encoding; mod from; mod inv_mod; -mod modular; mod mul; mod mul_mod; mod neg; @@ -36,6 +35,9 @@ mod sqrt; mod sub; mod sub_mod; +/// Implements modular arithmetic for constant moduli. +pub mod modular; + #[cfg(feature = "generic-array")] mod array; diff --git a/src/uint/div.rs b/src/uint/div.rs index fa8020f2..828b9f50 100644 --- a/src/uint/div.rs +++ b/src/uint/div.rs @@ -75,6 +75,7 @@ impl UInt { /// /// When used with a fixed `rhs`, this function is constant-time with respect /// to `self`. + #[allow(dead_code)] pub(crate) const fn ct_reduce_wide(lower_upper: (Self, Self), rhs: &Self) -> (Self, u8) { let mb = rhs.bits_vartime(); diff --git a/src/uint/modular/add.rs b/src/uint/modular/add.rs index 0ed6fe4f..9d1cea0d 100644 --- a/src/uint/modular/add.rs +++ b/src/uint/modular/add.rs @@ -5,6 +5,7 @@ use crate::UInt; use super::{Residue, ResidueParams}; impl, const LIMBS: usize> Residue { + /// Computes the (reduced) sum of two residues. pub fn add(&self, rhs: &Self) -> Self { Residue { montgomery_form: self diff --git a/src/uint/modular/macros.rs b/src/uint/modular/macros.rs index 28711089..53b46214 100644 --- a/src/uint/modular/macros.rs +++ b/src/uint/modular/macros.rs @@ -1,10 +1,9 @@ // TODO: Use `adt_const_params` once stabilized to make a `Residue` generic around a modulus rather than having to implement a ZST + trait #[macro_export] +/// Implements a modulus with the given name, type, and value, in that specific order. Please `use crypto_bigint::traits::Encoding` to make this work. +/// For example, `impl_modulus!(MyModulus, U256, "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001");` implements a 256-bit modulus named `MyModulus`. macro_rules! impl_modulus { ($name:ident, $uint_type:ty, $value:expr) => { - // use $crate::traits::{Encoding, Concat, Split}; - // use $crate::{Limb, Word}; - #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct $name {} impl ResidueParams<{ nlimbs!(<$uint_type>::BIT_SIZE) }> for $name @@ -31,6 +30,8 @@ macro_rules! impl_modulus { } #[macro_export] +/// Creates a `Residue` with the given value for a specific modulus. +/// For example, `residue!(U256::from(105u64), MyModulus);` creates a `Residue` for 105 mod `MyModulus`. macro_rules! residue { ($variable:ident, $modulus:ident) => { $crate::uint::modular::Residue::<$modulus, { $modulus::LIMBS }>::new($variable) diff --git a/src/uint/modular/mod.rs b/src/uint/modular/mod.rs index e276b0c9..d733d8fe 100644 --- a/src/uint/modular/mod.rs +++ b/src/uint/modular/mod.rs @@ -5,26 +5,35 @@ use subtle::{Choice, ConditionallySelectable}; use crate::{Limb, UInt, WideWord, Word}; #[macro_use] -mod macros; - +/// Macros to remove the boilerplate code when dealing with constant moduli. +pub mod macros; +/// Additions between residues mod add; +/// Multiplications between residues mod mul; +/// Exponentiation of residues mod pow; /// The parameters to efficiently go to and from the Montgomery form for a given modulus. An easy way to generate these parameters is using the `impl_modulus!` macro. /// /// Unfortunately, `LIMBS` must be generic for now until const generics are stabilized. pub trait ResidueParams: Copy { + /// Number of limbs required to encode a residue const LIMBS: usize; + + /// The constant modulus const MODULUS: UInt; + /// Parameter used in Montgomery reduction const R: UInt; + /// R^2, used to move into Montgomery form const R2: UInt; + /// The lowest limbs of -(MODULUS^-1) mod R // We only need the LSB because during reduction this value is multiplied modulo 2**64. const MOD_NEG_INV: Limb; } -// TODO: We should consider taking modulus_params as a reference #[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// A residue mod `MOD`, represented using `LIMBS` limbs. pub struct Residue where MOD: ResidueParams, @@ -34,11 +43,13 @@ where } impl, const LIMBS: usize> Residue { + /// The representation of 1 mod `MOD`. pub const ONE: Self = Self { montgomery_form: MOD::R, phantom: PhantomData, }; + /// Instantiates a new `Residue` that represents this `integer` mod `MOD`. pub const fn new(integer: UInt) -> Self { let mut modular_integer = Residue { montgomery_form: integer, @@ -51,6 +62,7 @@ impl, const LIMBS: usize> Residue { modular_integer } + /// Retrieves the `integer` currently encoded in this `Residue`, guaranteed to be reduced. pub const fn retrieve(&self) -> UInt { montgomery_reduction::((self.montgomery_form, UInt::ZERO)) } diff --git a/src/uint/modular/mul.rs b/src/uint/modular/mul.rs index f4a4d846..1fedcb3d 100644 --- a/src/uint/modular/mul.rs +++ b/src/uint/modular/mul.rs @@ -5,6 +5,7 @@ use crate::{Concat, Split, UInt}; use super::{montgomery_reduction, Residue, ResidueParams}; impl, const LIMBS: usize> Residue { + /// Computes the (reduced) product between two residues. pub const fn mul(&self, rhs: &Self) -> Self { let product = self.montgomery_form.mul_wide(&rhs.montgomery_form); let montgomery_form = montgomery_reduction::(product); @@ -21,6 +22,7 @@ where UInt: Concat>, UInt: Split>, { + /// Computes the (reduced) square of a residue. pub fn square(&mut self) { let (hi, lo) = self.montgomery_form.square().split(); self.montgomery_form = montgomery_reduction::((lo, hi)); diff --git a/src/uint/modular/pow.rs b/src/uint/modular/pow.rs index 20447fc8..d8714413 100644 --- a/src/uint/modular/pow.rs +++ b/src/uint/modular/pow.rs @@ -3,11 +3,12 @@ use crate::{UInt, Word}; use super::{Residue, ResidueParams}; impl, const LIMBS: usize> Residue { + /// Performs modular exponentiation using Montgomery's ladder. pub const fn pow(&self, exponent: &UInt) -> Residue { self.pow_specific(exponent, LIMBS * Word::BITS as usize) } - /// Perform modular exponentiation using Montgomery's ladder. `exponent_bits` represents the number of bits to take into account for the exponent. Note that this value is leaked in the time pattern. + /// Performs modular exponentiation using Montgomery's ladder. `exponent_bits` represents the number of bits to take into account for the exponent. Note that this value is leaked in the time pattern. pub const fn pow_specific( &self, exponent: &UInt, @@ -45,33 +46,84 @@ impl, const LIMBS: usize> Residue { #[cfg(test)] mod tests { - use crate::{traits::Encoding, uint::modular::ResidueParams, U1024}; + use crate::{traits::Encoding, uint::modular::ResidueParams, U256}; - impl_modulus!(Modulus, U1024, "D509E7854ABDC81921F669F1DC6F61359523F3949803E58ED4EA8BC16483DC6F37BFE27A9AC9EEA2969B357ABC5C0EE214BE16A7D4C58FC620D5B5A20AFF001AD198D3155E5799DC4EA76652D64983A7E130B5EACEBAC768D28D589C36EC749C558D0B64E37CD0775C0D0104AE7D98BA23C815185DD43CD8B16292FD94156767"); + impl_modulus!( + Modulus, + U256, + "9CC24C5DF431A864188AB905AC751B727C9447A8E99E6366E1AD78A21E8D882B" + ); #[test] fn test_powmod_small_base() { - let base = U1024::from(105u64); + let base = U256::from(105u64); let base_mod = residue!(base, Modulus); - let exponent = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); + let exponent = + U256::from_be_hex("77117F1273373C26C700D076B3F780074D03339F56DD0EFB60E7F58441FD3685"); let res = base_mod.pow(&exponent); - let expected = U1024::from_be_hex("84FADE244D8A183FD9D209B07312E017F53BBDF4377108EDB4FEAD2AEB1DFF83B6CE604A2DACF49E52574E69055C6E2D30980938CF259421E17AB277C67663712B185C565C97D3200659D83B287C1D8325BFD258C7DBA4BB2766A57C5F2A7EE3FA784A8669C54839F3D29E73215E7939B16E8293524D871D56F67759D553A242"); + let expected = + U256::from_be_hex("7B2CD7BDDD96C271E6F232F2F415BB03FE2A90BD6CCCEA5E94F1BFD064993766"); assert_eq!(res.retrieve(), expected); } #[test] fn test_powmod_small_exponent() { - let base = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); + let base = + U256::from_be_hex("3435D18AA8313EBBE4D20002922225B53F75DC4453BB3EEC0378646F79B524A4"); let base_mod = residue!(base, Modulus); - let exponent = U1024::from(105u64); + let exponent = U256::from(105u64); let res = base_mod.pow(&exponent); - let expected = U1024::from_be_hex("6B717DC3571BEC59C1E370780630280B05F13D9BB69E192DA75EAE2A817B270840881034B0AB9EE6B47382C58424AE00F90B88DFA7DFF7C9417B28E4C9DF170BCDFC4689140E9BA067FDB224831A33E2E18232655D15EA985EC0C8FB774BFA967B734A60DD8FC1F9214B7C262050F269C248F3255D5D1E7BD63626707232FF72"); + let expected = + U256::from_be_hex("89E2A4E99F649A5AE2C18068148C355CA927B34A3245C938178ED00D6EF218AA"); assert_eq!(res.retrieve(), expected); } + + #[test] + fn test_powmod() { + let base = + U256::from_be_hex("3435D18AA8313EBBE4D20002922225B53F75DC4453BB3EEC0378646F79B524A4"); + let base_mod = residue!(base, Modulus); + + let exponent = + U256::from_be_hex("77117F1273373C26C700D076B3F780074D03339F56DD0EFB60E7F58441FD3685"); + + let res = base_mod.pow(&exponent); + + let expected = + U256::from_be_hex("3681BC0FEA2E5D394EB178155A127B0FD2EF405486D354251C385BDD51B9D421"); + assert_eq!(res.retrieve(), expected); + } + + // TODO: These tests can be re-included if the `const_eval_limit` is set higher than it is currently. + // #[test] + // fn test_powmod_small_base() { + // let base = U1024::from(105u64); + // let base_mod = residue!(base, Modulus); + + // let exponent = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); + + // let res = base_mod.pow(&exponent); + + // let expected = U1024::from_be_hex("84FADE244D8A183FD9D209B07312E017F53BBDF4377108EDB4FEAD2AEB1DFF83B6CE604A2DACF49E52574E69055C6E2D30980938CF259421E17AB277C67663712B185C565C97D3200659D83B287C1D8325BFD258C7DBA4BB2766A57C5F2A7EE3FA784A8669C54839F3D29E73215E7939B16E8293524D871D56F67759D553A242"); + // assert_eq!(res.retrieve(), expected); + // } + + // #[test] + // fn test_powmod_small_exponent() { + // let base = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); + // let base_mod = residue!(base, Modulus); + + // let exponent = U1024::from(105u64); + + // let res = base_mod.pow(&exponent); + + // let expected = U1024::from_be_hex("6B717DC3571BEC59C1E370780630280B05F13D9BB69E192DA75EAE2A817B270840881034B0AB9EE6B47382C58424AE00F90B88DFA7DFF7C9417B28E4C9DF170BCDFC4689140E9BA067FDB224831A33E2E18232655D15EA985EC0C8FB774BFA967B734A60DD8FC1F9214B7C262050F269C248F3255D5D1E7BD63626707232FF72"); + // assert_eq!(res.retrieve(), expected); + // } } From a0ed6ff1312d6d89186449a80119704c025dd698 Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Mon, 17 Oct 2022 16:11:37 +0200 Subject: [PATCH 11/23] Bump rustc version to 1.61 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 909bcdba..e2e0775f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["arbitrary", "crypto", "bignum", "integer", "precision"] readme = "README.md" resolver = "2" edition = "2021" -rust-version = "1.57" +rust-version = "1.61" [dependencies] subtle = { version = "2.4", default-features = false } diff --git a/src/lib.rs b/src/lib.rs index 6046fc68..84e93789 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,7 +51,7 @@ //! The current way to deal with this problem is to either simplify this computation, //! or increase the compiler's limit (currently a nightly feature). //! One can completely remove the compiler's limit using: -//! ``` +//! ```ignore //! #![feature(const_eval_limit)] //! #![const_eval_limit = "0"] //! ``` From 0cd95b70fb578f90887be8dfa7fc6d9410ca4049 Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Mon, 17 Oct 2022 16:15:56 +0200 Subject: [PATCH 12/23] Bump rustc version to 1.61 in Github actions --- .github/workflows/crypto-bigint.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/crypto-bigint.yml b/.github/workflows/crypto-bigint.yml index 8e98a130..bb5556e0 100644 --- a/.github/workflows/crypto-bigint.yml +++ b/.github/workflows/crypto-bigint.yml @@ -19,7 +19,7 @@ jobs: strategy: matrix: rust: - - 1.57.0 # MSRV + - 1.61.0 # MSRV - stable target: - thumbv7em-none-eabi @@ -49,7 +49,7 @@ jobs: include: # 32-bit Linux - target: i686-unknown-linux-gnu - rust: 1.57.0 # MSRV + rust: 1.61.0 # MSRV deps: sudo apt update && sudo apt install gcc-multilib - target: i686-unknown-linux-gnu rust: stable @@ -57,7 +57,7 @@ jobs: # 64-bit Linux - target: x86_64-unknown-linux-gnu - rust: 1.57.0 # MSRV + rust: 1.61.0 # MSRV - target: x86_64-unknown-linux-gnu rust: stable steps: @@ -122,7 +122,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: 1.57.0 + toolchain: 1.61.0 components: clippy override: true profile: minimal From dc85a35ef6bc0200c2370e7226ffea95e7b120e0 Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Thu, 3 Nov 2022 11:50:59 +0100 Subject: [PATCH 13/23] Address comments --- src/uint.rs | 1 - src/uint/inv_mod.rs | 16 ++++++++-------- src/uint/modular/add.rs | 1 - src/uint/modular/mul.rs | 18 ++++++++---------- src/uint/modular/pow.rs | 3 +-- src/uint/mul.rs | 2 +- src/uint/pow_mod.rs | 42 ----------------------------------------- 7 files changed, 18 insertions(+), 65 deletions(-) delete mode 100644 src/uint/pow_mod.rs diff --git a/src/uint.rs b/src/uint.rs index e2ccae30..2e730986 100644 --- a/src/uint.rs +++ b/src/uint.rs @@ -27,7 +27,6 @@ mod mul; mod mul_mod; mod neg; mod neg_mod; -mod pow_mod; mod resize; mod shl; mod shr; diff --git a/src/uint/inv_mod.rs b/src/uint/inv_mod.rs index 712fbf2c..e2bca77d 100644 --- a/src/uint/inv_mod.rs +++ b/src/uint/inv_mod.rs @@ -29,7 +29,7 @@ impl UInt { } /// Computes the multiplicative inverse of `self` mod `modulus`. In other words `self^-1 mod modulus`. Returns `(inverse, 1...1)` if an inverse exists, otherwise `(undefined, 0...0)`. The algorithm is the same as in GMP 6.2.1's `mpn_sec_invert`. - pub const fn ct_inv_mod(self, modulus: &UInt) -> (Self, Word) { + pub(crate) const fn ct_inv_mod(self, modulus: UInt) -> (Self, Word) { debug_assert!(modulus.ct_is_odd() == Word::MAX); let mut a = self; @@ -37,12 +37,12 @@ impl UInt { let mut u = UInt::ONE; let mut v = UInt::ZERO; - let mut b = *modulus; + let mut b = modulus; // TODO: This can be lower if `self` is known to be small. let bit_size = 2 * LIMBS * 64; - let mut m1hp = *modulus; + let mut m1hp = modulus; let (m1hp_new, carry) = m1hp.shr_1(); debug_assert!(carry == Word::MAX); m1hp = m1hp_new.wrapping_add(&UInt::ONE); @@ -62,7 +62,7 @@ impl UInt { let (new_u, new_v) = UInt::ct_swap(u, v, swap); let (new_u, cy) = new_u.conditional_wrapping_sub(&new_v, self_odd); - let (new_u, cyy) = new_u.conditional_wrapping_add(modulus, cy); + let (new_u, cyy) = new_u.conditional_wrapping_add(&modulus, cy); debug_assert!(cy == cyy); let (new_a, overflow) = a.shr_1(); @@ -84,7 +84,7 @@ impl UInt { } /// Computes the multiplicative inverse of `self` mod `modulus`. In other words `self^-1 mod modulus`. Returns `None` if the inverse does not exist. The algorithm is the same as in GMP 6.2.1's `mpn_sec_invert`. - pub fn inv_mod(self, modulus: &UInt) -> CtOption { + pub fn inv_mod(self, modulus: UInt) -> CtOption { let (inverse, exists) = self.ct_inv_mod(modulus); CtOption::new(inverse, Choice::from((exists == Word::MAX) as u8)) } @@ -128,7 +128,7 @@ mod tests { let a = U1024::from_be_hex("000225E99153B467A5B451979A3F451DAEF3BF8D6C6521D2FA24BBB17F29544E347A412B065B75A351EA9719E2430D2477B11CC9CF9C1AD6EDEE26CB15F463F8BCC72EF87EA30288E95A48AA792226CEC959DCB0672D8F9D80A54CBBEA85CAD8382EC224DEB2F5784E62D0CC2F81C2E6AD14EBABE646D6764B30C32B87688985"); let m = U1024::from_be_hex("D509E7854ABDC81921F669F1DC6F61359523F3949803E58ED4EA8BC16483DC6F37BFE27A9AC9EEA2969B357ABC5C0EE214BE16A7D4C58FC620D5B5A20AFF001AD198D3155E5799DC4EA76652D64983A7E130B5EACEBAC768D28D589C36EC749C558D0B64E37CD0775C0D0104AE7D98BA23C815185DD43CD8B16292FD94156767"); - let res = a.inv_mod(&m); + let res = a.inv_mod(m); let expected = U1024::from_be_hex("B03623284B0EBABCABD5C5881893320281460C0A8E7BF4BFDCFFCBCCBF436A55D364235C8171E46C7D21AAD0680676E57274A8FDA6D12768EF961CACDD2DAE5788D93DA5EB8EDC391EE3726CDCF4613C539F7D23E8702200CB31B5ED5B06E5CA3E520968399B4017BF98A864FABA2B647EFC4998B56774D4F2CB026BC024A336"); assert_eq!(res.unwrap(), expected); @@ -139,7 +139,7 @@ mod tests { let a = U64::from(3u64); let m = U64::from(13u64); - let res = a.inv_mod(&m); + let res = a.inv_mod(m); assert_eq!(U64::from(9u64), res.unwrap()); } @@ -149,7 +149,7 @@ mod tests { let a = U64::from(14u64); let m = U64::from(49u64); - let res = a.inv_mod(&m); + let res = a.inv_mod(m); assert!(res.is_none().unwrap_u8() == 1); } diff --git a/src/uint/modular/add.rs b/src/uint/modular/add.rs index 9d1cea0d..56f9e2a1 100644 --- a/src/uint/modular/add.rs +++ b/src/uint/modular/add.rs @@ -26,7 +26,6 @@ impl, const LIMBS: usize> AddAssign<&UInt> impl, const LIMBS: usize> AddAssign<&Self> for Residue { fn add_assign(&mut self, rhs: &Self) { - // TODO: Can we easily verify that these have the same MontgomeryParams? (e.g. using a debug_assert) *self = self.add(rhs); } } diff --git a/src/uint/modular/mul.rs b/src/uint/modular/mul.rs index 1fedcb3d..e1845aaf 100644 --- a/src/uint/modular/mul.rs +++ b/src/uint/modular/mul.rs @@ -1,7 +1,5 @@ use core::{marker::PhantomData, ops::MulAssign}; -use crate::{Concat, Split, UInt}; - use super::{montgomery_reduction, Residue, ResidueParams}; impl, const LIMBS: usize> Residue { @@ -17,15 +15,15 @@ impl, const LIMBS: usize> Residue { } } -impl, const LIMBS: usize, const DLIMBS: usize> Residue -where - UInt: Concat>, - UInt: Split>, -{ +impl, const LIMBS: usize> Residue { /// Computes the (reduced) square of a residue. - pub fn square(&mut self) { - let (hi, lo) = self.montgomery_form.square().split(); - self.montgomery_form = montgomery_reduction::((lo, hi)); + pub const fn square(&self) -> Self { + let lo_hi = self.montgomery_form.square_wide(); + + Self { + montgomery_form: montgomery_reduction::(lo_hi), + phantom: PhantomData, + } } } diff --git a/src/uint/modular/pow.rs b/src/uint/modular/pow.rs index d8714413..9a3644bf 100644 --- a/src/uint/modular/pow.rs +++ b/src/uint/modular/pow.rs @@ -23,7 +23,6 @@ impl, const LIMBS: usize> Residue { let mut i = 0; while i < exponent_bits { - // TODO: Remove one of the squares and instead conditionally select x1 or x2 to square // Peel off one bit at a time from the left side let (next_n, overflow) = n.shl_1(); n = next_n; @@ -32,7 +31,7 @@ impl, const LIMBS: usize> Residue { product = product.mul(&x2); let mut square = Residue::ct_select(x1, x2, overflow); - square = square.mul(&square); + square = square.square(); x1 = Residue::ct_select(square, product, overflow); x2 = Residue::ct_select(product, square, overflow); diff --git a/src/uint/mul.rs b/src/uint/mul.rs index 916dca9a..3600aa55 100644 --- a/src/uint/mul.rs +++ b/src/uint/mul.rs @@ -80,7 +80,7 @@ impl UInt { where Self: Concat, { - let (lo, hi) = self.mul_wide(self); + let (lo, hi) = self.square_wide(); hi.concat(&lo) } diff --git a/src/uint/pow_mod.rs b/src/uint/pow_mod.rs deleted file mode 100644 index d7028980..00000000 --- a/src/uint/pow_mod.rs +++ /dev/null @@ -1,42 +0,0 @@ -// use crate::{Concat, Split, UInt}; - -// use super::modular::{Residue, ResidueParams}; - -// impl UInt -// where -// UInt: Concat>, -// UInt: Split>, -// { -// /// Computes `self^exponent mod modulus` using Montgomery's ladder. If you are also performing other modular operations, consider using `Modular` and the associated `pow` function. -// pub fn pow_mod(&self, exponent: &Self, modulus: &Self) -> Self { -// let modulus_params = ResidueParams::new(*modulus); -// let base_mod = Residue::new(*self, modulus_params); - -// base_mod.pow(exponent).retrieve() -// } - -// /// Computes `self^exponent mod modulus` using Montgomery's ladder, but only considering the first `exponent_bits` bits of the exponent. This number is revealed from the timing pattern. If you are also performing other modular operations, consider using `Modular` and the associated `pow` function. -// pub fn pow_mod_specific(&self, exponent: &Self, modulus: &Self, exponent_bits: usize) -> Self { -// let modulus_params = ResidueParams::new(*modulus); -// let base_mod = Residue::new(*self, modulus_params); - -// base_mod.pow_specific(exponent, exponent_bits).retrieve() -// } -// } - -// #[cfg(test)] -// mod tests { -// use crate::U64; - -// #[test] -// fn test_powmod_mini() { -// let b = U64::from(3u64); -// let e = U64::from(7u64); -// let m = U64::from(11u64); - -// let res = b.pow_mod(&e, &m); - -// let expected = U64::from(9u64); -// assert_eq!(res, expected); -// } -// } From e57e1d998a6bc0a9a29cc90c3eead6eb943d5ef7 Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Fri, 4 Nov 2022 09:26:20 +0100 Subject: [PATCH 14/23] Add const keyword to modular add --- src/uint/modular/add.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uint/modular/add.rs b/src/uint/modular/add.rs index 56f9e2a1..ce5383a7 100644 --- a/src/uint/modular/add.rs +++ b/src/uint/modular/add.rs @@ -6,7 +6,7 @@ use super::{Residue, ResidueParams}; impl, const LIMBS: usize> Residue { /// Computes the (reduced) sum of two residues. - pub fn add(&self, rhs: &Self) -> Self { + pub const fn add(&self, rhs: &Self) -> Self { Residue { montgomery_form: self .montgomery_form From 57cb0b7cb9ddc61feaefe29a972ea11ed0d332c2 Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Fri, 4 Nov 2022 15:57:49 +0100 Subject: [PATCH 15/23] Initial implementation of runtime moduli --- src/uint.rs | 2 +- src/uint/modular/add.rs | 61 +----- src/uint/modular/constant_mod/const_add.rs | 74 +++++++ src/uint/modular/constant_mod/const_mul.rs | 49 +++++ src/uint/modular/constant_mod/const_pow.rs | 127 ++++++++++++ src/uint/modular/{ => constant_mod}/macros.rs | 8 +- src/uint/modular/constant_mod/mod.rs | 99 +++++++++ src/uint/modular/mod.rs | 190 ++++-------------- src/uint/modular/mul.rs | 50 ++--- src/uint/modular/pow.rs | 160 ++++----------- src/uint/modular/reduction.rs | 57 ++++++ src/uint/modular/runtime_mod/mod.rs | 60 ++++++ src/uint/modular/runtime_mod/runtime_add.rs | 38 ++++ src/uint/modular/runtime_mod/runtime_mul.rs | 52 +++++ src/uint/modular/runtime_mod/runtime_pow.rs | 28 +++ 15 files changed, 706 insertions(+), 349 deletions(-) create mode 100644 src/uint/modular/constant_mod/const_add.rs create mode 100644 src/uint/modular/constant_mod/const_mul.rs create mode 100644 src/uint/modular/constant_mod/const_pow.rs rename src/uint/modular/{ => constant_mod}/macros.rs (88%) create mode 100644 src/uint/modular/constant_mod/mod.rs create mode 100644 src/uint/modular/reduction.rs create mode 100644 src/uint/modular/runtime_mod/mod.rs create mode 100644 src/uint/modular/runtime_mod/runtime_add.rs create mode 100644 src/uint/modular/runtime_mod/runtime_mul.rs create mode 100644 src/uint/modular/runtime_mod/runtime_pow.rs diff --git a/src/uint.rs b/src/uint.rs index 2e730986..b944440f 100644 --- a/src/uint.rs +++ b/src/uint.rs @@ -34,7 +34,7 @@ mod sqrt; mod sub; mod sub_mod; -/// Implements modular arithmetic for constant moduli. +/// Implements modular arithmetic for constant moduli and moduli set at runtime. pub mod modular; #[cfg(feature = "generic-array")] diff --git a/src/uint/modular/add.rs b/src/uint/modular/add.rs index 56f9e2a1..ef3e5a0e 100644 --- a/src/uint/modular/add.rs +++ b/src/uint/modular/add.rs @@ -1,59 +1,14 @@ -use core::ops::AddAssign; - use crate::UInt; -use super::{Residue, ResidueParams}; - -impl, const LIMBS: usize> Residue { +pub trait AddResidue { /// Computes the (reduced) sum of two residues. - pub fn add(&self, rhs: &Self) -> Self { - Residue { - montgomery_form: self - .montgomery_form - .add_mod(&rhs.montgomery_form, &MOD::MODULUS), - phantom: core::marker::PhantomData, - } - } -} - -impl, const LIMBS: usize> AddAssign<&UInt> - for Residue -{ - fn add_assign(&mut self, rhs: &UInt) { - *self += &Residue::new(*rhs); - } -} - -impl, const LIMBS: usize> AddAssign<&Self> for Residue { - fn add_assign(&mut self, rhs: &Self) { - *self = self.add(rhs); - } + fn add(&self, rhs: &Self) -> Self; } -#[cfg(test)] -mod tests { - use crate::{traits::Encoding, uint::modular::ResidueParams, U256}; - - impl_modulus!( - Modulus, - U256, - "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551" - ); - - #[test] - fn add_overflow() { - let x = - U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); - let mut x_mod = residue!(x, Modulus); - - let y = - U256::from_be_hex("d5777c45019673125ad240f83094d4252d829516fac8601ed01979ec1ec1a251"); - - x_mod += &y; - - let expected = - U256::from_be_hex("1a2472fde50286541d97ca6a3592dd75beb9c9646e40c511b82496cfc3926956"); - - assert_eq!(expected, x_mod.retrieve()); - } +pub(crate) const fn add_montgomery_form( + a: &UInt, + b: &UInt, + modulus: &UInt, +) -> UInt { + a.add_mod(b, modulus) } diff --git a/src/uint/modular/constant_mod/const_add.rs b/src/uint/modular/constant_mod/const_add.rs new file mode 100644 index 00000000..1a6f22b5 --- /dev/null +++ b/src/uint/modular/constant_mod/const_add.rs @@ -0,0 +1,74 @@ +use core::ops::AddAssign; + +use crate::{ + modular::add::{add_montgomery_form, AddResidue}, + UInt, +}; + +use super::{ConstResidue, ConstResidueParams}; + +impl, const LIMBS: usize> AddResidue for ConstResidue { + fn add(&self, rhs: &Self) -> Self { + self.add(rhs) + } +} + +impl, const LIMBS: usize> ConstResidue { + pub const fn add(&self, rhs: &Self) -> Self { + ConstResidue { + montgomery_form: add_montgomery_form( + &self.montgomery_form, + &rhs.montgomery_form, + &MOD::MODULUS, + ), + phantom: core::marker::PhantomData, + } + } +} + +impl, const LIMBS: usize> AddAssign<&UInt> + for ConstResidue +{ + fn add_assign(&mut self, rhs: &UInt) { + *self += &ConstResidue::new(*rhs); + } +} + +impl, const LIMBS: usize> AddAssign<&Self> + for ConstResidue +{ + fn add_assign(&mut self, rhs: &Self) { + *self = self.add(rhs); + } +} + +#[cfg(test)] +mod tests { + use crate::{ + const_residue, impl_modulus, modular::constant_mod::ConstResidueParams, traits::Encoding, + U256, + }; + + impl_modulus!( + Modulus, + U256, + "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551" + ); + + #[test] + fn add_overflow() { + let x = + U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); + let mut x_mod = const_residue!(x, Modulus); + + let y = + U256::from_be_hex("d5777c45019673125ad240f83094d4252d829516fac8601ed01979ec1ec1a251"); + + x_mod += &y; + + let expected = + U256::from_be_hex("1a2472fde50286541d97ca6a3592dd75beb9c9646e40c511b82496cfc3926956"); + + assert_eq!(expected, x_mod.retrieve()); + } +} diff --git a/src/uint/modular/constant_mod/const_mul.rs b/src/uint/modular/constant_mod/const_mul.rs new file mode 100644 index 00000000..636c4f1b --- /dev/null +++ b/src/uint/modular/constant_mod/const_mul.rs @@ -0,0 +1,49 @@ +use core::{marker::PhantomData, ops::MulAssign}; + +use crate::modular::{ + mul::{mul_montgomery_form, MulResidue}, + reduction::montgomery_reduction, +}; + +use super::{ConstResidue, ConstResidueParams}; + +impl, const LIMBS: usize> MulResidue for ConstResidue { + fn mul(&self, rhs: &Self) -> Self { + self.mul(rhs) + } + + fn square(&self) -> Self { + self.square() + } +} + +impl, const LIMBS: usize> ConstResidue { + /// Computes the (reduced) product between two residues. + pub const fn mul(&self, rhs: &Self) -> Self { + Self { + montgomery_form: mul_montgomery_form( + &self.montgomery_form, + &rhs.montgomery_form, + MOD::MODULUS, + MOD::MOD_NEG_INV, + ), + phantom: PhantomData, + } + } + + /// Computes the (reduced) square of a residue. + pub const fn square(&self) -> Self { + let lo_hi = self.montgomery_form.square_wide(); + + Self { + montgomery_form: montgomery_reduction::(lo_hi, MOD::MODULUS, MOD::MOD_NEG_INV), + phantom: PhantomData, + } + } +} + +impl, const LIMBS: usize> MulAssign for ConstResidue { + fn mul_assign(&mut self, rhs: Self) { + *self = self.mul(&rhs) + } +} diff --git a/src/uint/modular/constant_mod/const_pow.rs b/src/uint/modular/constant_mod/const_pow.rs new file mode 100644 index 00000000..51a8f6dd --- /dev/null +++ b/src/uint/modular/constant_mod/const_pow.rs @@ -0,0 +1,127 @@ +use crate::{ + modular::pow::{pow_montgomery_form, PowResidue}, + UInt, Word, +}; + +use super::{ConstResidue, ConstResidueParams}; + +impl, const LIMBS: usize> PowResidue + for ConstResidue +{ + fn pow_specific(self, exponent: &UInt, exponent_bits: usize) -> Self { + self.pow_specific(exponent, exponent_bits) + } +} + +impl, const LIMBS: usize> ConstResidue { + /// Performs modular exponentiation using Montgomery's ladder. + pub const fn pow(self, exponent: &UInt) -> ConstResidue { + self.pow_specific(exponent, LIMBS * Word::BITS as usize) + } + + /// Performs modular exponentiation using Montgomery's ladder. `exponent_bits` represents the number of bits to take into account for the exponent. Note that this value is leaked in the time pattern. + pub const fn pow_specific( + self, + exponent: &UInt, + exponent_bits: usize, + ) -> ConstResidue { + Self { + montgomery_form: pow_montgomery_form( + self.montgomery_form, + exponent, + exponent_bits, + MOD::MODULUS, + MOD::R, + MOD::MOD_NEG_INV, + ), + phantom: core::marker::PhantomData, + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + const_residue, impl_modulus, modular::constant_mod::ConstResidueParams, traits::Encoding, + U256, + }; + + impl_modulus!( + Modulus, + U256, + "9CC24C5DF431A864188AB905AC751B727C9447A8E99E6366E1AD78A21E8D882B" + ); + + #[test] + fn test_powmod_small_base() { + let base = U256::from(105u64); + let base_mod = const_residue!(base, Modulus); + + let exponent = + U256::from_be_hex("77117F1273373C26C700D076B3F780074D03339F56DD0EFB60E7F58441FD3685"); + + let res = base_mod.pow(&exponent); + + let expected = + U256::from_be_hex("7B2CD7BDDD96C271E6F232F2F415BB03FE2A90BD6CCCEA5E94F1BFD064993766"); + assert_eq!(res.retrieve(), expected); + } + + #[test] + fn test_powmod_small_exponent() { + let base = + U256::from_be_hex("3435D18AA8313EBBE4D20002922225B53F75DC4453BB3EEC0378646F79B524A4"); + let base_mod = const_residue!(base, Modulus); + + let exponent = U256::from(105u64); + + let res = base_mod.pow(&exponent); + + let expected = + U256::from_be_hex("89E2A4E99F649A5AE2C18068148C355CA927B34A3245C938178ED00D6EF218AA"); + assert_eq!(res.retrieve(), expected); + } + + #[test] + fn test_powmod() { + let base = + U256::from_be_hex("3435D18AA8313EBBE4D20002922225B53F75DC4453BB3EEC0378646F79B524A4"); + let base_mod = const_residue!(base, Modulus); + + let exponent = + U256::from_be_hex("77117F1273373C26C700D076B3F780074D03339F56DD0EFB60E7F58441FD3685"); + + let res = base_mod.pow(&exponent); + + let expected = + U256::from_be_hex("3681BC0FEA2E5D394EB178155A127B0FD2EF405486D354251C385BDD51B9D421"); + assert_eq!(res.retrieve(), expected); + } + + // TODO: These tests can be re-included if the `const_eval_limit` is set higher than it is currently. + // #[test] + // fn test_powmod_small_base() { + // let base = U1024::from(105u64); + // let base_mod = residue!(base, Modulus); + + // let exponent = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); + + // let res = base_mod.pow(&exponent); + + // let expected = U1024::from_be_hex("84FADE244D8A183FD9D209B07312E017F53BBDF4377108EDB4FEAD2AEB1DFF83B6CE604A2DACF49E52574E69055C6E2D30980938CF259421E17AB277C67663712B185C565C97D3200659D83B287C1D8325BFD258C7DBA4BB2766A57C5F2A7EE3FA784A8669C54839F3D29E73215E7939B16E8293524D871D56F67759D553A242"); + // assert_eq!(res.retrieve(), expected); + // } + + // #[test] + // fn test_powmod_small_exponent() { + // let base = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); + // let base_mod = residue!(base, Modulus); + + // let exponent = U1024::from(105u64); + + // let res = base_mod.pow(&exponent); + + // let expected = U1024::from_be_hex("6B717DC3571BEC59C1E370780630280B05F13D9BB69E192DA75EAE2A817B270840881034B0AB9EE6B47382C58424AE00F90B88DFA7DFF7C9417B28E4C9DF170BCDFC4689140E9BA067FDB224831A33E2E18232655D15EA985EC0C8FB774BFA967B734A60DD8FC1F9214B7C262050F269C248F3255D5D1E7BD63626707232FF72"); + // assert_eq!(res.retrieve(), expected); + // } +} diff --git a/src/uint/modular/macros.rs b/src/uint/modular/constant_mod/macros.rs similarity index 88% rename from src/uint/modular/macros.rs rename to src/uint/modular/constant_mod/macros.rs index 53b46214..0e55a502 100644 --- a/src/uint/modular/macros.rs +++ b/src/uint/modular/constant_mod/macros.rs @@ -6,7 +6,7 @@ macro_rules! impl_modulus { ($name:ident, $uint_type:ty, $value:expr) => { #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct $name {} - impl ResidueParams<{ nlimbs!(<$uint_type>::BIT_SIZE) }> for $name + impl ConstResidueParams<{ nlimbs!(<$uint_type>::BIT_SIZE) }> for $name where $crate::UInt<{ nlimbs!(<$uint_type>::BIT_SIZE) }>: $crate::traits::Concat>, @@ -32,8 +32,10 @@ macro_rules! impl_modulus { #[macro_export] /// Creates a `Residue` with the given value for a specific modulus. /// For example, `residue!(U256::from(105u64), MyModulus);` creates a `Residue` for 105 mod `MyModulus`. -macro_rules! residue { +macro_rules! const_residue { ($variable:ident, $modulus:ident) => { - $crate::uint::modular::Residue::<$modulus, { $modulus::LIMBS }>::new($variable) + $crate::uint::modular::constant_mod::ConstResidue::<$modulus, { $modulus::LIMBS }>::new( + $variable, + ) }; } diff --git a/src/uint/modular/constant_mod/mod.rs b/src/uint/modular/constant_mod/mod.rs new file mode 100644 index 00000000..dd21a3db --- /dev/null +++ b/src/uint/modular/constant_mod/mod.rs @@ -0,0 +1,99 @@ +use core::marker::PhantomData; + +use subtle::{Choice, ConditionallySelectable}; + +use crate::{Limb, UInt}; + +use super::{reduction::montgomery_reduction, GenericResidue}; + +/// Additions between residues with a constant modulus +mod const_add; +/// Multiplications between residues with a constant modulus +mod const_mul; +/// Exponentiation of residues with a constant modulus +mod const_pow; + +#[macro_use] +/// Macros to remove the boilerplate code when dealing with constant moduli. +pub mod macros; + +/// The parameters to efficiently go to and from the Montgomery form for a given modulus. An easy way to generate these parameters is using the `impl_modulus!` macro. These parameters are constant, so they cannot be set at runtime. +/// +/// Unfortunately, `LIMBS` must be generic for now until const generics are stabilized. +pub trait ConstResidueParams: Copy { + /// Number of limbs required to encode a residue + const LIMBS: usize; + + /// The constant modulus + const MODULUS: UInt; + /// Parameter used in Montgomery reduction + const R: UInt; + /// R^2, used to move into Montgomery form + const R2: UInt; + /// The lowest limbs of -(MODULUS^-1) mod R + // We only need the LSB because during reduction this value is multiplied modulo 2**64. + const MOD_NEG_INV: Limb; +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// A residue mod `MOD`, represented using `LIMBS` limbs. The modulus of this residue is constant, so it cannot be set at runtime. +pub struct ConstResidue +where + MOD: ConstResidueParams, +{ + montgomery_form: UInt, + phantom: PhantomData, +} + +impl, const LIMBS: usize> ConstResidue { + /// The representation of 1 mod `MOD`. + pub const ONE: Self = Self { + montgomery_form: MOD::R, + phantom: PhantomData, + }; + + /// Instantiates a new `Residue` that represents this `integer` mod `MOD`. + pub const fn new(integer: UInt) -> Self { + let mut modular_integer = ConstResidue { + montgomery_form: integer, + phantom: PhantomData, + }; + + let product = integer.mul_wide(&MOD::R2); + modular_integer.montgomery_form = + montgomery_reduction::(product, MOD::MODULUS, MOD::MOD_NEG_INV); + + modular_integer + } + + pub const fn retrieve(&self) -> UInt { + montgomery_reduction::( + (self.montgomery_form, UInt::ZERO), + MOD::MODULUS, + MOD::MOD_NEG_INV, + ) + } +} + +impl, const LIMBS: usize> GenericResidue + for ConstResidue +{ + fn retrieve(&self) -> UInt { + self.retrieve() + } +} + +impl + Copy, const LIMBS: usize> ConditionallySelectable + for ConstResidue +{ + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + ConstResidue { + montgomery_form: UInt::conditional_select( + &a.montgomery_form, + &b.montgomery_form, + choice, + ), + phantom: PhantomData, + } + } +} diff --git a/src/uint/modular/mod.rs b/src/uint/modular/mod.rs index d733d8fe..ba3d79f4 100644 --- a/src/uint/modular/mod.rs +++ b/src/uint/modular/mod.rs @@ -1,158 +1,30 @@ -use core::marker::PhantomData; +use crate::UInt; -use subtle::{Choice, ConditionallySelectable}; +use self::{add::AddResidue, mul::MulResidue, pow::PowResidue}; -use crate::{Limb, UInt, WideWord, Word}; +mod reduction; + +pub mod constant_mod; +pub mod runtime_mod; -#[macro_use] -/// Macros to remove the boilerplate code when dealing with constant moduli. -pub mod macros; -/// Additions between residues mod add; -/// Multiplications between residues mod mul; -/// Exponentiation of residues mod pow; -/// The parameters to efficiently go to and from the Montgomery form for a given modulus. An easy way to generate these parameters is using the `impl_modulus!` macro. -/// -/// Unfortunately, `LIMBS` must be generic for now until const generics are stabilized. -pub trait ResidueParams: Copy { - /// Number of limbs required to encode a residue - const LIMBS: usize; - - /// The constant modulus - const MODULUS: UInt; - /// Parameter used in Montgomery reduction - const R: UInt; - /// R^2, used to move into Montgomery form - const R2: UInt; - /// The lowest limbs of -(MODULUS^-1) mod R - // We only need the LSB because during reduction this value is multiplied modulo 2**64. - const MOD_NEG_INV: Limb; -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -/// A residue mod `MOD`, represented using `LIMBS` limbs. -pub struct Residue -where - MOD: ResidueParams, -{ - montgomery_form: UInt, - phantom: PhantomData, -} - -impl, const LIMBS: usize> Residue { - /// The representation of 1 mod `MOD`. - pub const ONE: Self = Self { - montgomery_form: MOD::R, - phantom: PhantomData, - }; - - /// Instantiates a new `Residue` that represents this `integer` mod `MOD`. - pub const fn new(integer: UInt) -> Self { - let mut modular_integer = Residue { - montgomery_form: integer, - phantom: PhantomData, - }; - - let product = integer.mul_wide(&MOD::R2); - modular_integer.montgomery_form = montgomery_reduction::(product); - - modular_integer - } - +pub trait GenericResidue: AddResidue + MulResidue + PowResidue { /// Retrieves the `integer` currently encoded in this `Residue`, guaranteed to be reduced. - pub const fn retrieve(&self) -> UInt { - montgomery_reduction::((self.montgomery_form, UInt::ZERO)) - } - - /// Return `a` if `c`==0 or `b` if `c`==`Word::MAX`. - /// - /// Const-friendly: we can't yet use `subtle` in `const fn` contexts. - #[inline] - pub(crate) const fn ct_select(a: Self, b: Self, c: Word) -> Self { - Residue { - montgomery_form: UInt::ct_select(a.montgomery_form, b.montgomery_form, c), - phantom: PhantomData, - } - } -} - -/// Algorithm 14.32 in Handbook of Applied Cryptography (https://cacr.uwaterloo.ca/hac/about/chap14.pdf) -const fn montgomery_reduction, const LIMBS: usize>( - lower_upper: (UInt, UInt), -) -> UInt { - let (mut lower, mut upper) = lower_upper; - - let mut meta_carry = 0; - - let mut i = 0; - while i < LIMBS { - let u = (lower.limbs[i].0.wrapping_mul(MOD::MOD_NEG_INV.0)) as WideWord; - - let new_limb = - (u * MOD::MODULUS.limbs[0].0 as WideWord).wrapping_add(lower.limbs[i].0 as WideWord); - let mut carry = new_limb >> Word::BITS; - - let mut j = 1; - while j < (LIMBS - i) { - let new_limb = (u * MOD::MODULUS.limbs[j].0 as WideWord) - .wrapping_add(lower.limbs[i + j].0 as WideWord) - .wrapping_add(carry); - carry = new_limb >> Word::BITS; - lower.limbs[i + j] = Limb(new_limb as Word); - - j += 1; - } - while j < LIMBS { - let new_limb = (u * MOD::MODULUS.limbs[j].0 as WideWord) - .wrapping_add(upper.limbs[i + j - LIMBS].0 as WideWord) - .wrapping_add(carry); - carry = new_limb >> Word::BITS; - upper.limbs[i + j - LIMBS] = Limb(new_limb as Word); - - j += 1; - } - - let new_sum = (upper.limbs[i].0 as WideWord) - .wrapping_add(carry) - .wrapping_add(meta_carry); - meta_carry = new_sum >> Word::BITS; - upper.limbs[i] = Limb(new_sum as Word); - - i += 1; - } - - // Division is simply taking the upper half of the limbs - // Final reduction (at this point, the value is at most 2 * modulus) - let must_reduce = (meta_carry as Word).saturating_mul(Word::MAX) - | ((upper.ct_cmp(&MOD::MODULUS) != -1) as Word).saturating_mul(Word::MAX); - upper = upper.wrapping_sub(&UInt::ct_select(UInt::ZERO, MOD::MODULUS, must_reduce)); - - upper -} - -impl + Copy, const LIMBS: usize> ConditionallySelectable - for Residue -{ - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Residue { - montgomery_form: UInt::conditional_select( - &a.montgomery_form, - &b.montgomery_form, - choice, - ), - phantom: PhantomData, - } - } + fn retrieve(&self) -> UInt; } #[cfg(test)] mod tests { use crate::{ + const_residue, impl_modulus, + modular::{ + constant_mod::ConstResidue, constant_mod::ConstResidueParams, + reduction::montgomery_reduction, + }, traits::Encoding, - uint::modular::{montgomery_reduction, Residue, ResidueParams}, UInt, U256, U64, }; @@ -188,7 +60,11 @@ mod tests { fn test_reducing_r() { // Divide the value R by R, which should equal 1 assert_eq!( - montgomery_reduction::((Modulus2::R, UInt::ZERO)), + montgomery_reduction::<{ Modulus2::LIMBS }>( + (Modulus2::R, UInt::ZERO), + Modulus2::MODULUS, + Modulus2::MOD_NEG_INV + ), UInt::ONE ); } @@ -197,7 +73,11 @@ mod tests { fn test_reducing_r2() { // Divide the value R^2 by R, which should equal R assert_eq!( - montgomery_reduction::((Modulus2::R2, UInt::ZERO)), + montgomery_reduction::<{ Modulus2::LIMBS }>( + (Modulus2::R2, UInt::ZERO), + Modulus2::MODULUS, + Modulus2::MOD_NEG_INV + ), Modulus2::R ); } @@ -207,7 +87,11 @@ mod tests { // Divide the value R^2 by R, which should equal R let (hi, lo) = Modulus2::R.square().split(); assert_eq!( - montgomery_reduction::((lo, hi)), + montgomery_reduction::<{ Modulus2::LIMBS }>( + (lo, hi), + Modulus2::MODULUS, + Modulus2::MOD_NEG_INV + ), Modulus2::R ); } @@ -219,7 +103,11 @@ mod tests { U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); let product = x.mul_wide(&Modulus2::R); assert_eq!( - montgomery_reduction::(product), + montgomery_reduction::<{ Modulus2::LIMBS }>( + product, + Modulus2::MODULUS, + Modulus2::MOD_NEG_INV + ), x ); } @@ -239,7 +127,11 @@ mod tests { assert_eq!(hi, UInt::ZERO); assert_eq!( - montgomery_reduction::(product), + montgomery_reduction::<{ Modulus2::LIMBS }>( + product, + Modulus2::MODULUS, + Modulus2::MOD_NEG_INV + ), lo ); } @@ -248,7 +140,7 @@ mod tests { fn test_new_retrieve() { let x = U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); - let x_mod = Residue::::new(x); + let x_mod = ConstResidue::::new(x); // Confirm that when creating a Modular and retrieving the value, that it equals the original assert_eq!(x, x_mod.retrieve()); @@ -259,8 +151,8 @@ mod tests { let x = U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); assert_eq!( - Residue::::new(x), - residue!(x, Modulus2) + ConstResidue::::new(x), + const_residue!(x, Modulus2) ); } } diff --git a/src/uint/modular/mul.rs b/src/uint/modular/mul.rs index e1845aaf..03008f0e 100644 --- a/src/uint/modular/mul.rs +++ b/src/uint/modular/mul.rs @@ -1,34 +1,34 @@ -use core::{marker::PhantomData, ops::MulAssign}; +use crate::{Limb, UInt}; -use super::{montgomery_reduction, Residue, ResidueParams}; +use super::reduction::montgomery_reduction; -impl, const LIMBS: usize> Residue { - /// Computes the (reduced) product between two residues. - pub const fn mul(&self, rhs: &Self) -> Self { - let product = self.montgomery_form.mul_wide(&rhs.montgomery_form); - let montgomery_form = montgomery_reduction::(product); +pub trait MulResidue +where + Self: Sized, +{ + /// Computes the (reduced) product of two residues. + fn mul(&self, rhs: &Self) -> Self; - Self { - montgomery_form, - phantom: PhantomData, - } + fn square(&self) -> Self { + self.mul(self) } } -impl, const LIMBS: usize> Residue { - /// Computes the (reduced) square of a residue. - pub const fn square(&self) -> Self { - let lo_hi = self.montgomery_form.square_wide(); - - Self { - montgomery_form: montgomery_reduction::(lo_hi), - phantom: PhantomData, - } - } +pub(crate) const fn mul_montgomery_form( + a: &UInt, + b: &UInt, + modulus: UInt, + mod_neg_inv: Limb, +) -> UInt { + let product = a.mul_wide(b); + montgomery_reduction::(product, modulus, mod_neg_inv) } -impl, const LIMBS: usize> MulAssign for Residue { - fn mul_assign(&mut self, rhs: Self) { - *self = self.mul(&rhs) - } +pub(crate) const fn square_montgomery_form( + a: &UInt, + modulus: UInt, + mod_neg_inv: Limb, +) -> UInt { + let product = a.square_wide(); + montgomery_reduction::(product, modulus, mod_neg_inv) } diff --git a/src/uint/modular/pow.rs b/src/uint/modular/pow.rs index 9a3644bf..cc3b67e2 100644 --- a/src/uint/modular/pow.rs +++ b/src/uint/modular/pow.rs @@ -1,128 +1,52 @@ -use crate::{UInt, Word}; +use crate::{Limb, UInt, Word}; -use super::{Residue, ResidueParams}; +use super::mul::{mul_montgomery_form, square_montgomery_form}; -impl, const LIMBS: usize> Residue { - /// Performs modular exponentiation using Montgomery's ladder. - pub const fn pow(&self, exponent: &UInt) -> Residue { +pub trait PowResidue +where + Self: Sized, +{ + /// Computes the (reduced) exponentiation of a residue. + fn pow(self, exponent: &UInt) -> Self { self.pow_specific(exponent, LIMBS * Word::BITS as usize) } - /// Performs modular exponentiation using Montgomery's ladder. `exponent_bits` represents the number of bits to take into account for the exponent. Note that this value is leaked in the time pattern. - pub const fn pow_specific( - &self, - exponent: &UInt, - exponent_bits: usize, - ) -> Residue { - let mut x1: Residue = Residue::ONE; - let mut x2: Residue = *self; - - // Shift the exponent all the way to the left so the leftmost bit is the MSB of the `UInt` - let mut n: UInt = - exponent.shl_vartime((LIMBS * Word::BITS as usize) - exponent_bits); - - let mut i = 0; - while i < exponent_bits { - // Peel off one bit at a time from the left side - let (next_n, overflow) = n.shl_1(); - n = next_n; - - let mut product: Residue = x1; - product = product.mul(&x2); - - let mut square = Residue::ct_select(x1, x2, overflow); - square = square.square(); - - x1 = Residue::ct_select(square, product, overflow); - x2 = Residue::ct_select(product, square, overflow); - - i += 1; - } - - x1 - } + /// Computes the (reduced) exponentiation of a residue, here `exponent_bits` represents the number of bits to take into account for the exponent. Note that this value is leaked in the time pattern. + fn pow_specific(self, exponent: &UInt, exponent_bits: usize) -> Self; } -#[cfg(test)] -mod tests { - use crate::{traits::Encoding, uint::modular::ResidueParams, U256}; - - impl_modulus!( - Modulus, - U256, - "9CC24C5DF431A864188AB905AC751B727C9447A8E99E6366E1AD78A21E8D882B" - ); - - #[test] - fn test_powmod_small_base() { - let base = U256::from(105u64); - let base_mod = residue!(base, Modulus); - - let exponent = - U256::from_be_hex("77117F1273373C26C700D076B3F780074D03339F56DD0EFB60E7F58441FD3685"); - - let res = base_mod.pow(&exponent); - - let expected = - U256::from_be_hex("7B2CD7BDDD96C271E6F232F2F415BB03FE2A90BD6CCCEA5E94F1BFD064993766"); - assert_eq!(res.retrieve(), expected); - } - - #[test] - fn test_powmod_small_exponent() { - let base = - U256::from_be_hex("3435D18AA8313EBBE4D20002922225B53F75DC4453BB3EEC0378646F79B524A4"); - let base_mod = residue!(base, Modulus); - - let exponent = U256::from(105u64); - - let res = base_mod.pow(&exponent); - - let expected = - U256::from_be_hex("89E2A4E99F649A5AE2C18068148C355CA927B34A3245C938178ED00D6EF218AA"); - assert_eq!(res.retrieve(), expected); - } - - #[test] - fn test_powmod() { - let base = - U256::from_be_hex("3435D18AA8313EBBE4D20002922225B53F75DC4453BB3EEC0378646F79B524A4"); - let base_mod = residue!(base, Modulus); - - let exponent = - U256::from_be_hex("77117F1273373C26C700D076B3F780074D03339F56DD0EFB60E7F58441FD3685"); - - let res = base_mod.pow(&exponent); - - let expected = - U256::from_be_hex("3681BC0FEA2E5D394EB178155A127B0FD2EF405486D354251C385BDD51B9D421"); - assert_eq!(res.retrieve(), expected); +/// Performs modular exponentiation using Montgomery's ladder. `exponent_bits` represents the number of bits to take into account for the exponent. Note that this value is leaked in the time pattern. +pub const fn pow_montgomery_form( + x: UInt, + exponent: &UInt, + exponent_bits: usize, + modulus: UInt, + r: UInt, + mod_neg_inv: Limb, +) -> UInt { + let mut x1: UInt = r; + let mut x2: UInt = x; + + // Shift the exponent all the way to the left so the leftmost bit is the MSB of the `UInt` + let mut n: UInt = exponent.shl_vartime((LIMBS * Word::BITS as usize) - exponent_bits); + + let mut i = 0; + while i < exponent_bits { + // Peel off one bit at a time from the left side + let (next_n, overflow) = n.shl_1(); + n = next_n; + + let mut product: UInt = x1; + product = mul_montgomery_form(&product, &x2, modulus, mod_neg_inv); + + let mut square = UInt::ct_select(x1, x2, overflow); + square = square_montgomery_form(&square, modulus, mod_neg_inv); + + x1 = UInt::::ct_select(square, product, overflow); + x2 = UInt::::ct_select(product, square, overflow); + + i += 1; } - // TODO: These tests can be re-included if the `const_eval_limit` is set higher than it is currently. - // #[test] - // fn test_powmod_small_base() { - // let base = U1024::from(105u64); - // let base_mod = residue!(base, Modulus); - - // let exponent = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); - - // let res = base_mod.pow(&exponent); - - // let expected = U1024::from_be_hex("84FADE244D8A183FD9D209B07312E017F53BBDF4377108EDB4FEAD2AEB1DFF83B6CE604A2DACF49E52574E69055C6E2D30980938CF259421E17AB277C67663712B185C565C97D3200659D83B287C1D8325BFD258C7DBA4BB2766A57C5F2A7EE3FA784A8669C54839F3D29E73215E7939B16E8293524D871D56F67759D553A242"); - // assert_eq!(res.retrieve(), expected); - // } - - // #[test] - // fn test_powmod_small_exponent() { - // let base = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); - // let base_mod = residue!(base, Modulus); - - // let exponent = U1024::from(105u64); - - // let res = base_mod.pow(&exponent); - - // let expected = U1024::from_be_hex("6B717DC3571BEC59C1E370780630280B05F13D9BB69E192DA75EAE2A817B270840881034B0AB9EE6B47382C58424AE00F90B88DFA7DFF7C9417B28E4C9DF170BCDFC4689140E9BA067FDB224831A33E2E18232655D15EA985EC0C8FB774BFA967B734A60DD8FC1F9214B7C262050F269C248F3255D5D1E7BD63626707232FF72"); - // assert_eq!(res.retrieve(), expected); - // } + x1 } diff --git a/src/uint/modular/reduction.rs b/src/uint/modular/reduction.rs new file mode 100644 index 00000000..da4895a2 --- /dev/null +++ b/src/uint/modular/reduction.rs @@ -0,0 +1,57 @@ +use crate::{Limb, UInt, WideWord, Word}; + +/// Algorithm 14.32 in Handbook of Applied Cryptography (https://cacr.uwaterloo.ca/hac/about/chap14.pdf) +pub(crate) const fn montgomery_reduction( + lower_upper: (UInt, UInt), + modulus: UInt, + mod_neg_inv: Limb, +) -> UInt { + let (mut lower, mut upper) = lower_upper; + + let mut meta_carry = 0; + + let mut i = 0; + while i < LIMBS { + let u = (lower.limbs[i].0.wrapping_mul(mod_neg_inv.0)) as WideWord; + + let new_limb = + (u * modulus.limbs[0].0 as WideWord).wrapping_add(lower.limbs[i].0 as WideWord); + let mut carry = new_limb >> Word::BITS; + + let mut j = 1; + while j < (LIMBS - i) { + let new_limb = (u * modulus.limbs[j].0 as WideWord) + .wrapping_add(lower.limbs[i + j].0 as WideWord) + .wrapping_add(carry); + carry = new_limb >> Word::BITS; + lower.limbs[i + j] = Limb(new_limb as Word); + + j += 1; + } + while j < LIMBS { + let new_limb = (u * modulus.limbs[j].0 as WideWord) + .wrapping_add(upper.limbs[i + j - LIMBS].0 as WideWord) + .wrapping_add(carry); + carry = new_limb >> Word::BITS; + upper.limbs[i + j - LIMBS] = Limb(new_limb as Word); + + j += 1; + } + + let new_sum = (upper.limbs[i].0 as WideWord) + .wrapping_add(carry) + .wrapping_add(meta_carry); + meta_carry = new_sum >> Word::BITS; + upper.limbs[i] = Limb(new_sum as Word); + + i += 1; + } + + // Division is simply taking the upper half of the limbs + // Final reduction (at this point, the value is at most 2 * modulus) + let must_reduce = (meta_carry as Word).saturating_mul(Word::MAX) + | ((upper.ct_cmp(&modulus) != -1) as Word).saturating_mul(Word::MAX); + upper = upper.wrapping_sub(&UInt::ct_select(UInt::ZERO, modulus, must_reduce)); + + upper +} diff --git a/src/uint/modular/runtime_mod/mod.rs b/src/uint/modular/runtime_mod/mod.rs new file mode 100644 index 00000000..2ad89caf --- /dev/null +++ b/src/uint/modular/runtime_mod/mod.rs @@ -0,0 +1,60 @@ +use crate::{Limb, UInt}; + +use super::{reduction::montgomery_reduction, GenericResidue}; + +/// Additions between residues with a modulus set at runtime +mod runtime_add; +/// Multiplications between residues with a modulus set at runtime +mod runtime_mul; +/// Exponentiation of residues with a modulus set at runtime +mod runtime_pow; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ResidueParams { + // The constant modulus + modulus: UInt, + // Parameter used in Montgomery reduction + r: UInt, + // R^2, used to move into Montgomery form + r2: UInt, + // The lowest limbs of -(MODULUS^-1) mod R + // We only need the LSB because during reduction this value is multiplied modulo 2**64. + mod_neg_inv: Limb, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Residue { + montgomery_form: UInt, + residue_params: ResidueParams, +} + +impl Residue { + /// Instantiates a new `Residue` that represents this `integer` mod `MOD`. + pub const fn new(integer: UInt, residue_params: ResidueParams) -> Self { + let mut modular_integer = Self { + montgomery_form: integer, + residue_params, + }; + + let product = integer.mul_wide(&residue_params.r2); + modular_integer.montgomery_form = + montgomery_reduction(product, residue_params.modulus, residue_params.mod_neg_inv); + + modular_integer + } + + /// Retrieves the `integer` currently encoded in this `Residue`, guaranteed to be reduced. + pub const fn retrieve(&self) -> UInt { + montgomery_reduction( + (self.montgomery_form, UInt::ZERO), + self.residue_params.modulus, + self.residue_params.mod_neg_inv, + ) + } +} + +impl GenericResidue for Residue { + fn retrieve(&self) -> UInt { + self.retrieve() + } +} diff --git a/src/uint/modular/runtime_mod/runtime_add.rs b/src/uint/modular/runtime_mod/runtime_add.rs new file mode 100644 index 00000000..88b3b9aa --- /dev/null +++ b/src/uint/modular/runtime_mod/runtime_add.rs @@ -0,0 +1,38 @@ +use core::ops::{Add, AddAssign}; + +use crate::modular::add::{add_montgomery_form, AddResidue}; + +use super::Residue; + +impl AddResidue for Residue { + fn add(&self, rhs: &Self) -> Self { + debug_assert_eq!(self.residue_params, rhs.residue_params); + Self { + montgomery_form: add_montgomery_form( + &self.montgomery_form, + &rhs.montgomery_form, + &self.residue_params.modulus, + ), + residue_params: self.residue_params, + } + } +} + +impl AddAssign for Residue { + fn add_assign(&mut self, rhs: Self) { + self.montgomery_form = add_montgomery_form( + &self.montgomery_form, + &rhs.montgomery_form, + &self.residue_params.modulus, + ); + } +} + +impl Add for Residue { + type Output = Residue; + + fn add(mut self, rhs: Self) -> Self::Output { + self += rhs; + self + } +} diff --git a/src/uint/modular/runtime_mod/runtime_mul.rs b/src/uint/modular/runtime_mod/runtime_mul.rs new file mode 100644 index 00000000..948d211c --- /dev/null +++ b/src/uint/modular/runtime_mod/runtime_mul.rs @@ -0,0 +1,52 @@ +use core::ops::{Mul, MulAssign}; + +use crate::modular::mul::{mul_montgomery_form, square_montgomery_form, MulResidue}; + +use super::Residue; + +impl MulResidue for Residue { + fn mul(&self, rhs: &Self) -> Self { + debug_assert_eq!(self.residue_params, rhs.residue_params); + Self { + montgomery_form: mul_montgomery_form( + &self.montgomery_form, + &rhs.montgomery_form, + self.residue_params.modulus, + self.residue_params.mod_neg_inv, + ), + residue_params: self.residue_params, + } + } + + fn square(&self) -> Self { + Self { + montgomery_form: square_montgomery_form( + &self.montgomery_form, + self.residue_params.modulus, + self.residue_params.mod_neg_inv, + ), + residue_params: self.residue_params, + } + } +} + +impl MulAssign for Residue { + fn mul_assign(&mut self, rhs: Self) { + debug_assert_eq!(self.residue_params, rhs.residue_params); + self.montgomery_form = mul_montgomery_form( + &self.montgomery_form, + &rhs.montgomery_form, + self.residue_params.modulus, + self.residue_params.mod_neg_inv, + ); + } +} + +impl Mul for Residue { + type Output = Residue; + + fn mul(mut self, rhs: Self) -> Self::Output { + self *= rhs; + self + } +} diff --git a/src/uint/modular/runtime_mod/runtime_pow.rs b/src/uint/modular/runtime_mod/runtime_pow.rs new file mode 100644 index 00000000..6bc90f89 --- /dev/null +++ b/src/uint/modular/runtime_mod/runtime_pow.rs @@ -0,0 +1,28 @@ +use crate::{ + modular::pow::{pow_montgomery_form, PowResidue}, + UInt, +}; + +use super::Residue; + +impl PowResidue for Residue { + fn pow_specific(self, exponent: &UInt, exponent_bits: usize) -> Self { + self.pow_specific(exponent, exponent_bits) + } +} + +impl Residue { + pub const fn pow_specific(self, exponent: &UInt, exponent_bits: usize) -> Self { + Self { + montgomery_form: pow_montgomery_form( + self.montgomery_form, + exponent, + exponent_bits, + self.residue_params.modulus, + self.residue_params.r, + self.residue_params.mod_neg_inv, + ), + residue_params: self.residue_params, + } + } +} From 9051b11785f3eb0c10efe8c9b47070f4f291b179 Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Fri, 4 Nov 2022 16:05:34 +0100 Subject: [PATCH 16/23] Write docstring where required --- src/uint/modular/constant_mod/const_add.rs | 1 + src/uint/modular/constant_mod/mod.rs | 1 + src/uint/modular/mod.rs | 5 ++++- src/uint/modular/runtime_mod/mod.rs | 4 +++- src/uint/modular/runtime_mod/runtime_pow.rs | 1 + 5 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/uint/modular/constant_mod/const_add.rs b/src/uint/modular/constant_mod/const_add.rs index 1a6f22b5..6c76aa30 100644 --- a/src/uint/modular/constant_mod/const_add.rs +++ b/src/uint/modular/constant_mod/const_add.rs @@ -14,6 +14,7 @@ impl, const LIMBS: usize> AddResidue for ConstRes } impl, const LIMBS: usize> ConstResidue { + /// Adds two residues together. pub const fn add(&self, rhs: &Self) -> Self { ConstResidue { montgomery_form: add_montgomery_form( diff --git a/src/uint/modular/constant_mod/mod.rs b/src/uint/modular/constant_mod/mod.rs index dd21a3db..3883047b 100644 --- a/src/uint/modular/constant_mod/mod.rs +++ b/src/uint/modular/constant_mod/mod.rs @@ -66,6 +66,7 @@ impl, const LIMBS: usize> ConstResidue UInt { montgomery_reduction::( (self.montgomery_form, UInt::ZERO), diff --git a/src/uint/modular/mod.rs b/src/uint/modular/mod.rs index ba3d79f4..8fe08566 100644 --- a/src/uint/modular/mod.rs +++ b/src/uint/modular/mod.rs @@ -4,15 +4,18 @@ use self::{add::AddResidue, mul::MulResidue, pow::PowResidue}; mod reduction; +/// Implements `ConstResidue`s, supporting modular arithmetic with a constant modulus. pub mod constant_mod; +/// Implements `Residue`s, supporting modular arithmetic with a modulus set at runtime. pub mod runtime_mod; mod add; mod mul; mod pow; +/// The `GenericResidue` trait provides a consistent API for dealing with residues with a constant modulus and those with a modulus chosen at runtime. pub trait GenericResidue: AddResidue + MulResidue + PowResidue { - /// Retrieves the `integer` currently encoded in this `Residue`, guaranteed to be reduced. + /// Retrieves the integer currently encoded in this `Residue`, guaranteed to be reduced. fn retrieve(&self) -> UInt; } diff --git a/src/uint/modular/runtime_mod/mod.rs b/src/uint/modular/runtime_mod/mod.rs index 2ad89caf..4e732714 100644 --- a/src/uint/modular/runtime_mod/mod.rs +++ b/src/uint/modular/runtime_mod/mod.rs @@ -9,6 +9,7 @@ mod runtime_mul; /// Exponentiation of residues with a modulus set at runtime mod runtime_pow; +/// The parameters to efficiently go to and from the Montgomery form for a modulus provided at runtime. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ResidueParams { // The constant modulus @@ -22,6 +23,7 @@ pub struct ResidueParams { mod_neg_inv: Limb, } +/// A residue represented using `LIMBS` limbs. The modulus of this residue is set at runtime. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Residue { montgomery_form: UInt, @@ -43,7 +45,7 @@ impl Residue { modular_integer } - /// Retrieves the `integer` currently encoded in this `Residue`, guaranteed to be reduced. + /// Retrieves the integer currently encoded in this `Residue`, guaranteed to be reduced. pub const fn retrieve(&self) -> UInt { montgomery_reduction( (self.montgomery_form, UInt::ZERO), diff --git a/src/uint/modular/runtime_mod/runtime_pow.rs b/src/uint/modular/runtime_mod/runtime_pow.rs index 6bc90f89..56e2e5f9 100644 --- a/src/uint/modular/runtime_mod/runtime_pow.rs +++ b/src/uint/modular/runtime_mod/runtime_pow.rs @@ -12,6 +12,7 @@ impl PowResidue for Residue { } impl Residue { + /// Computes the (reduced) exponentiation of a residue, here `exponent_bits` represents the number of bits to take into account for the exponent. Note that this value is leaked in the time pattern. pub const fn pow_specific(self, exponent: &UInt, exponent_bits: usize) -> Self { Self { montgomery_form: pow_montgomery_form( From beccc95e4beb03c0b14ae7c5ba5b0468a64c1c06 Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Sat, 5 Nov 2022 09:32:10 +0100 Subject: [PATCH 17/23] Implement inverse for residues --- src/uint/inv_mod.rs | 12 ++--- src/uint/modular/constant_mod/const_inv.rs | 52 +++++++++++++++++++++ src/uint/modular/constant_mod/const_mul.rs | 21 +++++++-- src/uint/modular/constant_mod/macros.rs | 6 +++ src/uint/modular/constant_mod/mod.rs | 6 ++- src/uint/modular/inv.rs | 17 +++++++ src/uint/modular/mod.rs | 7 ++- src/uint/modular/runtime_mod/mod.rs | 27 ++++++++++- src/uint/modular/runtime_mod/runtime_add.rs | 36 +++++++++++++- src/uint/modular/runtime_mod/runtime_inv.rs | 17 +++++++ 10 files changed, 185 insertions(+), 16 deletions(-) create mode 100644 src/uint/modular/constant_mod/const_inv.rs create mode 100644 src/uint/modular/inv.rs create mode 100644 src/uint/modular/runtime_mod/runtime_inv.rs diff --git a/src/uint/inv_mod.rs b/src/uint/inv_mod.rs index e2bca77d..911fbc4e 100644 --- a/src/uint/inv_mod.rs +++ b/src/uint/inv_mod.rs @@ -29,7 +29,7 @@ impl UInt { } /// Computes the multiplicative inverse of `self` mod `modulus`. In other words `self^-1 mod modulus`. Returns `(inverse, 1...1)` if an inverse exists, otherwise `(undefined, 0...0)`. The algorithm is the same as in GMP 6.2.1's `mpn_sec_invert`. - pub(crate) const fn ct_inv_mod(self, modulus: UInt) -> (Self, Word) { + pub const fn inv_odd_mod(self, modulus: UInt) -> (Self, Word) { debug_assert!(modulus.ct_is_odd() == Word::MAX); let mut a = self; @@ -84,8 +84,8 @@ impl UInt { } /// Computes the multiplicative inverse of `self` mod `modulus`. In other words `self^-1 mod modulus`. Returns `None` if the inverse does not exist. The algorithm is the same as in GMP 6.2.1's `mpn_sec_invert`. - pub fn inv_mod(self, modulus: UInt) -> CtOption { - let (inverse, exists) = self.ct_inv_mod(modulus); + pub fn inv_odd_mod_option(self, modulus: UInt) -> CtOption { + let (inverse, exists) = self.inv_odd_mod(modulus); CtOption::new(inverse, Choice::from((exists == Word::MAX) as u8)) } } @@ -128,7 +128,7 @@ mod tests { let a = U1024::from_be_hex("000225E99153B467A5B451979A3F451DAEF3BF8D6C6521D2FA24BBB17F29544E347A412B065B75A351EA9719E2430D2477B11CC9CF9C1AD6EDEE26CB15F463F8BCC72EF87EA30288E95A48AA792226CEC959DCB0672D8F9D80A54CBBEA85CAD8382EC224DEB2F5784E62D0CC2F81C2E6AD14EBABE646D6764B30C32B87688985"); let m = U1024::from_be_hex("D509E7854ABDC81921F669F1DC6F61359523F3949803E58ED4EA8BC16483DC6F37BFE27A9AC9EEA2969B357ABC5C0EE214BE16A7D4C58FC620D5B5A20AFF001AD198D3155E5799DC4EA76652D64983A7E130B5EACEBAC768D28D589C36EC749C558D0B64E37CD0775C0D0104AE7D98BA23C815185DD43CD8B16292FD94156767"); - let res = a.inv_mod(m); + let res = a.inv_odd_mod_option(m); let expected = U1024::from_be_hex("B03623284B0EBABCABD5C5881893320281460C0A8E7BF4BFDCFFCBCCBF436A55D364235C8171E46C7D21AAD0680676E57274A8FDA6D12768EF961CACDD2DAE5788D93DA5EB8EDC391EE3726CDCF4613C539F7D23E8702200CB31B5ED5B06E5CA3E520968399B4017BF98A864FABA2B647EFC4998B56774D4F2CB026BC024A336"); assert_eq!(res.unwrap(), expected); @@ -139,7 +139,7 @@ mod tests { let a = U64::from(3u64); let m = U64::from(13u64); - let res = a.inv_mod(m); + let res = a.inv_odd_mod_option(m); assert_eq!(U64::from(9u64), res.unwrap()); } @@ -149,7 +149,7 @@ mod tests { let a = U64::from(14u64); let m = U64::from(49u64); - let res = a.inv_mod(m); + let res = a.inv_odd_mod_option(m); assert!(res.is_none().unwrap_u8() == 1); } diff --git a/src/uint/modular/constant_mod/const_inv.rs b/src/uint/modular/constant_mod/const_inv.rs new file mode 100644 index 00000000..28cc2c80 --- /dev/null +++ b/src/uint/modular/constant_mod/const_inv.rs @@ -0,0 +1,52 @@ +use core::marker::PhantomData; + +use crate::modular::inv::{inv_montgomery_form, InvResidue}; + +use super::{ConstResidue, ConstResidueParams}; + +impl, const LIMBS: usize> InvResidue for ConstResidue { + fn inv(self) -> Self { + self.inv() + } +} + +impl, const LIMBS: usize> ConstResidue { + /// Computes the residue `self^-1` representing the multiplicative inverse of `self`. I.e. `self * self^-1 = 1`. + pub const fn inv(self) -> Self { + Self { + montgomery_form: inv_montgomery_form( + self.montgomery_form, + MOD::MODULUS, + &MOD::R3, + MOD::MOD_NEG_INV, + ), + phantom: PhantomData, + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + const_residue, impl_modulus, modular::constant_mod::ConstResidueParams, traits::Encoding, + U256, + }; + + impl_modulus!( + Modulus, + U256, + "15477BCCEFE197328255BFA79A1217899016D927EF460F4FF404029D24FA4409" + ); + + #[test] + fn test_self_inverse() { + let x = + U256::from_be_hex("77117F1273373C26C700D076B3F780074D03339F56DD0EFB60E7F58441FD3685"); + let x_mod = const_residue!(x, Modulus); + + let inv = x_mod.inv(); + let res = &x_mod * &inv; + + assert_eq!(res.retrieve(), U256::ONE); + } +} diff --git a/src/uint/modular/constant_mod/const_mul.rs b/src/uint/modular/constant_mod/const_mul.rs index 636c4f1b..8a783e8c 100644 --- a/src/uint/modular/constant_mod/const_mul.rs +++ b/src/uint/modular/constant_mod/const_mul.rs @@ -1,4 +1,7 @@ -use core::{marker::PhantomData, ops::MulAssign}; +use core::{ + marker::PhantomData, + ops::{Mul, MulAssign}, +}; use crate::modular::{ mul::{mul_montgomery_form, MulResidue}, @@ -42,8 +45,18 @@ impl, const LIMBS: usize> ConstResidue, const LIMBS: usize> MulAssign for ConstResidue { - fn mul_assign(&mut self, rhs: Self) { - *self = self.mul(&rhs) +impl, const LIMBS: usize> MulAssign<&Self> + for ConstResidue +{ + fn mul_assign(&mut self, rhs: &Self) { + *self = self.mul(rhs) + } +} + +impl, const LIMBS: usize> Mul for &ConstResidue { + type Output = ConstResidue; + + fn mul(self, rhs: Self) -> Self::Output { + self.mul(rhs) } } diff --git a/src/uint/modular/constant_mod/macros.rs b/src/uint/modular/constant_mod/macros.rs index 0e55a502..2717b328 100644 --- a/src/uint/modular/constant_mod/macros.rs +++ b/src/uint/modular/constant_mod/macros.rs @@ -25,6 +25,12 @@ macro_rules! impl_modulus { $crate::Word::MIN .wrapping_sub(Self::MODULUS.inv_mod2k($crate::Word::BITS as usize).limbs[0].0), ); + const R3: $crate::UInt<{ nlimbs!(<$uint_type>::BIT_SIZE) }> = + $crate::uint::modular::reduction::montgomery_reduction( + Self::R2.square_wide(), + Self::MODULUS, + Self::MOD_NEG_INV, + ); } }; } diff --git a/src/uint/modular/constant_mod/mod.rs b/src/uint/modular/constant_mod/mod.rs index 3883047b..15e0f220 100644 --- a/src/uint/modular/constant_mod/mod.rs +++ b/src/uint/modular/constant_mod/mod.rs @@ -8,6 +8,8 @@ use super::{reduction::montgomery_reduction, GenericResidue}; /// Additions between residues with a constant modulus mod const_add; +/// Multiplicative inverses of residues with a constant modulus +mod const_inv; /// Multiplications between residues with a constant modulus mod const_mul; /// Exponentiation of residues with a constant modulus @@ -17,7 +19,7 @@ mod const_pow; /// Macros to remove the boilerplate code when dealing with constant moduli. pub mod macros; -/// The parameters to efficiently go to and from the Montgomery form for a given modulus. An easy way to generate these parameters is using the `impl_modulus!` macro. These parameters are constant, so they cannot be set at runtime. +/// The parameters to efficiently go to and from the Montgomery form for a given odd modulus. An easy way to generate these parameters is using the `impl_modulus!` macro. These parameters are constant, so they cannot be set at runtime. /// /// Unfortunately, `LIMBS` must be generic for now until const generics are stabilized. pub trait ConstResidueParams: Copy { @@ -30,6 +32,8 @@ pub trait ConstResidueParams: Copy { const R: UInt; /// R^2, used to move into Montgomery form const R2: UInt; + /// R^3, used to perform a multiplicative inverse + const R3: UInt; /// The lowest limbs of -(MODULUS^-1) mod R // We only need the LSB because during reduction this value is multiplied modulo 2**64. const MOD_NEG_INV: Limb; diff --git a/src/uint/modular/inv.rs b/src/uint/modular/inv.rs new file mode 100644 index 00000000..c51d6c0a --- /dev/null +++ b/src/uint/modular/inv.rs @@ -0,0 +1,17 @@ +use crate::{modular::reduction::montgomery_reduction, Limb, UInt, Word}; + +pub trait InvResidue { + /// Computes the (reduced) multiplicative inverse of the residue. + fn inv(self) -> Self; +} + +pub const fn inv_montgomery_form( + x: UInt, + modulus: UInt, + r3: &UInt, + mod_neg_inv: Limb, +) -> UInt { + let (inverse, error) = x.inv_odd_mod(modulus); + debug_assert!(error == Word::MAX); + montgomery_reduction(inverse.mul_wide(r3), modulus, mod_neg_inv) +} diff --git a/src/uint/modular/mod.rs b/src/uint/modular/mod.rs index 8fe08566..4220c2f5 100644 --- a/src/uint/modular/mod.rs +++ b/src/uint/modular/mod.rs @@ -1,6 +1,6 @@ use crate::UInt; -use self::{add::AddResidue, mul::MulResidue, pow::PowResidue}; +use self::{add::AddResidue, inv::InvResidue, mul::MulResidue, pow::PowResidue}; mod reduction; @@ -10,11 +10,14 @@ pub mod constant_mod; pub mod runtime_mod; mod add; +mod inv; mod mul; mod pow; /// The `GenericResidue` trait provides a consistent API for dealing with residues with a constant modulus and those with a modulus chosen at runtime. -pub trait GenericResidue: AddResidue + MulResidue + PowResidue { +pub trait GenericResidue: + AddResidue + MulResidue + PowResidue + InvResidue +{ /// Retrieves the integer currently encoded in this `Residue`, guaranteed to be reduced. fn retrieve(&self) -> UInt; } diff --git a/src/uint/modular/runtime_mod/mod.rs b/src/uint/modular/runtime_mod/mod.rs index 4e732714..13d6d80f 100644 --- a/src/uint/modular/runtime_mod/mod.rs +++ b/src/uint/modular/runtime_mod/mod.rs @@ -1,9 +1,11 @@ -use crate::{Limb, UInt}; +use crate::{Limb, UInt, Word}; use super::{reduction::montgomery_reduction, GenericResidue}; /// Additions between residues with a modulus set at runtime mod runtime_add; +/// Multiplicative inverses of residues with a modulus set at runtime +mod runtime_inv; /// Multiplications between residues with a modulus set at runtime mod runtime_mul; /// Exponentiation of residues with a modulus set at runtime @@ -18,12 +20,33 @@ pub struct ResidueParams { r: UInt, // R^2, used to move into Montgomery form r2: UInt, + // R^3, used to compute the multiplicative inverse + r3: UInt, // The lowest limbs of -(MODULUS^-1) mod R // We only need the LSB because during reduction this value is multiplied modulo 2**64. mod_neg_inv: Limb, } -/// A residue represented using `LIMBS` limbs. The modulus of this residue is set at runtime. +impl ResidueParams { + /// Instantiates a new set of `ResidueParams` representing the given `modulus`. + pub fn new(modulus: UInt) -> Self { + let r = UInt::MAX.ct_reduce(&modulus).0.wrapping_add(&UInt::ONE); + let r2 = UInt::ct_reduce_wide(r.square_wide(), &modulus).0; + let mod_neg_inv = + Limb(Word::MIN.wrapping_sub(modulus.inv_mod2k(Word::BITS as usize).limbs[0].0)); + let r3 = montgomery_reduction(r2.square_wide(), modulus, mod_neg_inv); + + Self { + modulus, + r, + r2, + r3, + mod_neg_inv, + } + } +} + +/// A residue represented using `LIMBS` limbs. The odd modulus of this residue is set at runtime. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Residue { montgomery_form: UInt, diff --git a/src/uint/modular/runtime_mod/runtime_add.rs b/src/uint/modular/runtime_mod/runtime_add.rs index 88b3b9aa..f90f3ffb 100644 --- a/src/uint/modular/runtime_mod/runtime_add.rs +++ b/src/uint/modular/runtime_mod/runtime_add.rs @@ -1,6 +1,6 @@ use core::ops::{Add, AddAssign}; -use crate::modular::add::{add_montgomery_form, AddResidue}; +use crate::{modular::add::{add_montgomery_form, AddResidue}, UInt}; use super::Residue; @@ -28,6 +28,16 @@ impl AddAssign for Residue { } } +impl AddAssign> for Residue { + fn add_assign(&mut self, rhs: UInt) { + self.montgomery_form = add_montgomery_form( + &self.montgomery_form, + &Residue::new(rhs, self.residue_params).montgomery_form, + &self.residue_params.modulus, + ); + } +} + impl Add for Residue { type Output = Residue; @@ -36,3 +46,27 @@ impl Add for Residue { self } } + +#[cfg(test)] +mod tests { + use crate::{modular::runtime_mod::{ResidueParams, Residue}, U256}; + + #[test] + fn add_overflow() { + let params = ResidueParams::new(U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551")); + + let x = + U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); + let mut x_mod = Residue::new(x, params); + + let y = + U256::from_be_hex("d5777c45019673125ad240f83094d4252d829516fac8601ed01979ec1ec1a251"); + + x_mod += y; + + let expected = + U256::from_be_hex("1a2472fde50286541d97ca6a3592dd75beb9c9646e40c511b82496cfc3926956"); + + assert_eq!(expected, x_mod.retrieve()); + } +} diff --git a/src/uint/modular/runtime_mod/runtime_inv.rs b/src/uint/modular/runtime_mod/runtime_inv.rs new file mode 100644 index 00000000..7e0ccb26 --- /dev/null +++ b/src/uint/modular/runtime_mod/runtime_inv.rs @@ -0,0 +1,17 @@ +use crate::modular::inv::{inv_montgomery_form, InvResidue}; + +use super::Residue; + +impl InvResidue for Residue { + fn inv(self) -> Self { + Self { + montgomery_form: inv_montgomery_form( + self.montgomery_form, + self.residue_params.modulus, + &self.residue_params.r3, + self.residue_params.mod_neg_inv, + ), + residue_params: self.residue_params, + } + } +} From 2fff3e10d4df1db808c488e0bdc7b194dce14ec4 Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Sat, 5 Nov 2022 09:32:27 +0100 Subject: [PATCH 18/23] Cargo fmt --- src/uint/modular/runtime_mod/runtime_add.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/uint/modular/runtime_mod/runtime_add.rs b/src/uint/modular/runtime_mod/runtime_add.rs index f90f3ffb..ad83f61c 100644 --- a/src/uint/modular/runtime_mod/runtime_add.rs +++ b/src/uint/modular/runtime_mod/runtime_add.rs @@ -1,6 +1,9 @@ use core::ops::{Add, AddAssign}; -use crate::{modular::add::{add_montgomery_form, AddResidue}, UInt}; +use crate::{ + modular::add::{add_montgomery_form, AddResidue}, + UInt, +}; use super::Residue; @@ -49,11 +52,16 @@ impl Add for Residue { #[cfg(test)] mod tests { - use crate::{modular::runtime_mod::{ResidueParams, Residue}, U256}; + use crate::{ + modular::runtime_mod::{Residue, ResidueParams}, + U256, + }; #[test] fn add_overflow() { - let params = ResidueParams::new(U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551")); + let params = ResidueParams::new(U256::from_be_hex( + "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", + )); let x = U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); From 423e60f27e953fdbbe62340ef163d5110959a12d Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Sat, 5 Nov 2022 09:35:50 +0100 Subject: [PATCH 19/23] Remove runtime moduli (not in PR scope) --- src/uint/modular/mod.rs | 2 - src/uint/modular/runtime_mod/mod.rs | 85 --------------------- src/uint/modular/runtime_mod/runtime_add.rs | 80 ------------------- src/uint/modular/runtime_mod/runtime_inv.rs | 17 ----- src/uint/modular/runtime_mod/runtime_mul.rs | 52 ------------- src/uint/modular/runtime_mod/runtime_pow.rs | 29 ------- 6 files changed, 265 deletions(-) delete mode 100644 src/uint/modular/runtime_mod/mod.rs delete mode 100644 src/uint/modular/runtime_mod/runtime_add.rs delete mode 100644 src/uint/modular/runtime_mod/runtime_inv.rs delete mode 100644 src/uint/modular/runtime_mod/runtime_mul.rs delete mode 100644 src/uint/modular/runtime_mod/runtime_pow.rs diff --git a/src/uint/modular/mod.rs b/src/uint/modular/mod.rs index 4220c2f5..0c5d7dcb 100644 --- a/src/uint/modular/mod.rs +++ b/src/uint/modular/mod.rs @@ -6,8 +6,6 @@ mod reduction; /// Implements `ConstResidue`s, supporting modular arithmetic with a constant modulus. pub mod constant_mod; -/// Implements `Residue`s, supporting modular arithmetic with a modulus set at runtime. -pub mod runtime_mod; mod add; mod inv; diff --git a/src/uint/modular/runtime_mod/mod.rs b/src/uint/modular/runtime_mod/mod.rs deleted file mode 100644 index 13d6d80f..00000000 --- a/src/uint/modular/runtime_mod/mod.rs +++ /dev/null @@ -1,85 +0,0 @@ -use crate::{Limb, UInt, Word}; - -use super::{reduction::montgomery_reduction, GenericResidue}; - -/// Additions between residues with a modulus set at runtime -mod runtime_add; -/// Multiplicative inverses of residues with a modulus set at runtime -mod runtime_inv; -/// Multiplications between residues with a modulus set at runtime -mod runtime_mul; -/// Exponentiation of residues with a modulus set at runtime -mod runtime_pow; - -/// The parameters to efficiently go to and from the Montgomery form for a modulus provided at runtime. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct ResidueParams { - // The constant modulus - modulus: UInt, - // Parameter used in Montgomery reduction - r: UInt, - // R^2, used to move into Montgomery form - r2: UInt, - // R^3, used to compute the multiplicative inverse - r3: UInt, - // The lowest limbs of -(MODULUS^-1) mod R - // We only need the LSB because during reduction this value is multiplied modulo 2**64. - mod_neg_inv: Limb, -} - -impl ResidueParams { - /// Instantiates a new set of `ResidueParams` representing the given `modulus`. - pub fn new(modulus: UInt) -> Self { - let r = UInt::MAX.ct_reduce(&modulus).0.wrapping_add(&UInt::ONE); - let r2 = UInt::ct_reduce_wide(r.square_wide(), &modulus).0; - let mod_neg_inv = - Limb(Word::MIN.wrapping_sub(modulus.inv_mod2k(Word::BITS as usize).limbs[0].0)); - let r3 = montgomery_reduction(r2.square_wide(), modulus, mod_neg_inv); - - Self { - modulus, - r, - r2, - r3, - mod_neg_inv, - } - } -} - -/// A residue represented using `LIMBS` limbs. The odd modulus of this residue is set at runtime. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Residue { - montgomery_form: UInt, - residue_params: ResidueParams, -} - -impl Residue { - /// Instantiates a new `Residue` that represents this `integer` mod `MOD`. - pub const fn new(integer: UInt, residue_params: ResidueParams) -> Self { - let mut modular_integer = Self { - montgomery_form: integer, - residue_params, - }; - - let product = integer.mul_wide(&residue_params.r2); - modular_integer.montgomery_form = - montgomery_reduction(product, residue_params.modulus, residue_params.mod_neg_inv); - - modular_integer - } - - /// Retrieves the integer currently encoded in this `Residue`, guaranteed to be reduced. - pub const fn retrieve(&self) -> UInt { - montgomery_reduction( - (self.montgomery_form, UInt::ZERO), - self.residue_params.modulus, - self.residue_params.mod_neg_inv, - ) - } -} - -impl GenericResidue for Residue { - fn retrieve(&self) -> UInt { - self.retrieve() - } -} diff --git a/src/uint/modular/runtime_mod/runtime_add.rs b/src/uint/modular/runtime_mod/runtime_add.rs deleted file mode 100644 index ad83f61c..00000000 --- a/src/uint/modular/runtime_mod/runtime_add.rs +++ /dev/null @@ -1,80 +0,0 @@ -use core::ops::{Add, AddAssign}; - -use crate::{ - modular::add::{add_montgomery_form, AddResidue}, - UInt, -}; - -use super::Residue; - -impl AddResidue for Residue { - fn add(&self, rhs: &Self) -> Self { - debug_assert_eq!(self.residue_params, rhs.residue_params); - Self { - montgomery_form: add_montgomery_form( - &self.montgomery_form, - &rhs.montgomery_form, - &self.residue_params.modulus, - ), - residue_params: self.residue_params, - } - } -} - -impl AddAssign for Residue { - fn add_assign(&mut self, rhs: Self) { - self.montgomery_form = add_montgomery_form( - &self.montgomery_form, - &rhs.montgomery_form, - &self.residue_params.modulus, - ); - } -} - -impl AddAssign> for Residue { - fn add_assign(&mut self, rhs: UInt) { - self.montgomery_form = add_montgomery_form( - &self.montgomery_form, - &Residue::new(rhs, self.residue_params).montgomery_form, - &self.residue_params.modulus, - ); - } -} - -impl Add for Residue { - type Output = Residue; - - fn add(mut self, rhs: Self) -> Self::Output { - self += rhs; - self - } -} - -#[cfg(test)] -mod tests { - use crate::{ - modular::runtime_mod::{Residue, ResidueParams}, - U256, - }; - - #[test] - fn add_overflow() { - let params = ResidueParams::new(U256::from_be_hex( - "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", - )); - - let x = - U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); - let mut x_mod = Residue::new(x, params); - - let y = - U256::from_be_hex("d5777c45019673125ad240f83094d4252d829516fac8601ed01979ec1ec1a251"); - - x_mod += y; - - let expected = - U256::from_be_hex("1a2472fde50286541d97ca6a3592dd75beb9c9646e40c511b82496cfc3926956"); - - assert_eq!(expected, x_mod.retrieve()); - } -} diff --git a/src/uint/modular/runtime_mod/runtime_inv.rs b/src/uint/modular/runtime_mod/runtime_inv.rs deleted file mode 100644 index 7e0ccb26..00000000 --- a/src/uint/modular/runtime_mod/runtime_inv.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::modular::inv::{inv_montgomery_form, InvResidue}; - -use super::Residue; - -impl InvResidue for Residue { - fn inv(self) -> Self { - Self { - montgomery_form: inv_montgomery_form( - self.montgomery_form, - self.residue_params.modulus, - &self.residue_params.r3, - self.residue_params.mod_neg_inv, - ), - residue_params: self.residue_params, - } - } -} diff --git a/src/uint/modular/runtime_mod/runtime_mul.rs b/src/uint/modular/runtime_mod/runtime_mul.rs deleted file mode 100644 index 948d211c..00000000 --- a/src/uint/modular/runtime_mod/runtime_mul.rs +++ /dev/null @@ -1,52 +0,0 @@ -use core::ops::{Mul, MulAssign}; - -use crate::modular::mul::{mul_montgomery_form, square_montgomery_form, MulResidue}; - -use super::Residue; - -impl MulResidue for Residue { - fn mul(&self, rhs: &Self) -> Self { - debug_assert_eq!(self.residue_params, rhs.residue_params); - Self { - montgomery_form: mul_montgomery_form( - &self.montgomery_form, - &rhs.montgomery_form, - self.residue_params.modulus, - self.residue_params.mod_neg_inv, - ), - residue_params: self.residue_params, - } - } - - fn square(&self) -> Self { - Self { - montgomery_form: square_montgomery_form( - &self.montgomery_form, - self.residue_params.modulus, - self.residue_params.mod_neg_inv, - ), - residue_params: self.residue_params, - } - } -} - -impl MulAssign for Residue { - fn mul_assign(&mut self, rhs: Self) { - debug_assert_eq!(self.residue_params, rhs.residue_params); - self.montgomery_form = mul_montgomery_form( - &self.montgomery_form, - &rhs.montgomery_form, - self.residue_params.modulus, - self.residue_params.mod_neg_inv, - ); - } -} - -impl Mul for Residue { - type Output = Residue; - - fn mul(mut self, rhs: Self) -> Self::Output { - self *= rhs; - self - } -} diff --git a/src/uint/modular/runtime_mod/runtime_pow.rs b/src/uint/modular/runtime_mod/runtime_pow.rs deleted file mode 100644 index 56e2e5f9..00000000 --- a/src/uint/modular/runtime_mod/runtime_pow.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::{ - modular::pow::{pow_montgomery_form, PowResidue}, - UInt, -}; - -use super::Residue; - -impl PowResidue for Residue { - fn pow_specific(self, exponent: &UInt, exponent_bits: usize) -> Self { - self.pow_specific(exponent, exponent_bits) - } -} - -impl Residue { - /// Computes the (reduced) exponentiation of a residue, here `exponent_bits` represents the number of bits to take into account for the exponent. Note that this value is leaked in the time pattern. - pub const fn pow_specific(self, exponent: &UInt, exponent_bits: usize) -> Self { - Self { - montgomery_form: pow_montgomery_form( - self.montgomery_form, - exponent, - exponent_bits, - self.residue_params.modulus, - self.residue_params.r, - self.residue_params.mod_neg_inv, - ), - residue_params: self.residue_params, - } - } -} From b455a09f302898f2c4249f394e49714104e69c73 Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Sat, 5 Nov 2022 09:55:22 +0100 Subject: [PATCH 20/23] Fix docstring and remove stale code --- src/lib.rs | 5 +++- src/uint.rs | 2 +- src/uint/modular/constant_mod/const_pow.rs | 27 ---------------------- src/uint/modular/mod.rs | 2 +- 4 files changed, 6 insertions(+), 30 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 84e93789..995011a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,7 +93,6 @@ //! This library has initial support for modular arithmetic in the form of the //! [`AddMod`], [`SubMod`], [`NegMod`], and [`MulMod`] traits, as well as the //! support for the [`Rem`] trait when used with a [`NonZero`] operand. -//! It also supports modular arithmetic over constant moduli using `Residue`. //! //! ``` //! use crypto_bigint::{AddMod, U256}; @@ -110,6 +109,10 @@ //! assert_eq!(b, U256::ZERO); //! ``` //! +//! It also supports modular arithmetic over constant moduli using `ConstResidue`. +//! That includes modular exponentiation and multiplicative inverses. +//! These features are described in the [`modular`] module. +//! //! ### Random number generation //! //! When the `rand_core` or `rand` features of this crate are enabled, it's diff --git a/src/uint.rs b/src/uint.rs index b944440f..2e730986 100644 --- a/src/uint.rs +++ b/src/uint.rs @@ -34,7 +34,7 @@ mod sqrt; mod sub; mod sub_mod; -/// Implements modular arithmetic for constant moduli and moduli set at runtime. +/// Implements modular arithmetic for constant moduli. pub mod modular; #[cfg(feature = "generic-array")] diff --git a/src/uint/modular/constant_mod/const_pow.rs b/src/uint/modular/constant_mod/const_pow.rs index 51a8f6dd..39955ac1 100644 --- a/src/uint/modular/constant_mod/const_pow.rs +++ b/src/uint/modular/constant_mod/const_pow.rs @@ -97,31 +97,4 @@ mod tests { U256::from_be_hex("3681BC0FEA2E5D394EB178155A127B0FD2EF405486D354251C385BDD51B9D421"); assert_eq!(res.retrieve(), expected); } - - // TODO: These tests can be re-included if the `const_eval_limit` is set higher than it is currently. - // #[test] - // fn test_powmod_small_base() { - // let base = U1024::from(105u64); - // let base_mod = residue!(base, Modulus); - - // let exponent = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); - - // let res = base_mod.pow(&exponent); - - // let expected = U1024::from_be_hex("84FADE244D8A183FD9D209B07312E017F53BBDF4377108EDB4FEAD2AEB1DFF83B6CE604A2DACF49E52574E69055C6E2D30980938CF259421E17AB277C67663712B185C565C97D3200659D83B287C1D8325BFD258C7DBA4BB2766A57C5F2A7EE3FA784A8669C54839F3D29E73215E7939B16E8293524D871D56F67759D553A242"); - // assert_eq!(res.retrieve(), expected); - // } - - // #[test] - // fn test_powmod_small_exponent() { - // let base = U1024::from_be_hex("84385019BF5E0213C46BA7F0B1A9667925F9BD65FC8CE280A728AEF543B7468E7C7AA7297EF67A50F2FF3E1C36DF532F8B83684F211D98FF39B0C3CE6E3E2C86A353DC2E3A34A05139704992F59068BE80BD86FFDA8BEEF98528D12231CD102D24C1C41F778C3618DC9C1AF1ADE2AF3689683B0C05930985BF15D771F4DCCE9B"); - // let base_mod = residue!(base, Modulus); - - // let exponent = U1024::from(105u64); - - // let res = base_mod.pow(&exponent); - - // let expected = U1024::from_be_hex("6B717DC3571BEC59C1E370780630280B05F13D9BB69E192DA75EAE2A817B270840881034B0AB9EE6B47382C58424AE00F90B88DFA7DFF7C9417B28E4C9DF170BCDFC4689140E9BA067FDB224831A33E2E18232655D15EA985EC0C8FB774BFA967B734A60DD8FC1F9214B7C262050F269C248F3255D5D1E7BD63626707232FF72"); - // assert_eq!(res.retrieve(), expected); - // } } diff --git a/src/uint/modular/mod.rs b/src/uint/modular/mod.rs index 0c5d7dcb..6c4d82c2 100644 --- a/src/uint/modular/mod.rs +++ b/src/uint/modular/mod.rs @@ -12,7 +12,7 @@ mod inv; mod mul; mod pow; -/// The `GenericResidue` trait provides a consistent API for dealing with residues with a constant modulus and those with a modulus chosen at runtime. +/// The `GenericResidue` trait provides a consistent API for dealing with residues with a constant modulus. pub trait GenericResidue: AddResidue + MulResidue + PowResidue + InvResidue { From dcdb1f660d09f42c190ecc458b6a1888029e01e9 Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Sun, 6 Nov 2022 15:28:43 +0100 Subject: [PATCH 21/23] Revert ConstResidue to Residue --- src/uint/modular/constant_mod/const_add.rs | 21 +++++++++------------ src/uint/modular/constant_mod/const_inv.rs | 9 ++++----- src/uint/modular/constant_mod/const_mul.rs | 14 ++++++-------- src/uint/modular/constant_mod/const_pow.rs | 15 ++++++--------- src/uint/modular/constant_mod/macros.rs | 4 ++-- src/uint/modular/constant_mod/mod.rs | 20 +++++++++----------- src/uint/modular/mod.rs | 7 +++---- 7 files changed, 39 insertions(+), 51 deletions(-) diff --git a/src/uint/modular/constant_mod/const_add.rs b/src/uint/modular/constant_mod/const_add.rs index 6c76aa30..288b2a76 100644 --- a/src/uint/modular/constant_mod/const_add.rs +++ b/src/uint/modular/constant_mod/const_add.rs @@ -5,18 +5,18 @@ use crate::{ UInt, }; -use super::{ConstResidue, ConstResidueParams}; +use super::{Residue, ResidueParams}; -impl, const LIMBS: usize> AddResidue for ConstResidue { +impl, const LIMBS: usize> AddResidue for Residue { fn add(&self, rhs: &Self) -> Self { self.add(rhs) } } -impl, const LIMBS: usize> ConstResidue { +impl, const LIMBS: usize> Residue { /// Adds two residues together. pub const fn add(&self, rhs: &Self) -> Self { - ConstResidue { + Residue { montgomery_form: add_montgomery_form( &self.montgomery_form, &rhs.montgomery_form, @@ -27,17 +27,15 @@ impl, const LIMBS: usize> ConstResidue, const LIMBS: usize> AddAssign<&UInt> - for ConstResidue +impl, const LIMBS: usize> AddAssign<&UInt> + for Residue { fn add_assign(&mut self, rhs: &UInt) { - *self += &ConstResidue::new(*rhs); + *self += &Residue::new(*rhs); } } -impl, const LIMBS: usize> AddAssign<&Self> - for ConstResidue -{ +impl, const LIMBS: usize> AddAssign<&Self> for Residue { fn add_assign(&mut self, rhs: &Self) { *self = self.add(rhs); } @@ -46,8 +44,7 @@ impl, const LIMBS: usize> AddAssign<&Self> #[cfg(test)] mod tests { use crate::{ - const_residue, impl_modulus, modular::constant_mod::ConstResidueParams, traits::Encoding, - U256, + const_residue, impl_modulus, modular::constant_mod::ResidueParams, traits::Encoding, U256, }; impl_modulus!( diff --git a/src/uint/modular/constant_mod/const_inv.rs b/src/uint/modular/constant_mod/const_inv.rs index 28cc2c80..05712507 100644 --- a/src/uint/modular/constant_mod/const_inv.rs +++ b/src/uint/modular/constant_mod/const_inv.rs @@ -2,15 +2,15 @@ use core::marker::PhantomData; use crate::modular::inv::{inv_montgomery_form, InvResidue}; -use super::{ConstResidue, ConstResidueParams}; +use super::{Residue, ResidueParams}; -impl, const LIMBS: usize> InvResidue for ConstResidue { +impl, const LIMBS: usize> InvResidue for Residue { fn inv(self) -> Self { self.inv() } } -impl, const LIMBS: usize> ConstResidue { +impl, const LIMBS: usize> Residue { /// Computes the residue `self^-1` representing the multiplicative inverse of `self`. I.e. `self * self^-1 = 1`. pub const fn inv(self) -> Self { Self { @@ -28,8 +28,7 @@ impl, const LIMBS: usize> ConstResidue, const LIMBS: usize> MulResidue for ConstResidue { +impl, const LIMBS: usize> MulResidue for Residue { fn mul(&self, rhs: &Self) -> Self { self.mul(rhs) } @@ -20,7 +20,7 @@ impl, const LIMBS: usize> MulResidue for ConstRes } } -impl, const LIMBS: usize> ConstResidue { +impl, const LIMBS: usize> Residue { /// Computes the (reduced) product between two residues. pub const fn mul(&self, rhs: &Self) -> Self { Self { @@ -45,16 +45,14 @@ impl, const LIMBS: usize> ConstResidue, const LIMBS: usize> MulAssign<&Self> - for ConstResidue -{ +impl, const LIMBS: usize> MulAssign<&Self> for Residue { fn mul_assign(&mut self, rhs: &Self) { *self = self.mul(rhs) } } -impl, const LIMBS: usize> Mul for &ConstResidue { - type Output = ConstResidue; +impl, const LIMBS: usize> Mul for &Residue { + type Output = Residue; fn mul(self, rhs: Self) -> Self::Output { self.mul(rhs) diff --git a/src/uint/modular/constant_mod/const_pow.rs b/src/uint/modular/constant_mod/const_pow.rs index 39955ac1..321daa68 100644 --- a/src/uint/modular/constant_mod/const_pow.rs +++ b/src/uint/modular/constant_mod/const_pow.rs @@ -3,19 +3,17 @@ use crate::{ UInt, Word, }; -use super::{ConstResidue, ConstResidueParams}; +use super::{Residue, ResidueParams}; -impl, const LIMBS: usize> PowResidue - for ConstResidue -{ +impl, const LIMBS: usize> PowResidue for Residue { fn pow_specific(self, exponent: &UInt, exponent_bits: usize) -> Self { self.pow_specific(exponent, exponent_bits) } } -impl, const LIMBS: usize> ConstResidue { +impl, const LIMBS: usize> Residue { /// Performs modular exponentiation using Montgomery's ladder. - pub const fn pow(self, exponent: &UInt) -> ConstResidue { + pub const fn pow(self, exponent: &UInt) -> Residue { self.pow_specific(exponent, LIMBS * Word::BITS as usize) } @@ -24,7 +22,7 @@ impl, const LIMBS: usize> ConstResidue, exponent_bits: usize, - ) -> ConstResidue { + ) -> Residue { Self { montgomery_form: pow_montgomery_form( self.montgomery_form, @@ -42,8 +40,7 @@ impl, const LIMBS: usize> ConstResidue { #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct $name {} - impl ConstResidueParams<{ nlimbs!(<$uint_type>::BIT_SIZE) }> for $name + impl ResidueParams<{ nlimbs!(<$uint_type>::BIT_SIZE) }> for $name where $crate::UInt<{ nlimbs!(<$uint_type>::BIT_SIZE) }>: $crate::traits::Concat>, @@ -40,7 +40,7 @@ macro_rules! impl_modulus { /// For example, `residue!(U256::from(105u64), MyModulus);` creates a `Residue` for 105 mod `MyModulus`. macro_rules! const_residue { ($variable:ident, $modulus:ident) => { - $crate::uint::modular::constant_mod::ConstResidue::<$modulus, { $modulus::LIMBS }>::new( + $crate::uint::modular::constant_mod::Residue::<$modulus, { $modulus::LIMBS }>::new( $variable, ) }; diff --git a/src/uint/modular/constant_mod/mod.rs b/src/uint/modular/constant_mod/mod.rs index 15e0f220..877ccc3e 100644 --- a/src/uint/modular/constant_mod/mod.rs +++ b/src/uint/modular/constant_mod/mod.rs @@ -22,7 +22,7 @@ pub mod macros; /// The parameters to efficiently go to and from the Montgomery form for a given odd modulus. An easy way to generate these parameters is using the `impl_modulus!` macro. These parameters are constant, so they cannot be set at runtime. /// /// Unfortunately, `LIMBS` must be generic for now until const generics are stabilized. -pub trait ConstResidueParams: Copy { +pub trait ResidueParams: Copy { /// Number of limbs required to encode a residue const LIMBS: usize; @@ -41,15 +41,15 @@ pub trait ConstResidueParams: Copy { #[derive(Debug, Clone, Copy, PartialEq, Eq)] /// A residue mod `MOD`, represented using `LIMBS` limbs. The modulus of this residue is constant, so it cannot be set at runtime. -pub struct ConstResidue +pub struct Residue where - MOD: ConstResidueParams, + MOD: ResidueParams, { montgomery_form: UInt, phantom: PhantomData, } -impl, const LIMBS: usize> ConstResidue { +impl, const LIMBS: usize> Residue { /// The representation of 1 mod `MOD`. pub const ONE: Self = Self { montgomery_form: MOD::R, @@ -58,7 +58,7 @@ impl, const LIMBS: usize> ConstResidue) -> Self { - let mut modular_integer = ConstResidue { + let mut modular_integer = Residue { montgomery_form: integer, phantom: PhantomData, }; @@ -80,19 +80,17 @@ impl, const LIMBS: usize> ConstResidue, const LIMBS: usize> GenericResidue - for ConstResidue -{ +impl, const LIMBS: usize> GenericResidue for Residue { fn retrieve(&self) -> UInt { self.retrieve() } } -impl + Copy, const LIMBS: usize> ConditionallySelectable - for ConstResidue +impl + Copy, const LIMBS: usize> ConditionallySelectable + for Residue { fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - ConstResidue { + Residue { montgomery_form: UInt::conditional_select( &a.montgomery_form, &b.montgomery_form, diff --git a/src/uint/modular/mod.rs b/src/uint/modular/mod.rs index 6c4d82c2..36a7d714 100644 --- a/src/uint/modular/mod.rs +++ b/src/uint/modular/mod.rs @@ -25,8 +25,7 @@ mod tests { use crate::{ const_residue, impl_modulus, modular::{ - constant_mod::ConstResidue, constant_mod::ConstResidueParams, - reduction::montgomery_reduction, + constant_mod::Residue, constant_mod::ResidueParams, reduction::montgomery_reduction, }, traits::Encoding, UInt, U256, U64, @@ -144,7 +143,7 @@ mod tests { fn test_new_retrieve() { let x = U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); - let x_mod = ConstResidue::::new(x); + let x_mod = Residue::::new(x); // Confirm that when creating a Modular and retrieving the value, that it equals the original assert_eq!(x, x_mod.retrieve()); @@ -155,7 +154,7 @@ mod tests { let x = U256::from_be_hex("44acf6b7e36c1342c2c5897204fe09504e1e2efb1a900377dbc4e7a6a133ec56"); assert_eq!( - ConstResidue::::new(x), + Residue::::new(x), const_residue!(x, Modulus2) ); } From a2ee635654065a7882fc0f683604a4c45b23c6cc Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Tue, 8 Nov 2022 17:43:01 +0100 Subject: [PATCH 22/23] Impl two invs: panicking and ctoption --- src/uint/modular/constant_mod/const_inv.rs | 41 ++++++++++++++++------ src/uint/modular/inv.rs | 19 ++++++---- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/uint/modular/constant_mod/const_inv.rs b/src/uint/modular/constant_mod/const_inv.rs index 05712507..8bc94360 100644 --- a/src/uint/modular/constant_mod/const_inv.rs +++ b/src/uint/modular/constant_mod/const_inv.rs @@ -1,25 +1,46 @@ use core::marker::PhantomData; -use crate::modular::inv::{inv_montgomery_form, InvResidue}; +use subtle::{Choice, CtOption}; + +use crate::{ + modular::inv::{inv_montgomery_form, InvResidue}, + Word, +}; use super::{Residue, ResidueParams}; impl, const LIMBS: usize> InvResidue for Residue { - fn inv(self) -> Self { - self.inv() + fn inv(self) -> CtOption { + let (montgomery_form, error) = inv_montgomery_form( + self.montgomery_form, + MOD::MODULUS, + &MOD::R3, + MOD::MOD_NEG_INV, + ); + + let value = Self { + montgomery_form, + phantom: PhantomData, + }; + + CtOption::new(value, Choice::from((error == Word::MAX) as u8)) } } impl, const LIMBS: usize> Residue { - /// Computes the residue `self^-1` representing the multiplicative inverse of `self`. I.e. `self * self^-1 = 1`. + /// Computes the residue `self^-1` representing the multiplicative inverse of `self`. I.e. `self * self^-1 = 1`. Panics if `self` was not invertible. pub const fn inv(self) -> Self { + let (montgomery_form, error) = inv_montgomery_form( + self.montgomery_form, + MOD::MODULUS, + &MOD::R3, + MOD::MOD_NEG_INV, + ); + + assert!(error == Word::MAX); + Self { - montgomery_form: inv_montgomery_form( - self.montgomery_form, - MOD::MODULUS, - &MOD::R3, - MOD::MOD_NEG_INV, - ), + montgomery_form, phantom: PhantomData, } } diff --git a/src/uint/modular/inv.rs b/src/uint/modular/inv.rs index c51d6c0a..60096bd3 100644 --- a/src/uint/modular/inv.rs +++ b/src/uint/modular/inv.rs @@ -1,8 +1,13 @@ +use subtle::CtOption; + use crate::{modular::reduction::montgomery_reduction, Limb, UInt, Word}; -pub trait InvResidue { - /// Computes the (reduced) multiplicative inverse of the residue. - fn inv(self) -> Self; +pub trait InvResidue +where + Self: Sized, +{ + /// Computes the (reduced) multiplicative inverse of the residue. Returns CtOption, which is None if the residue was not invertible. + fn inv(self) -> CtOption; } pub const fn inv_montgomery_form( @@ -10,8 +15,10 @@ pub const fn inv_montgomery_form( modulus: UInt, r3: &UInt, mod_neg_inv: Limb, -) -> UInt { +) -> (UInt, Word) { let (inverse, error) = x.inv_odd_mod(modulus); - debug_assert!(error == Word::MAX); - montgomery_reduction(inverse.mul_wide(r3), modulus, mod_neg_inv) + ( + montgomery_reduction(inverse.mul_wide(r3), modulus, mod_neg_inv), + error, + ) } From dbcbc9ace17217d44ac987d9cd0c741f01ab2645 Mon Sep 17 00:00:00 2001 From: Jelle Vos Date: Fri, 11 Nov 2022 16:45:46 +0100 Subject: [PATCH 23/23] Fix docs --- src/lib.rs | 2 +- src/uint/modular/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 995011a4..855ea9b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -109,7 +109,7 @@ //! assert_eq!(b, U256::ZERO); //! ``` //! -//! It also supports modular arithmetic over constant moduli using `ConstResidue`. +//! It also supports modular arithmetic over constant moduli using `Residue`. //! That includes modular exponentiation and multiplicative inverses. //! These features are described in the [`modular`] module. //! diff --git a/src/uint/modular/mod.rs b/src/uint/modular/mod.rs index 36a7d714..23534149 100644 --- a/src/uint/modular/mod.rs +++ b/src/uint/modular/mod.rs @@ -4,7 +4,7 @@ use self::{add::AddResidue, inv::InvResidue, mul::MulResidue, pow::PowResidue}; mod reduction; -/// Implements `ConstResidue`s, supporting modular arithmetic with a constant modulus. +/// Implements `Residue`s, supporting modular arithmetic with a constant modulus. pub mod constant_mod; mod add;