1use crate::{Limb, Uint, Word};
23use super::{
4 constant_mod::{Residue, ResidueParams},
5 div_by_2::div_by_2,
6 reduction::montgomery_reduction,
7 Retrieve,
8};
910use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
1112/// Additions between residues with a modulus set at runtime
13mod runtime_add;
14/// Multiplicative inverses of residues with a modulus set at runtime
15mod runtime_inv;
16/// Multiplications between residues with a modulus set at runtime
17mod runtime_mul;
18/// Negations of residues with a modulus set at runtime
19mod runtime_neg;
20/// Exponentiation of residues with a modulus set at runtime
21mod runtime_pow;
22/// Subtractions between residues with a modulus set at runtime
23mod runtime_sub;
2425/// The parameters to efficiently go to and from the Montgomery form for an odd modulus provided at runtime.
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct DynResidueParams<const LIMBS: usize> {
28// The constant modulus
29modulus: Uint<LIMBS>,
30// Parameter used in Montgomery reduction
31r: Uint<LIMBS>,
32// R^2, used to move into Montgomery form
33r2: Uint<LIMBS>,
34// R^3, used to compute the multiplicative inverse
35r3: Uint<LIMBS>,
36// The lowest limbs of -(MODULUS^-1) mod R
37 // We only need the LSB because during reduction this value is multiplied modulo 2**Limb::BITS.
38mod_neg_inv: Limb,
39}
4041impl<const LIMBS: usize> DynResidueParams<LIMBS> {
42// Internal helper function to generate parameters; this lets us wrap the constructors more cleanly
43const fn generate_params(modulus: &Uint<LIMBS>) -> Self {
44let r = Uint::MAX.const_rem(modulus).0.wrapping_add(&Uint::ONE);
45let r2 = Uint::const_rem_wide(r.square_wide(), modulus).0;
4647// Since we are calculating the inverse modulo (Word::MAX+1),
48 // we can take the modulo right away and calculate the inverse of the first limb only.
49let modulus_lo = Uint::<1>::from_words([modulus.limbs[0].0]);
50let mod_neg_inv = Limb(
51 Word::MIN.wrapping_sub(modulus_lo.inv_mod2k_vartime(Word::BITS as usize).limbs[0].0),
52 );
5354let r3 = montgomery_reduction(&r2.square_wide(), modulus, mod_neg_inv);
5556Self {
57 modulus: *modulus,
58 r,
59 r2,
60 r3,
61 mod_neg_inv,
62 }
63 }
6465/// Instantiates a new set of `ResidueParams` representing the given `modulus`, which _must_ be odd.
66 /// If `modulus` is not odd, this function will panic; use [`new_checked`][`DynResidueParams::new_checked`] if you want to be able to detect an invalid modulus.
67pub const fn new(modulus: &Uint<LIMBS>) -> Self {
68// A valid modulus must be odd
69if modulus.ct_is_odd().to_u8() == 0 {
70panic!("modulus must be odd");
71 }
7273Self::generate_params(modulus)
74 }
7576/// Instantiates a new set of `ResidueParams` representing the given `modulus` if it is odd.
77 /// Returns a `CtOption` that is `None` if the provided modulus is not odd; this is a safer version of [`new`][`DynResidueParams::new`], which can panic.
78#[deprecated(
79 since = "0.5.3",
80 note = "This functionality will be moved to `new` in a future release."
81)]
82pub fn new_checked(modulus: &Uint<LIMBS>) -> CtOption<Self> {
83// A valid modulus must be odd.
84CtOption::new(Self::generate_params(modulus), modulus.ct_is_odd().into())
85 }
8687/// Returns the modulus which was used to initialize these parameters.
88pub const fn modulus(&self) -> &Uint<LIMBS> {
89&self.modulus
90 }
9192/// Create `DynResidueParams` corresponding to a `ResidueParams`.
93pub const fn from_residue_params<P>() -> Self
94where
95P: ResidueParams<LIMBS>,
96 {
97Self {
98 modulus: P::MODULUS,
99 r: P::R,
100 r2: P::R2,
101 r3: P::R3,
102 mod_neg_inv: P::MOD_NEG_INV,
103 }
104 }
105}
106107impl<const LIMBS: usize> ConditionallySelectable for DynResidueParams<LIMBS> {
108fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
109Self {
110 modulus: Uint::conditional_select(&a.modulus, &b.modulus, choice),
111 r: Uint::conditional_select(&a.r, &b.r, choice),
112 r2: Uint::conditional_select(&a.r2, &b.r2, choice),
113 r3: Uint::conditional_select(&a.r3, &b.r3, choice),
114 mod_neg_inv: Limb::conditional_select(&a.mod_neg_inv, &b.mod_neg_inv, choice),
115 }
116 }
117}
118119impl<const LIMBS: usize> ConstantTimeEq for DynResidueParams<LIMBS> {
120fn ct_eq(&self, other: &Self) -> Choice {
121self.modulus.ct_eq(&other.modulus)
122 & self.r.ct_eq(&other.r)
123 & self.r2.ct_eq(&other.r2)
124 & self.r3.ct_eq(&other.r3)
125 & self.mod_neg_inv.ct_eq(&other.mod_neg_inv)
126 }
127}
128129/// A residue represented using `LIMBS` limbs. The odd modulus of this residue is set at runtime.
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
131pub struct DynResidue<const LIMBS: usize> {
132 montgomery_form: Uint<LIMBS>,
133 residue_params: DynResidueParams<LIMBS>,
134}
135136impl<const LIMBS: usize> DynResidue<LIMBS> {
137/// Instantiates a new `Residue` that represents this `integer` mod `MOD`.
138pub const fn new(integer: &Uint<LIMBS>, residue_params: DynResidueParams<LIMBS>) -> Self {
139let product = integer.mul_wide(&residue_params.r2);
140let montgomery_form = montgomery_reduction(
141&product,
142&residue_params.modulus,
143 residue_params.mod_neg_inv,
144 );
145146Self {
147 montgomery_form,
148 residue_params,
149 }
150 }
151152/// Retrieves the integer currently encoded in this `Residue`, guaranteed to be reduced.
153pub const fn retrieve(&self) -> Uint<LIMBS> {
154 montgomery_reduction(
155&(self.montgomery_form, Uint::ZERO),
156&self.residue_params.modulus,
157self.residue_params.mod_neg_inv,
158 )
159 }
160161/// Instantiates a new `Residue` that represents zero.
162pub const fn zero(residue_params: DynResidueParams<LIMBS>) -> Self {
163Self {
164 montgomery_form: Uint::<LIMBS>::ZERO,
165 residue_params,
166 }
167 }
168169/// Instantiates a new `Residue` that represents 1.
170pub const fn one(residue_params: DynResidueParams<LIMBS>) -> Self {
171Self {
172 montgomery_form: residue_params.r,
173 residue_params,
174 }
175 }
176177/// Returns the parameter struct used to initialize this residue.
178pub const fn params(&self) -> &DynResidueParams<LIMBS> {
179&self.residue_params
180 }
181182/// Access the `DynResidue` value in Montgomery form.
183pub const fn as_montgomery(&self) -> &Uint<LIMBS> {
184&self.montgomery_form
185 }
186187/// Mutably access the `DynResidue` value in Montgomery form.
188pub fn as_montgomery_mut(&mut self) -> &mut Uint<LIMBS> {
189&mut self.montgomery_form
190 }
191192/// Create a `DynResidue` from a value in Montgomery form.
193pub const fn from_montgomery(
194 integer: Uint<LIMBS>,
195 residue_params: DynResidueParams<LIMBS>,
196 ) -> Self {
197Self {
198 montgomery_form: integer,
199 residue_params,
200 }
201 }
202203/// Extract the value from the `DynResidue` in Montgomery form.
204pub const fn to_montgomery(&self) -> Uint<LIMBS> {
205self.montgomery_form
206 }
207208/// Performs the modular division by 2, that is for given `x` returns `y`
209 /// such that `y * 2 = x mod p`. This means:
210 /// - if `x` is even, returns `x / 2`,
211 /// - if `x` is odd, returns `(x + p) / 2`
212 /// (since the modulus `p` in Montgomery form is always odd, this divides entirely).
213pub fn div_by_2(&self) -> Self {
214Self {
215 montgomery_form: div_by_2(&self.montgomery_form, &self.residue_params.modulus),
216 residue_params: self.residue_params,
217 }
218 }
219}
220221impl<const LIMBS: usize> Retrieve for DynResidue<LIMBS> {
222type Output = Uint<LIMBS>;
223fn retrieve(&self) -> Self::Output {
224self.retrieve()
225 }
226}
227228impl<const LIMBS: usize, P: ResidueParams<LIMBS>> From<&Residue<P, LIMBS>> for DynResidue<LIMBS> {
229fn from(residue: &Residue<P, LIMBS>) -> Self {
230Self {
231 montgomery_form: residue.to_montgomery(),
232 residue_params: DynResidueParams::from_residue_params::<P>(),
233 }
234 }
235}
236237impl<const LIMBS: usize> ConditionallySelectable for DynResidue<LIMBS> {
238fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
239Self {
240 montgomery_form: Uint::conditional_select(
241&a.montgomery_form,
242&b.montgomery_form,
243 choice,
244 ),
245 residue_params: DynResidueParams::conditional_select(
246&a.residue_params,
247&b.residue_params,
248 choice,
249 ),
250 }
251 }
252}
253254impl<const LIMBS: usize> ConstantTimeEq for DynResidue<LIMBS> {
255fn ct_eq(&self, other: &Self) -> Choice {
256self.montgomery_form.ct_eq(&other.montgomery_form)
257 & self.residue_params.ct_eq(&other.residue_params)
258 }
259}
260261/// NOTE: this does _not_ zeroize the parameters, in order to maintain some form of type consistency
262#[cfg(feature = "zeroize")]
263impl<const LIMBS: usize> zeroize::Zeroize for DynResidue<LIMBS> {
264fn zeroize(&mut self) {
265self.montgomery_form.zeroize()
266 }
267}
268269#[cfg(test)]
270mod test {
271use super::*;
272273const LIMBS: usize = nlimbs!(64);
274275#[test]
276 #[allow(deprecated)]
277// Test that a valid modulus yields `DynResidueParams`
278fn test_valid_modulus() {
279let valid_modulus = Uint::<LIMBS>::from(3u8);
280281 DynResidueParams::<LIMBS>::new_checked(&valid_modulus).unwrap();
282 DynResidueParams::<LIMBS>::new(&valid_modulus);
283 }
284285#[test]
286 #[allow(deprecated)]
287// Test that an invalid checked modulus does not yield `DynResidueParams`
288fn test_invalid_checked_modulus() {
289assert!(bool::from(
290 DynResidueParams::<LIMBS>::new_checked(&Uint::from(2u8)).is_none()
291 ))
292 }
293294#[test]
295 #[should_panic]
296// Tets that an invalid modulus panics
297fn test_invalid_modulus() {
298 DynResidueParams::<LIMBS>::new(&Uint::from(2u8));
299 }
300}