1#![allow(missing_docs)]
3
4use core::borrow::Borrow;
5
6#[cfg(feature = "alloc")]
7use alloc::boxed::Box;
8use const_num_traits::PrimBits;
9#[cfg(not(feature = "modmath"))]
10use const_num_traits::PrimInt;
11use const_num_traits::{FromBytes as NumFromBytes, ToBytes as NumToBytes, Zero};
12#[cfg(feature = "alloc")]
13use crypto_bigint::{
14 modular::{BoxedMontyForm, BoxedMontyParams},
15 BoxedUint, Resize as CryptoResize,
16};
17#[cfg(feature = "alloc")]
18use crypto_bigint::{NonZero as CryptoNonZero, Odd as CryptoOdd};
19use zeroize::Zeroize;
20
21use crate::errors::{Error, Result};
22
23pub trait NumBytes: Borrow<[u8]> + AsRef<[u8]> {}
24
25impl<T> NumBytes for T where T: Borrow<[u8]> + AsRef<[u8]> {}
26
27#[repr(transparent)]
28#[derive(Clone, Debug, Eq, PartialEq)]
29pub struct NonZero<T>(T);
30
31#[repr(transparent)]
32#[derive(Clone, Debug, Eq, PartialEq)]
33pub struct Odd<T>(T);
34
35pub trait IntegerResize: Sized {
36 type Output;
37
38 fn resize_unchecked(self, at_least_bits_precision: u32) -> Self::Output;
39 fn try_resize(self, at_least_bits_precision: u32) -> Option<Self::Output>;
40}
41
42pub trait FixedWidthUnsignedInt: Zeroize + Clone + Copy {
43 type Bytes: NumBytes + Default + AsMut<[u8]>;
44
45 fn leading_zeros(&self) -> u32;
46 fn to_be_bytes(&self) -> Self::Bytes;
47 fn try_from_be_bytes_vartime(bytes: &[u8]) -> Result<Self>;
48 fn bits_precision(&self) -> u32;
49}
50
51#[cfg(feature = "modmath")]
52impl<T> FixedWidthUnsignedInt for T
53where
54 T: Zeroize + Clone + Copy + PrimBits + Zero + NumToBytes + NumFromBytes,
55 T: NumToBytes<Bytes = <T as NumFromBytes>::Bytes>,
56 <T as NumToBytes>::Bytes: NumBytes + Default + AsMut<[u8]>,
57{
58 type Bytes = <T as NumToBytes>::Bytes;
59
60 fn leading_zeros(&self) -> u32 {
61 PrimBits::leading_zeros(*self)
62 }
63
64 fn to_be_bytes(&self) -> Self::Bytes {
65 NumToBytes::to_be_bytes(*self)
66 }
67
68 fn try_from_be_bytes_vartime(bytes: &[u8]) -> Result<Self> {
69 let mut repr = <T as NumFromBytes>::Bytes::default();
70 let out = repr.as_mut();
71 let out_len = out.len();
72 if bytes.len() > out_len {
73 return Err(Error::InvalidArguments);
74 }
75 out[out_len - bytes.len()..].copy_from_slice(bytes);
76 Ok(NumFromBytes::from_be_bytes(&repr))
77 }
78
79 fn bits_precision(&self) -> u32 {
80 PrimBits::count_zeros(<T as Zero>::zero())
81 }
82}
83
84#[cfg(not(feature = "modmath"))]
85impl<T> FixedWidthUnsignedInt for T
86where
87 T: Zeroize + Clone + Copy + PrimInt + NumToBytes + NumFromBytes,
88 T: NumToBytes<Bytes = <T as NumFromBytes>::Bytes>,
89 <T as NumToBytes>::Bytes: NumBytes + Default + AsMut<[u8]>,
90{
91 type Bytes = <T as NumToBytes>::Bytes;
92
93 fn leading_zeros(&self) -> u32 {
94 PrimBits::leading_zeros(*self)
95 }
96
97 fn to_be_bytes(&self) -> Self::Bytes {
98 NumToBytes::to_be_bytes(*self)
99 }
100
101 fn try_from_be_bytes_vartime(bytes: &[u8]) -> Result<Self> {
102 let mut repr = <T as NumFromBytes>::Bytes::default();
103 let out = repr.as_mut();
104 let out_len = out.len();
105 if bytes.len() > out_len {
106 return Err(Error::InvalidArguments);
107 }
108 out[out_len - bytes.len()..].copy_from_slice(bytes);
109 Ok(NumFromBytes::from_be_bytes(&repr))
110 }
111
112 fn bits_precision(&self) -> u32 {
113 <T as Zero>::zero().count_zeros()
114 }
115}
116
117#[cfg(not(feature = "alloc"))]
118impl<T> IntegerResize for T
119where
120 T: FixedWidthUnsignedInt,
121{
122 type Output = Self;
123
124 fn resize_unchecked(self, _at_least_bits_precision: u32) -> Self::Output {
125 self
126 }
127
128 fn try_resize(self, at_least_bits_precision: u32) -> Option<Self::Output> {
129 let value_bits = self.bits_precision() - self.leading_zeros();
135 if value_bits <= at_least_bits_precision {
136 Some(self)
137 } else {
138 None
139 }
140 }
141}
142
143#[cfg(not(feature = "alloc"))]
144impl<T> UnsignedModularInt for T
145where
146 T: FixedWidthUnsignedInt + PartialOrd,
147{
148 type Bytes = <T as FixedWidthUnsignedInt>::Bytes;
149
150 fn leading_zeros(&self) -> u32 {
151 FixedWidthUnsignedInt::leading_zeros(self)
152 }
153
154 fn to_be_bytes(&self) -> Self::Bytes {
155 FixedWidthUnsignedInt::to_be_bytes(self)
156 }
157
158 fn as_nz_ref(&self) -> NonZero<Self> {
159 NonZero::new(*self).expect("value is non-zero")
160 }
161
162 fn bits(&self) -> u32 {
163 self.bits_precision() - self.leading_zeros()
164 }
165
166 fn bits_precision(&self) -> u32 {
167 FixedWidthUnsignedInt::bits_precision(self)
168 }
169
170 #[cfg(feature = "alloc")]
171 fn to_be_bytes_trimmed_vartime(&self) -> Box<[u8]> {
172 unreachable!("alloc-gated")
173 }
174}
175
176#[cfg(not(feature = "alloc"))]
177impl<T> TryFromBeBytes for T
178where
179 T: FixedWidthUnsignedInt,
180{
181 fn try_from_be_bytes_vartime(bytes: &[u8]) -> Result<Self> {
182 FixedWidthUnsignedInt::try_from_be_bytes_vartime(bytes)
183 }
184}
185
186pub trait TryFromBeBytes: Sized {
187 fn try_from_be_bytes_vartime(bytes: &[u8]) -> Result<Self>;
188}
189
190pub trait UnsignedModularInt:
191 Zeroize + Clone + PartialOrd + IntegerResize<Output = Self> + TryFromBeBytes
192{
193 type Bytes: NumBytes + AsMut<[u8]>;
194 fn leading_zeros(&self) -> u32;
195 fn to_be_bytes(&self) -> Self::Bytes;
196 fn as_nz_ref(&self) -> NonZero<Self>;
197 fn bits(&self) -> u32;
198 fn bits_precision(&self) -> u32;
199 #[cfg(feature = "alloc")]
200 fn to_be_bytes_trimmed_vartime(&self) -> Box<[u8]>;
201}
202
203impl<T> NonZero<T>
204where
205 T: UnsignedModularInt,
206{
207 pub fn new(value: T) -> Option<Self> {
208 if value.bits() == 0 {
209 None
210 } else {
211 Some(Self(value))
212 }
213 }
214
215 pub fn get(self) -> T {
216 self.0
217 }
218
219 #[allow(clippy::should_implement_trait)]
220 pub fn as_ref(&self) -> &T {
221 &self.0
222 }
223
224 pub fn bits(&self) -> u32 {
225 self.0.bits()
226 }
227
228 pub fn bits_precision(&self) -> u32 {
229 self.0.bits_precision()
230 }
231
232 pub fn to_be_bytes(&self) -> T::Bytes {
233 self.0.to_be_bytes()
234 }
235
236 #[cfg(feature = "alloc")]
237 pub fn to_be_bytes_trimmed_vartime(&self) -> Box<[u8]> {
238 self.0.to_be_bytes_trimmed_vartime()
239 }
240}
241
242impl<T> Odd<T>
243where
244 T: UnsignedModularInt,
245{
246 pub fn new(value: T) -> Option<Self> {
247 let non_zero = NonZero::new(value)?;
248 let bytes = non_zero.as_ref().to_be_bytes();
249 let bytes = bytes.as_ref();
250 let is_odd = bytes.last().map(|byte| byte & 1 == 1).unwrap_or(false);
251 if is_odd {
252 Some(Self(non_zero.get()))
253 } else {
254 None
255 }
256 }
257
258 pub fn get(self) -> T {
259 self.0
260 }
261
262 #[allow(clippy::should_implement_trait)]
263 pub fn as_ref(&self) -> &T {
264 &self.0
265 }
266
267 pub fn as_nz_ref(&self) -> NonZero<T> {
268 NonZero::new(self.0.clone()).expect("odd values are non-zero")
269 }
270
271 pub fn bits_precision(&self) -> u32 {
272 self.0.bits_precision()
273 }
274}
275
276pub trait IntoMontyForm<P: ModulusParams>: Sized {
293 fn from_reduced(integer: P::Modulus, params: &P) -> Self;
295
296 fn from_value(integer: P::Modulus, params: &P) -> Self;
299}
300
301#[cfg(feature = "alloc")]
302impl IntoMontyForm<BoxedMontyParams> for BoxedMontyForm {
303 fn from_reduced(integer: BoxedUint, params: &BoxedMontyParams) -> Self {
304 BoxedMontyForm::new(integer, params)
305 }
306
307 fn from_value(integer: BoxedUint, params: &BoxedMontyParams) -> Self {
308 let modulus =
309 CryptoNonZero::new(params.modulus().as_ref().clone()).expect("modulus is non-zero");
310 let reduced = integer.rem_vartime(&modulus);
311 Self::from_reduced(reduced, params)
312 }
313}
314
315pub trait PowBoundedExp<M: ModulusParams>: Sized {
316 fn pow_bounded_exp(&self, exp: &M::Modulus, exp_bits: u32) -> Self;
317 fn retrieve(&self) -> M::Modulus;
318}
319
320#[cfg(feature = "alloc")]
321impl PowBoundedExp<BoxedMontyParams> for BoxedMontyForm {
322 fn pow_bounded_exp(&self, exp: &BoxedUint, exp_bits: u32) -> Self {
323 self.clone().pow_bounded_exp(exp, exp_bits)
324 }
325
326 fn retrieve(&self) -> BoxedUint {
327 self.clone().retrieve()
328 }
329}
330
331pub trait Pow<M: ModulusParams>: Sized {
332 fn pow(&self, exp: &M::Modulus) -> Self;
333}
334
335#[cfg(feature = "alloc")]
336impl Pow<BoxedMontyParams> for BoxedMontyForm {
337 fn pow(&self, exp: &BoxedUint) -> Self {
338 self.clone().pow(exp)
339 }
340}
341
342pub trait InvertCt<M: ModulusParams>: Sized {
355 fn invert_ct(&self) -> Option<Self>;
356}
357
358#[cfg(feature = "alloc")]
359impl InvertCt<BoxedMontyParams> for BoxedMontyForm {
360 fn invert_ct(&self) -> Option<Self> {
361 self.invert().into_option()
362 }
363}
364
365pub trait MulCt<M: ModulusParams>: Sized {
375 fn mul_ct(&self, rhs: &Self) -> Self;
376}
377
378#[cfg(feature = "alloc")]
379impl MulCt<BoxedMontyParams> for BoxedMontyForm {
380 fn mul_ct(&self, rhs: &Self) -> Self {
381 self * rhs
382 }
383}
384
385pub trait TryRandomMod: Sized {
395 fn try_random_mod<R>(rng: &mut R, modulus: &Self) -> Result<Self>
396 where
397 R: rand_core::TryCryptoRng + ?Sized;
398}
399
400#[cfg(feature = "alloc")]
401impl TryRandomMod for BoxedUint {
402 fn try_random_mod<R>(rng: &mut R, modulus: &Self) -> Result<Self>
403 where
404 R: rand_core::TryCryptoRng + ?Sized,
405 {
406 let nz = CryptoNonZero::new(modulus.clone())
407 .into_option()
408 .ok_or(Error::InvalidModulus)?;
409 <Self as crypto_bigint::RandomMod>::try_random_mod_vartime(rng, &nz).map_err(|_| Error::Rng)
410 }
411}
412
413pub trait ModulusParams: Sized {
414 type Modulus: UnsignedModularInt;
415 type MontgomeryForm: IntoMontyForm<Self> + PowBoundedExp<Self>;
416 fn modulus(&self) -> &Odd<Self::Modulus>;
417 fn bits_precision(&self) -> u32;
418}
419
420pub(crate) mod sealed {
421 pub trait CtModulusParamsSealed {}
425}
426
427pub trait CtModulusParams: ModulusParams + sealed::CtModulusParamsSealed {}
459
460#[cfg(feature = "alloc")]
461impl sealed::CtModulusParamsSealed for BoxedMontyParams {}
462#[cfg(feature = "alloc")]
463impl CtModulusParams for BoxedMontyParams {}
464
465#[cfg(feature = "alloc")]
466impl ModulusParams for BoxedMontyParams {
467 type Modulus = BoxedUint;
468 type MontgomeryForm = BoxedMontyForm;
469 fn modulus(&self) -> &Odd<Self::Modulus> {
470 const _: () = assert!(
476 core::mem::size_of::<CryptoOdd<BoxedUint>>() == core::mem::size_of::<Odd<BoxedUint>>()
477 );
478 const _: () = assert!(
479 core::mem::align_of::<CryptoOdd<BoxedUint>>()
480 == core::mem::align_of::<Odd<BoxedUint>>()
481 );
482 unsafe {
483 &*(self.modulus() as *const CryptoOdd<Self::Modulus> as *const Odd<Self::Modulus>)
484 }
485 }
486 fn bits_precision(&self) -> u32 {
487 self.bits_precision()
488 }
489}
490
491#[cfg(feature = "alloc")]
492impl IntegerResize for BoxedUint {
493 type Output = Self;
494
495 fn resize_unchecked(self, at_least_bits_precision: u32) -> Self::Output {
496 CryptoResize::resize_unchecked(self, at_least_bits_precision)
497 }
498
499 fn try_resize(self, at_least_bits_precision: u32) -> Option<Self::Output> {
500 CryptoResize::try_resize(self, at_least_bits_precision)
501 }
502}
503
504#[cfg(feature = "alloc")]
505impl UnsignedModularInt for BoxedUint {
506 type Bytes = alloc::boxed::Box<[u8]>;
507
508 fn leading_zeros(&self) -> u32 {
509 self.leading_zeros()
510 }
511
512 fn to_be_bytes(&self) -> Self::Bytes {
513 self.to_be_bytes()
514 }
515 #[cfg(feature = "alloc")]
516 fn to_be_bytes_trimmed_vartime(&self) -> Box<[u8]> {
517 self.to_be_bytes_trimmed_vartime()
518 }
519 fn as_nz_ref(&self) -> NonZero<Self> {
520 NonZero::new(self.clone()).expect("Value is non-zero")
521 }
522 fn bits(&self) -> u32 {
523 self.bits()
524 }
525 fn bits_precision(&self) -> u32 {
526 self.bits_precision()
527 }
528}
529
530#[cfg(feature = "alloc")]
531impl TryFromBeBytes for BoxedUint {
532 fn try_from_be_bytes_vartime(bytes: &[u8]) -> Result<Self> {
533 Ok(BoxedUint::from_be_slice_vartime(bytes))
534 }
535}