crypto_bigint/uint/modular/
constant_mod.rs1use core::{fmt::Debug, marker::PhantomData};
2
3use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
4
5use crate::{Limb, Uint, Zero};
6
7use super::{div_by_2::div_by_2, reduction::montgomery_reduction, Retrieve};
8
9#[cfg(feature = "rand_core")]
10use crate::{rand_core::CryptoRngCore, NonZero, Random, RandomMod};
11
12#[cfg(feature = "serde")]
13use {
14 crate::Encoding,
15 serdect::serde::de::Error,
16 serdect::serde::{Deserialize, Deserializer, Serialize, Serializer},
17};
18
19mod const_add;
21mod const_inv;
23mod const_mul;
25mod const_neg;
27mod const_pow;
29mod const_sub;
31
32#[macro_use]
34mod macros;
35
36pub use macros::*;
37
38pub trait ResidueParams<const LIMBS: usize>:
42 Copy + Debug + Default + Eq + Send + Sync + 'static
43{
44 const LIMBS: usize;
46
47 const MODULUS: Uint<LIMBS>;
49 const R: Uint<LIMBS>;
51 const R2: Uint<LIMBS>;
53 const R3: Uint<LIMBS>;
55 const MOD_NEG_INV: Limb;
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub struct Residue<MOD, const LIMBS: usize>
64where
65 MOD: ResidueParams<LIMBS>,
66{
67 montgomery_form: Uint<LIMBS>,
68 phantom: PhantomData<MOD>,
69}
70
71#[cfg(feature = "zeroize")]
72impl<MOD: ResidueParams<LIMBS>, const LIMBS: usize> zeroize::DefaultIsZeroes
73 for Residue<MOD, LIMBS>
74{
75}
76
77impl<MOD: ResidueParams<LIMBS>, const LIMBS: usize> Residue<MOD, LIMBS> {
78 pub const ZERO: Self = Self {
80 montgomery_form: Uint::<LIMBS>::ZERO,
81 phantom: PhantomData,
82 };
83
84 pub const ONE: Self = Self {
86 montgomery_form: MOD::R,
87 phantom: PhantomData,
88 };
89
90 const fn generate_residue(integer: &Uint<LIMBS>) -> Self {
92 let product = integer.mul_wide(&MOD::R2);
93 let montgomery_form =
94 montgomery_reduction::<LIMBS>(&product, &MOD::MODULUS, MOD::MOD_NEG_INV);
95
96 Self {
97 montgomery_form,
98 phantom: PhantomData,
99 }
100 }
101
102 pub const fn new(integer: &Uint<LIMBS>) -> Self {
105 if MOD::MODULUS.ct_is_odd().to_u8() == 0 {
107 panic!("modulus must be odd");
108 }
109
110 Self::generate_residue(integer)
111 }
112
113 pub fn new_checked(integer: &Uint<LIMBS>) -> CtOption<Self> {
118 CtOption::new(
120 Self::generate_residue(integer),
121 MOD::MODULUS.ct_is_odd().into(),
122 )
123 }
124
125 pub const fn retrieve(&self) -> Uint<LIMBS> {
127 montgomery_reduction::<LIMBS>(
128 &(self.montgomery_form, Uint::ZERO),
129 &MOD::MODULUS,
130 MOD::MOD_NEG_INV,
131 )
132 }
133
134 pub const fn as_montgomery(&self) -> &Uint<LIMBS> {
136 &self.montgomery_form
137 }
138
139 pub fn as_montgomery_mut(&mut self) -> &mut Uint<LIMBS> {
141 &mut self.montgomery_form
142 }
143
144 pub const fn from_montgomery(integer: Uint<LIMBS>) -> Self {
146 Self {
147 montgomery_form: integer,
148 phantom: PhantomData,
149 }
150 }
151
152 pub const fn to_montgomery(&self) -> Uint<LIMBS> {
154 self.montgomery_form
155 }
156
157 pub fn div_by_2(&self) -> Self {
163 Self {
164 montgomery_form: div_by_2(&self.montgomery_form, &MOD::MODULUS),
165 phantom: PhantomData,
166 }
167 }
168}
169
170impl<MOD: ResidueParams<LIMBS> + Copy, const LIMBS: usize> ConditionallySelectable
171 for Residue<MOD, LIMBS>
172{
173 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
174 Residue {
175 montgomery_form: Uint::conditional_select(
176 &a.montgomery_form,
177 &b.montgomery_form,
178 choice,
179 ),
180 phantom: PhantomData,
181 }
182 }
183}
184
185impl<MOD: ResidueParams<LIMBS>, const LIMBS: usize> ConstantTimeEq for Residue<MOD, LIMBS> {
186 fn ct_eq(&self, other: &Self) -> Choice {
187 ConstantTimeEq::ct_eq(&self.montgomery_form, &other.montgomery_form)
188 }
189}
190
191impl<MOD: ResidueParams<LIMBS>, const LIMBS: usize> Default for Residue<MOD, LIMBS> {
192 fn default() -> Self {
193 Self::ZERO
194 }
195}
196
197impl<MOD: ResidueParams<LIMBS>, const LIMBS: usize> Zero for Residue<MOD, LIMBS> {
198 const ZERO: Self = Self::ZERO;
199}
200
201#[cfg(feature = "rand_core")]
202impl<MOD, const LIMBS: usize> Random for Residue<MOD, LIMBS>
203where
204 MOD: ResidueParams<LIMBS>,
205{
206 #[inline]
207 fn random(rng: &mut impl CryptoRngCore) -> Self {
208 Self::new(&Uint::random_mod(rng, &NonZero::from_uint(MOD::MODULUS)))
209 }
210}
211
212impl<MOD: ResidueParams<LIMBS>, const LIMBS: usize> Retrieve for Residue<MOD, LIMBS> {
213 type Output = Uint<LIMBS>;
214 fn retrieve(&self) -> Self::Output {
215 self.retrieve()
216 }
217}
218
219#[cfg(feature = "serde")]
220impl<'de, MOD, const LIMBS: usize> Deserialize<'de> for Residue<MOD, LIMBS>
221where
222 MOD: ResidueParams<LIMBS>,
223 Uint<LIMBS>: Encoding,
224{
225 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
226 where
227 D: Deserializer<'de>,
228 {
229 Uint::<LIMBS>::deserialize(deserializer).and_then(|montgomery_form| {
230 if Uint::ct_lt(&montgomery_form, &MOD::MODULUS).into() {
231 Ok(Self {
232 montgomery_form,
233 phantom: PhantomData,
234 })
235 } else {
236 Err(D::Error::custom("montgomery form must be reduced"))
237 }
238 })
239 }
240}
241
242#[cfg(feature = "serde")]
243impl<MOD, const LIMBS: usize> Serialize for Residue<MOD, LIMBS>
244where
245 MOD: ResidueParams<LIMBS>,
246 Uint<LIMBS>: Encoding,
247{
248 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
249 where
250 S: Serializer,
251 {
252 self.montgomery_form.serialize(serializer)
253 }
254}