1use core::{fmt::Debug, marker::PhantomData};
23use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
45use crate::{Limb, Uint, Zero};
67use super::{div_by_2::div_by_2, reduction::montgomery_reduction, Retrieve};
89#[cfg(feature = "rand_core")]
10use crate::{rand_core::CryptoRngCore, NonZero, Random, RandomMod};
1112#[cfg(feature = "serde")]
13use {
14crate::Encoding,
15 serdect::serde::de::Error,
16 serdect::serde::{Deserialize, Deserializer, Serialize, Serializer},
17};
1819/// Additions between residues with a constant modulus
20mod const_add;
21/// Multiplicative inverses of residues with a constant modulus
22mod const_inv;
23/// Multiplications between residues with a constant modulus
24mod const_mul;
25/// Negations of residues with a constant modulus
26mod const_neg;
27/// Exponentiation of residues with a constant modulus
28mod const_pow;
29/// Subtractions between residues with a constant modulus
30mod const_sub;
3132/// Macros to remove the boilerplate code when dealing with constant moduli.
33#[macro_use]
34mod macros;
3536pub use macros::*;
3738/// 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.
39///
40/// Unfortunately, `LIMBS` must be generic for now until const generics are stabilized.
41pub trait ResidueParams<const LIMBS: usize>:
42 Copy + Debug + Default + Eq + Send + Sync + 'static
43{
44/// Number of limbs required to encode a residue
45const LIMBS: usize;
4647/// The constant modulus
48const MODULUS: Uint<LIMBS>;
49/// Parameter used in Montgomery reduction
50const R: Uint<LIMBS>;
51/// R^2, used to move into Montgomery form
52const R2: Uint<LIMBS>;
53/// R^3, used to perform a multiplicative inverse
54const R3: Uint<LIMBS>;
55/// The lowest limbs of -(MODULUS^-1) mod R
56// We only need the LSB because during reduction this value is multiplied modulo 2**Limb::BITS.
57const MOD_NEG_INV: Limb;
58}
5960#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61/// A residue mod `MOD`, represented using `LIMBS` limbs. The modulus of this residue is constant, so it cannot be set at runtime.
62/// Internally, the value is stored in Montgomery form (multiplied by MOD::R) until it is retrieved.
63pub struct Residue<MOD, const LIMBS: usize>
64where
65MOD: ResidueParams<LIMBS>,
66{
67 montgomery_form: Uint<LIMBS>,
68 phantom: PhantomData<MOD>,
69}
7071#[cfg(feature = "zeroize")]
72impl<MOD: ResidueParams<LIMBS>, const LIMBS: usize> zeroize::DefaultIsZeroes
73for Residue<MOD, LIMBS>
74{
75}
7677impl<MOD: ResidueParams<LIMBS>, const LIMBS: usize> Residue<MOD, LIMBS> {
78/// The representation of 0 mod `MOD`.
79pub const ZERO: Self = Self {
80 montgomery_form: Uint::<LIMBS>::ZERO,
81 phantom: PhantomData,
82 };
8384/// The representation of 1 mod `MOD`.
85pub const ONE: Self = Self {
86 montgomery_form: MOD::R,
87 phantom: PhantomData,
88 };
8990// Internal helper function to generate a residue; this lets us wrap the constructors more cleanly
91const fn generate_residue(integer: &Uint<LIMBS>) -> Self {
92let product = integer.mul_wide(&MOD::R2);
93let montgomery_form =
94 montgomery_reduction::<LIMBS>(&product, &MOD::MODULUS, MOD::MOD_NEG_INV);
9596Self {
97 montgomery_form,
98 phantom: PhantomData,
99 }
100 }
101102/// Instantiates a new `Residue` that represents this `integer` mod `MOD`.
103 /// If the modulus represented by `MOD` is not odd, this function will panic; use [`new_checked`][`Residue::new_checked`] if you want to be able to detect an invalid modulus.
104pub const fn new(integer: &Uint<LIMBS>) -> Self {
105// A valid modulus must be odd
106if MOD::MODULUS.ct_is_odd().to_u8() == 0 {
107panic!("modulus must be odd");
108 }
109110Self::generate_residue(integer)
111 }
112113/// Instantiates a new `Residue` that represents this `integer` mod `MOD` if the modulus is odd.
114 /// Returns a `CtOption` that is `None` if the provided modulus is not odd; this is a safer version of [`new`][`Residue::new`], which can panic.
115// TODO: remove this method when we can use `generic_const_exprs.` to ensure the modulus is
116 // always valid.
117pub fn new_checked(integer: &Uint<LIMBS>) -> CtOption<Self> {
118// A valid modulus must be odd.
119CtOption::new(
120Self::generate_residue(integer),
121 MOD::MODULUS.ct_is_odd().into(),
122 )
123 }
124125/// Retrieves the integer currently encoded in this `Residue`, guaranteed to be reduced.
126pub 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 }
133134/// Access the `Residue` value in Montgomery form.
135pub const fn as_montgomery(&self) -> &Uint<LIMBS> {
136&self.montgomery_form
137 }
138139/// Mutably access the `Residue` value in Montgomery form.
140pub fn as_montgomery_mut(&mut self) -> &mut Uint<LIMBS> {
141&mut self.montgomery_form
142 }
143144/// Create a `Residue` from a value in Montgomery form.
145pub const fn from_montgomery(integer: Uint<LIMBS>) -> Self {
146Self {
147 montgomery_form: integer,
148 phantom: PhantomData,
149 }
150 }
151152/// Extract the value from the `Residue` in Montgomery form.
153pub const fn to_montgomery(&self) -> Uint<LIMBS> {
154self.montgomery_form
155 }
156157/// Performs the modular division by 2, that is for given `x` returns `y`
158 /// such that `y * 2 = x mod p`. This means:
159 /// - if `x` is even, returns `x / 2`,
160 /// - if `x` is odd, returns `(x + p) / 2`
161 /// (since the modulus `p` in Montgomery form is always odd, this divides entirely).
162pub fn div_by_2(&self) -> Self {
163Self {
164 montgomery_form: div_by_2(&self.montgomery_form, &MOD::MODULUS),
165 phantom: PhantomData,
166 }
167 }
168}
169170impl<MOD: ResidueParams<LIMBS> + Copy, const LIMBS: usize> ConditionallySelectable
171for Residue<MOD, LIMBS>
172{
173fn 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}
184185impl<MOD: ResidueParams<LIMBS>, const LIMBS: usize> ConstantTimeEq for Residue<MOD, LIMBS> {
186fn ct_eq(&self, other: &Self) -> Choice {
187 ConstantTimeEq::ct_eq(&self.montgomery_form, &other.montgomery_form)
188 }
189}
190191impl<MOD: ResidueParams<LIMBS>, const LIMBS: usize> Default for Residue<MOD, LIMBS> {
192fn default() -> Self {
193Self::ZERO
194 }
195}
196197impl<MOD: ResidueParams<LIMBS>, const LIMBS: usize> Zero for Residue<MOD, LIMBS> {
198const ZERO: Self = Self::ZERO;
199}
200201#[cfg(feature = "rand_core")]
202impl<MOD, const LIMBS: usize> Random for Residue<MOD, LIMBS>
203where
204MOD: ResidueParams<LIMBS>,
205{
206#[inline]
207fn random(rng: &mut impl CryptoRngCore) -> Self {
208Self::new(&Uint::random_mod(rng, &NonZero::from_uint(MOD::MODULUS)))
209 }
210}
211212impl<MOD: ResidueParams<LIMBS>, const LIMBS: usize> Retrieve for Residue<MOD, LIMBS> {
213type Output = Uint<LIMBS>;
214fn retrieve(&self) -> Self::Output {
215self.retrieve()
216 }
217}
218219#[cfg(feature = "serde")]
220impl<'de, MOD, const LIMBS: usize> Deserialize<'de> for Residue<MOD, LIMBS>
221where
222MOD: ResidueParams<LIMBS>,
223 Uint<LIMBS>: Encoding,
224{
225fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
226where
227D: Deserializer<'de>,
228 {
229 Uint::<LIMBS>::deserialize(deserializer).and_then(|montgomery_form| {
230if Uint::ct_lt(&montgomery_form, &MOD::MODULUS).into() {
231Ok(Self {
232 montgomery_form,
233 phantom: PhantomData,
234 })
235 } else {
236Err(D::Error::custom("montgomery form must be reduced"))
237 }
238 })
239 }
240}
241242#[cfg(feature = "serde")]
243impl<MOD, const LIMBS: usize> Serialize for Residue<MOD, LIMBS>
244where
245MOD: ResidueParams<LIMBS>,
246 Uint<LIMBS>: Encoding,
247{
248fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
249where
250S: Serializer,
251 {
252self.montgomery_form.serialize(serializer)
253 }
254}