crypto_bigint/uint/modular/
runtime_mod.rs1use crate::{Limb, Uint, Word};
2
3use super::{
4    constant_mod::{Residue, ResidueParams},
5    div_by_2::div_by_2,
6    reduction::montgomery_reduction,
7    Retrieve,
8};
9
10use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
11
12mod runtime_add;
14mod runtime_inv;
16mod runtime_mul;
18mod runtime_neg;
20mod runtime_pow;
22mod runtime_sub;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct DynResidueParams<const LIMBS: usize> {
28    modulus: Uint<LIMBS>,
30    r: Uint<LIMBS>,
32    r2: Uint<LIMBS>,
34    r3: Uint<LIMBS>,
36    mod_neg_inv: Limb,
39}
40
41impl<const LIMBS: usize> DynResidueParams<LIMBS> {
42    const fn generate_params(modulus: &Uint<LIMBS>) -> Self {
44        let r = Uint::MAX.const_rem(modulus).0.wrapping_add(&Uint::ONE);
45        let r2 = Uint::const_rem_wide(r.square_wide(), modulus).0;
46
47        let modulus_lo = Uint::<1>::from_words([modulus.limbs[0].0]);
50        let mod_neg_inv = Limb(
51            Word::MIN.wrapping_sub(modulus_lo.inv_mod2k_vartime(Word::BITS as usize).limbs[0].0),
52        );
53
54        let r3 = montgomery_reduction(&r2.square_wide(), modulus, mod_neg_inv);
55
56        Self {
57            modulus: *modulus,
58            r,
59            r2,
60            r3,
61            mod_neg_inv,
62        }
63    }
64
65    pub const fn new(modulus: &Uint<LIMBS>) -> Self {
68        if modulus.ct_is_odd().to_u8() == 0 {
70            panic!("modulus must be odd");
71        }
72
73        Self::generate_params(modulus)
74    }
75
76    #[deprecated(
79        since = "0.5.3",
80        note = "This functionality will be moved to `new` in a future release."
81    )]
82    pub fn new_checked(modulus: &Uint<LIMBS>) -> CtOption<Self> {
83        CtOption::new(Self::generate_params(modulus), modulus.ct_is_odd().into())
85    }
86
87    pub const fn modulus(&self) -> &Uint<LIMBS> {
89        &self.modulus
90    }
91
92    pub const fn from_residue_params<P>() -> Self
94    where
95        P: ResidueParams<LIMBS>,
96    {
97        Self {
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}
106
107impl<const LIMBS: usize> ConditionallySelectable for DynResidueParams<LIMBS> {
108    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
109        Self {
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}
118
119impl<const LIMBS: usize> ConstantTimeEq for DynResidueParams<LIMBS> {
120    fn ct_eq(&self, other: &Self) -> Choice {
121        self.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}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq)]
131pub struct DynResidue<const LIMBS: usize> {
132    montgomery_form: Uint<LIMBS>,
133    residue_params: DynResidueParams<LIMBS>,
134}
135
136impl<const LIMBS: usize> DynResidue<LIMBS> {
137    pub const fn new(integer: &Uint<LIMBS>, residue_params: DynResidueParams<LIMBS>) -> Self {
139        let product = integer.mul_wide(&residue_params.r2);
140        let montgomery_form = montgomery_reduction(
141            &product,
142            &residue_params.modulus,
143            residue_params.mod_neg_inv,
144        );
145
146        Self {
147            montgomery_form,
148            residue_params,
149        }
150    }
151
152    pub const fn retrieve(&self) -> Uint<LIMBS> {
154        montgomery_reduction(
155            &(self.montgomery_form, Uint::ZERO),
156            &self.residue_params.modulus,
157            self.residue_params.mod_neg_inv,
158        )
159    }
160
161    pub const fn zero(residue_params: DynResidueParams<LIMBS>) -> Self {
163        Self {
164            montgomery_form: Uint::<LIMBS>::ZERO,
165            residue_params,
166        }
167    }
168
169    pub const fn one(residue_params: DynResidueParams<LIMBS>) -> Self {
171        Self {
172            montgomery_form: residue_params.r,
173            residue_params,
174        }
175    }
176
177    pub const fn params(&self) -> &DynResidueParams<LIMBS> {
179        &self.residue_params
180    }
181
182    pub const fn as_montgomery(&self) -> &Uint<LIMBS> {
184        &self.montgomery_form
185    }
186
187    pub fn as_montgomery_mut(&mut self) -> &mut Uint<LIMBS> {
189        &mut self.montgomery_form
190    }
191
192    pub const fn from_montgomery(
194        integer: Uint<LIMBS>,
195        residue_params: DynResidueParams<LIMBS>,
196    ) -> Self {
197        Self {
198            montgomery_form: integer,
199            residue_params,
200        }
201    }
202
203    pub const fn to_montgomery(&self) -> Uint<LIMBS> {
205        self.montgomery_form
206    }
207
208    pub fn div_by_2(&self) -> Self {
214        Self {
215            montgomery_form: div_by_2(&self.montgomery_form, &self.residue_params.modulus),
216            residue_params: self.residue_params,
217        }
218    }
219}
220
221impl<const LIMBS: usize> Retrieve for DynResidue<LIMBS> {
222    type Output = Uint<LIMBS>;
223    fn retrieve(&self) -> Self::Output {
224        self.retrieve()
225    }
226}
227
228impl<const LIMBS: usize, P: ResidueParams<LIMBS>> From<&Residue<P, LIMBS>> for DynResidue<LIMBS> {
229    fn from(residue: &Residue<P, LIMBS>) -> Self {
230        Self {
231            montgomery_form: residue.to_montgomery(),
232            residue_params: DynResidueParams::from_residue_params::<P>(),
233        }
234    }
235}
236
237impl<const LIMBS: usize> ConditionallySelectable for DynResidue<LIMBS> {
238    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
239        Self {
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}
253
254impl<const LIMBS: usize> ConstantTimeEq for DynResidue<LIMBS> {
255    fn ct_eq(&self, other: &Self) -> Choice {
256        self.montgomery_form.ct_eq(&other.montgomery_form)
257            & self.residue_params.ct_eq(&other.residue_params)
258    }
259}
260
261#[cfg(feature = "zeroize")]
263impl<const LIMBS: usize> zeroize::Zeroize for DynResidue<LIMBS> {
264    fn zeroize(&mut self) {
265        self.montgomery_form.zeroize()
266    }
267}
268
269#[cfg(test)]
270mod test {
271    use super::*;
272
273    const LIMBS: usize = nlimbs!(64);
274
275    #[test]
276    #[allow(deprecated)]
277    fn test_valid_modulus() {
279        let valid_modulus = Uint::<LIMBS>::from(3u8);
280
281        DynResidueParams::<LIMBS>::new_checked(&valid_modulus).unwrap();
282        DynResidueParams::<LIMBS>::new(&valid_modulus);
283    }
284
285    #[test]
286    #[allow(deprecated)]
287    fn test_invalid_checked_modulus() {
289        assert!(bool::from(
290            DynResidueParams::<LIMBS>::new_checked(&Uint::from(2u8)).is_none()
291        ))
292    }
293
294    #[test]
295    #[should_panic]
296    fn test_invalid_modulus() {
298        DynResidueParams::<LIMBS>::new(&Uint::from(2u8));
299    }
300}