1#![allow(missing_docs)]
6
7#[cfg(feature = "alloc")]
8use alloc::boxed::Box;
9use core::ops::{Shr, ShrAssign};
10
11use const_num_traits::ops::overflowing::OverflowingAdd;
12use const_num_traits::ops::wrapping::{WrappingAdd, WrappingMul, WrappingSub};
13use const_num_traits::{Ct, HasPersonality, Nct, Personality};
14use const_num_traits::{One, Zero};
15use modmath::{CiosMontMul, CiosMontMulCt, Field as ModmathField, Parity, WideMul};
16use zeroize::Zeroize;
17
18use crate::{
19 algorithms::rsa::rsa_encrypt,
20 errors::{Error, Result},
21 key::GenericRsaPublicKey,
22 traits::modular::{
23 FixedWidthUnsignedInt, IntegerResize, IntoMontyForm, ModulusParams, NonZero, Odd, Pow,
24 PowBoundedExp, TryFromBeBytes, UnsignedModularInt,
25 },
26};
27
28pub trait ModMathInt:
29 FixedWidthUnsignedInt
30 + From<u8>
31 + PartialEq
32 + PartialOrd
33 + One
34 + Zero
35 + Parity
36 + OverflowingAdd<Output = Self>
37 + WideMul
38 + CiosMontMul
39 + WrappingAdd<Output = Self>
40 + WrappingMul<Output = Self>
41 + WrappingSub<Output = Self>
42 + Shr<usize, Output = Self>
43 + ShrAssign<usize>
44 + HasPersonality
45{
46}
47
48impl<T> ModMathInt for T where
49 T: FixedWidthUnsignedInt
50 + From<u8>
51 + PartialEq
52 + PartialOrd
53 + One
54 + Zero
55 + Parity
56 + OverflowingAdd<Output = Self>
57 + WideMul
58 + CiosMontMul
59 + WrappingAdd<Output = Self>
60 + WrappingMul<Output = Self>
61 + WrappingSub<Output = Self>
62 + Shr<usize, Output = Self>
63 + ShrAssign<usize>
64 + HasPersonality
65{
66}
67
68pub trait ModMathIntCt:
69 FixedWidthUnsignedInt
70 + From<u8>
71 + PartialEq
72 + PartialOrd
73 + One
74 + Zero
75 + Parity
76 + OverflowingAdd<Output = Self>
77 + WideMul
78 + CiosMontMulCt
79 + WrappingAdd<Output = Self>
80 + WrappingMul<Output = Self>
81 + WrappingSub<Output = Self>
82 + Shr<usize, Output = Self>
83 + ShrAssign<usize>
84 + subtle::ConditionallySelectable
85 + subtle::ConstantTimeLess
86 + core::ops::BitAnd<Output = Self>
87 + HasPersonality
88 + const_num_traits::CtIsZero
89{
90}
91
92impl<T> ModMathIntCt for T where
93 T: FixedWidthUnsignedInt
94 + From<u8>
95 + PartialEq
96 + PartialOrd
97 + One
98 + Zero
99 + Parity
100 + OverflowingAdd<Output = Self>
101 + WideMul
102 + CiosMontMulCt
103 + WrappingAdd<Output = Self>
104 + WrappingMul<Output = Self>
105 + WrappingSub<Output = Self>
106 + Shr<usize, Output = Self>
107 + ShrAssign<usize>
108 + subtle::ConditionallySelectable
109 + subtle::ConstantTimeLess
110 + core::ops::BitAnd<Output = Self>
111 + HasPersonality
112 + const_num_traits::CtIsZero
113{
114}
115
116#[cfg(feature = "alloc")]
117fn wrap_value<T>(value: T) -> ModMathValue<T> {
118 ModMathValue(value)
119}
120
121#[cfg(not(feature = "alloc"))]
122fn wrap_value<T>(value: T) -> ModMathValue<T> {
123 value
124}
125
126#[cfg(feature = "alloc")]
127fn unwrap_value<T: Copy>(value: &ModMathValue<T>) -> T {
128 value.0
129}
130
131#[cfg(feature = "alloc")]
132fn unwrap_value_ref<T>(value: &ModMathValue<T>) -> &T {
133 &value.0
134}
135
136#[cfg(not(feature = "alloc"))]
137fn unwrap_value_ref<T>(value: &ModMathValue<T>) -> &T {
138 value
139}
140
141#[cfg(not(feature = "alloc"))]
142fn unwrap_value<T: Copy>(value: &ModMathValue<T>) -> T {
143 *value
144}
145
146#[cfg(feature = "alloc")]
147#[repr(transparent)]
148#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
149pub struct ModMathValue<T>(pub T);
150
151#[cfg(feature = "alloc")]
152impl<T> ModMathValue<T> {
153 pub fn from_inner(inner: T) -> Self {
154 Self(inner)
155 }
156
157 pub fn inner(&self) -> &T {
158 &self.0
159 }
160}
161
162#[cfg(feature = "alloc")]
163impl<T> Zeroize for ModMathValue<T>
164where
165 T: Zeroize,
166{
167 fn zeroize(&mut self) {
168 self.0.zeroize();
169 }
170}
171
172#[cfg(feature = "alloc")]
173impl<T> From<u8> for ModMathValue<T>
174where
175 T: From<u8>,
176{
177 fn from(value: u8) -> Self {
178 Self(<T as From<u8>>::from(value))
179 }
180}
181
182#[cfg(feature = "alloc")]
183impl<T> IntegerResize for ModMathValue<T>
184where
185 T: FixedWidthUnsignedInt + PartialOrd,
186{
187 type Output = Self;
188
189 fn resize_unchecked(self, _at_least_bits_precision: u32) -> Self::Output {
190 self
191 }
192
193 fn try_resize(self, at_least_bits_precision: u32) -> Option<Self::Output> {
194 let value_bits = self.bits_precision() - self.leading_zeros();
200 if value_bits <= at_least_bits_precision {
201 Some(self)
202 } else {
203 None
204 }
205 }
206}
207
208#[cfg(feature = "alloc")]
209impl<T> UnsignedModularInt for ModMathValue<T>
210where
211 T: FixedWidthUnsignedInt + PartialOrd,
212{
213 type Bytes = <T as FixedWidthUnsignedInt>::Bytes;
214
215 fn leading_zeros(&self) -> u32 {
216 FixedWidthUnsignedInt::leading_zeros(&self.0)
217 }
218
219 fn to_be_bytes(&self) -> Self::Bytes {
220 FixedWidthUnsignedInt::to_be_bytes(&self.0)
221 }
222
223 #[cfg(feature = "alloc")]
224 fn to_be_bytes_trimmed_vartime(&self) -> Box<[u8]> {
225 let bytes = self.to_be_bytes();
226 let bytes = bytes.as_ref();
227 let first_non_zero = bytes
228 .iter()
229 .position(|b| *b != 0)
230 .unwrap_or(bytes.len().saturating_sub(1));
231 bytes[first_non_zero..].to_vec().into_boxed_slice()
232 }
233
234 fn as_nz_ref(&self) -> NonZero<Self> {
235 NonZero::new(*self).expect("value is non-zero")
236 }
237
238 fn bits(&self) -> u32 {
239 self.bits_precision() - self.leading_zeros()
240 }
241
242 fn bits_precision(&self) -> u32 {
243 FixedWidthUnsignedInt::bits_precision(&self.0)
244 }
245}
246
247#[cfg(feature = "alloc")]
248impl<T> TryFromBeBytes for ModMathValue<T>
249where
250 T: FixedWidthUnsignedInt,
251{
252 fn try_from_be_bytes_vartime(bytes: &[u8]) -> Result<Self> {
253 Ok(Self(
254 <T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(bytes)?,
255 ))
256 }
257}
258
259#[cfg(feature = "alloc")]
264impl<T> crate::traits::keys::RawPrivateKeyConstructible for ModMathValue<T> where
265 T: FixedWidthUnsignedInt + PartialOrd
266{
267}
268
269#[cfg(not(feature = "alloc"))]
270pub type ModMathValue<T> = T;
271
272#[cfg(feature = "modmath")]
287fn try_random_mod_masked<R, T, W, F>(
288 rng: &mut R,
289 leading_zero_bits: u32,
290 modulus: &W,
291 wrap: F,
292) -> Result<W>
293where
294 R: rand_core::TryCryptoRng + ?Sized,
295 T: FixedWidthUnsignedInt,
296 W: PartialOrd,
297 F: Fn(T) -> W,
298{
299 let zero_bytes = (leading_zero_bits / 8) as usize;
300 let zero_bits_in_next = (leading_zero_bits % 8) as u8;
301
302 const MAX_TRIES: u32 = 128;
303 let mut bytes = <T as FixedWidthUnsignedInt>::Bytes::default();
304 for _ in 0..MAX_TRIES {
305 rng.try_fill_bytes(bytes.as_mut()).map_err(|_| Error::Rng)?;
306 let buf = bytes.as_mut();
308 for byte in buf.iter_mut().take(zero_bytes) {
309 *byte = 0;
310 }
311 if zero_bits_in_next > 0 {
312 if let Some(b) = buf.get_mut(zero_bytes) {
316 *b &= 0xFFu8 >> zero_bits_in_next;
317 }
318 }
319 let candidate = <T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(bytes.as_ref())?;
320 let wrapped = wrap(candidate);
321 if wrapped < *modulus {
322 return Ok(wrapped);
323 }
324 }
325 Err(Error::Internal)
326}
327
328#[cfg(feature = "alloc")]
329impl<T> crate::traits::modular::TryRandomMod for ModMathValue<T>
330where
331 T: FixedWidthUnsignedInt + PartialOrd,
332{
333 fn try_random_mod<R>(rng: &mut R, modulus: &Self) -> Result<Self>
334 where
335 R: rand_core::TryCryptoRng + ?Sized,
336 {
337 let container_bits = <T as FixedWidthUnsignedInt>::bits_precision(&modulus.0);
338 let leading_zero_bits = <T as FixedWidthUnsignedInt>::leading_zeros(&modulus.0);
339 if leading_zero_bits >= container_bits {
340 return Err(Error::InvalidModulus);
341 }
342 try_random_mod_masked::<R, T, _, _>(rng, leading_zero_bits, modulus, wrap_value::<T>)
343 }
344}
345
346#[cfg(not(feature = "alloc"))]
347impl<T> crate::traits::modular::TryRandomMod for T
348where
349 T: FixedWidthUnsignedInt + PartialOrd,
350{
351 fn try_random_mod<R>(rng: &mut R, modulus: &Self) -> Result<Self>
352 where
353 R: rand_core::TryCryptoRng + ?Sized,
354 {
355 let container_bits = <T as FixedWidthUnsignedInt>::bits_precision(modulus);
356 let leading_zero_bits = <T as FixedWidthUnsignedInt>::leading_zeros(modulus);
357 if leading_zero_bits >= container_bits {
358 return Err(Error::InvalidModulus);
359 }
360 try_random_mod_masked::<R, T, T, _>(rng, leading_zero_bits, modulus, |x| x)
361 }
362}
363
364#[derive(Clone, Debug)]
365pub struct ModMathParams<T, P: Personality = Nct> {
366 field: ModmathField<T, P>,
370 modulus_odd: Odd<ModMathValue<T>>,
376}
377
378impl<T: ModMathInt + HasPersonality<P = Nct>> ModMathParams<T, Nct> {
379 pub fn new(modulus: T) -> Result<Self> {
380 let field = ModmathField::<T, Nct>::new(modulus).ok_or(Error::InvalidModulus)?;
381 let modulus_odd = Odd::new(wrap_value(modulus)).ok_or(Error::InvalidModulus)?;
382 Ok(Self { field, modulus_odd })
383 }
384}
385
386impl<T: ModMathIntCt + HasPersonality<P = Ct>> ModMathParams<T, Ct> {
387 pub fn new(modulus: T) -> Result<Self> {
390 let field = ModmathField::<T, Ct>::new(modulus).ok_or(Error::InvalidModulus)?;
391 let modulus_odd = Odd::new(wrap_value(modulus)).ok_or(Error::InvalidModulus)?;
392 Ok(Self { field, modulus_odd })
393 }
394}
395
396impl<T, P: Personality> ModMathParams<T, P> {
397 pub(crate) fn field(&self) -> &ModmathField<T, P> {
398 &self.field
399 }
400}
401
402pub fn public_key_from_be_bytes<T>(
405 modulus: &[u8],
406 exponent: u32,
407) -> Result<GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Nct>>>
408where
409 T: ModMathInt + HasPersonality<P = Nct>,
410{
411 let n = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
412 modulus,
413 )?);
414 let exponent = exponent.to_be_bytes();
415 let e = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
416 &exponent,
417 )?);
418 GenericRsaPublicKey::from_components(n, e, ModMathParams::<T, Nct>::new(unwrap_value(&n))?)
419}
420
421pub fn rsa_public_op<T>(
424 key: &GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Nct>>,
425 input: &[u8],
426) -> Result<<ModMathValue<T> as UnsignedModularInt>::Bytes>
427where
428 T: ModMathInt + HasPersonality<P = Nct>,
429{
430 let input = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
431 input,
432 )?);
433 Ok(rsa_encrypt(key, &input)?.to_be_bytes())
434}
435
436pub fn public_key_ct_from_be_bytes<T>(
441 modulus: &[u8],
442 exponent: u32,
443) -> Result<GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Ct>>>
444where
445 T: ModMathIntCt + HasPersonality<P = Ct>,
446{
447 let n = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
448 modulus,
449 )?);
450 let exponent = exponent.to_be_bytes();
451 let e = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
452 &exponent,
453 )?);
454 GenericRsaPublicKey::from_components(n, e, ModMathParams::<T, Ct>::new(unwrap_value(&n))?)
455}
456
457pub fn rsa_public_op_ct<T>(
458 key: &GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Ct>>,
459 input: &[u8],
460) -> Result<<ModMathValue<T> as UnsignedModularInt>::Bytes>
461where
462 T: ModMathIntCt + HasPersonality<P = Ct>,
463{
464 let input = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
465 input,
466 )?);
467 Ok(rsa_encrypt(key, &input)?.to_be_bytes())
468}
469
470#[derive(Clone, Debug)]
473pub struct ModMathForm<T, P: Personality = Nct>
474where
475 T: Clone + Zeroize,
476{
477 integer_mont: ModMathValue<T>,
478 params: ModMathParams<T, P>,
479}
480
481impl<T, P: Personality> Zeroize for ModMathForm<T, P>
483where
484 T: Clone + Zeroize,
485{
486 fn zeroize(&mut self) {
487 self.integer_mont.zeroize();
488 }
489}
490
491impl<T, P: Personality> Drop for ModMathForm<T, P>
492where
493 T: Clone + Zeroize,
494{
495 fn drop(&mut self) {
496 self.zeroize();
497 }
498}
499
500impl<T, P: Personality> zeroize::ZeroizeOnDrop for ModMathForm<T, P> where T: Clone + Zeroize {}
501
502impl<T: ModMathInt + HasPersonality<P = Nct>> IntoMontyForm<ModMathParams<T, Nct>>
503 for ModMathForm<T, Nct>
504{
505 fn from_reduced(integer: ModMathValue<T>, params: &ModMathParams<T, Nct>) -> Self {
506 let field = params.field();
507 let r = field.reduce(unwrap_value_ref(&integer));
508 Self {
509 integer_mont: wrap_value(*r.mont_value()),
510 params: params.clone(),
511 }
512 }
513
514 fn from_value(integer: ModMathValue<T>, params: &ModMathParams<T, Nct>) -> Self {
518 Self::from_reduced(integer, params)
519 }
520}
521
522impl<T: ModMathInt + HasPersonality<P = Nct>> ModMathForm<T, Nct> {
523 fn pow_loop(&self, exp_raw: T) -> T {
524 let field = self.params.field();
525 let base = field.residue_from_mont(unwrap_value(&self.integer_mont));
526 *field.exp(&base, &exp_raw).mont_value()
527 }
528
529 fn to_reduced(&self) -> T {
530 let field = self.params.field();
531 let r = field.residue_from_mont(unwrap_value(&self.integer_mont));
532 field.into_raw(&r)
533 }
534}
535
536impl<T: ModMathInt + HasPersonality<P = Nct>> Pow<ModMathParams<T, Nct>> for ModMathForm<T, Nct> {
537 fn pow(&self, exp: &ModMathValue<T>) -> Self {
538 let result_mont = self.pow_loop(unwrap_value(exp));
539 Self {
540 integer_mont: wrap_value(result_mont),
541 params: self.params.clone(),
542 }
543 }
544}
545
546impl<T: ModMathInt + HasPersonality<P = Nct>> PowBoundedExp<ModMathParams<T, Nct>>
547 for ModMathForm<T, Nct>
548{
549 fn pow_bounded_exp(&self, exp: &ModMathValue<T>, _exp_bits: u32) -> Self {
550 let result_mont = self.pow_loop(unwrap_value(exp));
553 Self {
554 integer_mont: wrap_value(result_mont),
555 params: self.params.clone(),
556 }
557 }
558
559 fn retrieve(&self) -> ModMathValue<T> {
560 wrap_value(self.to_reduced())
561 }
562}
563
564impl<T: ModMathInt + HasPersonality<P = Nct>> ModulusParams for ModMathParams<T, Nct> {
565 type Modulus = ModMathValue<T>;
566 type MontgomeryForm = ModMathForm<T, Nct>;
567
568 fn modulus(&self) -> &Odd<Self::Modulus> {
569 &self.modulus_odd
570 }
571
572 fn bits_precision(&self) -> u32 {
573 FixedWidthUnsignedInt::bits_precision(self.field.modulus())
574 }
575}
576
577impl<T: ModMathIntCt + HasPersonality<P = Ct>> IntoMontyForm<ModMathParams<T, Ct>>
578 for ModMathForm<T, Ct>
579{
580 fn from_reduced(integer: ModMathValue<T>, params: &ModMathParams<T, Ct>) -> Self {
581 let field = params.field();
582 let r = field.reduce(unwrap_value_ref(&integer));
583 Self {
584 integer_mont: wrap_value(*r.mont_value()),
585 params: params.clone(),
586 }
587 }
588
589 fn from_value(integer: ModMathValue<T>, params: &ModMathParams<T, Ct>) -> Self {
592 Self::from_reduced(integer, params)
593 }
594}
595
596impl<T: ModMathIntCt + HasPersonality<P = Ct>> ModMathForm<T, Ct> {
597 fn pow_loop_ct(&self, exp_raw: T) -> T {
602 let field = self.params.field();
603 let base = field.residue_from_mont(unwrap_value(&self.integer_mont));
604 *field.exp(&base, &exp_raw).mont_value()
605 }
606
607 fn pow_loop_public_exp(&self, exp_raw: T) -> T {
612 let field = self.params.field();
613 let base = field.residue_from_mont(unwrap_value(&self.integer_mont));
614 *field.exp_public_exp(&base, &exp_raw).mont_value()
615 }
616
617 fn to_reduced(&self) -> T {
618 let field = self.params.field();
619 let r = field.residue_from_mont(unwrap_value(&self.integer_mont));
620 field.into_raw(&r)
621 }
622}
623
624impl<T: ModMathIntCt + HasPersonality<P = Ct>> Pow<ModMathParams<T, Ct>> for ModMathForm<T, Ct> {
625 fn pow(&self, exp: &ModMathValue<T>) -> Self {
626 let result_mont = self.pow_loop_ct(unwrap_value(exp));
627 Self {
628 integer_mont: wrap_value(result_mont),
629 params: self.params.clone(),
630 }
631 }
632}
633
634impl<T: ModMathIntCt + HasPersonality<P = Ct>> PowBoundedExp<ModMathParams<T, Ct>>
635 for ModMathForm<T, Ct>
636{
637 fn pow_bounded_exp(&self, exp: &ModMathValue<T>, _exp_bits: u32) -> Self {
638 let result_mont = self.pow_loop_public_exp(unwrap_value(exp));
639 Self {
640 integer_mont: wrap_value(result_mont),
641 params: self.params.clone(),
642 }
643 }
644
645 fn retrieve(&self) -> ModMathValue<T> {
646 wrap_value(self.to_reduced())
647 }
648}
649
650impl<T> crate::traits::modular::InvertCt<ModMathParams<T, Ct>> for ModMathForm<T, Ct>
655where
656 T: ModMathIntCt
657 + HasPersonality<P = Ct>
658 + modmath_cios::CiosRowOps
659 + core::ops::BitOr<Output = T>,
660 <T as modmath_cios::CiosRowOps>::Word: const_num_traits::CtParity,
661{
662 fn invert_ct(&self) -> Option<Self> {
663 let field = self.params.field();
664 let residue = field.residue_from_mont(unwrap_value(&self.integer_mont));
665 let ct_option = field.inv_safegcd_ct(&residue);
666 ct_option.into_option().map(|inv_res| Self {
667 integer_mont: wrap_value(*inv_res.mont_value()),
668 params: self.params.clone(),
669 })
670 }
671}
672
673impl<T: ModMathIntCt + HasPersonality<P = Ct>> crate::traits::modular::MulCt<ModMathParams<T, Ct>>
678 for ModMathForm<T, Ct>
679{
680 fn mul_ct(&self, rhs: &Self) -> Self {
681 debug_assert!(
687 self.params.modulus_odd == rhs.params.modulus_odd,
688 "MulCt operands must share the same modulus"
689 );
690 let field = self.params.field();
691 let lhs_res = field.residue_from_mont(unwrap_value(&self.integer_mont));
692 let rhs_res = field.residue_from_mont(unwrap_value(&rhs.integer_mont));
693 let product = field.mul(&lhs_res, &rhs_res);
694 Self {
695 integer_mont: wrap_value(*product.mont_value()),
696 params: self.params.clone(),
697 }
698 }
699}
700
701impl<T: ModMathIntCt + HasPersonality<P = Ct>> ModulusParams for ModMathParams<T, Ct> {
702 type Modulus = ModMathValue<T>;
703 type MontgomeryForm = ModMathForm<T, Ct>;
704
705 fn modulus(&self) -> &Odd<Self::Modulus> {
706 &self.modulus_odd
707 }
708
709 fn bits_precision(&self) -> u32 {
710 FixedWidthUnsignedInt::bits_precision(self.field.modulus())
711 }
712}
713
714impl<T: ModMathIntCt + HasPersonality<P = Ct>> crate::traits::modular::sealed::CtModulusParamsSealed
720 for ModMathParams<T, Ct>
721{
722}
723impl<T: ModMathIntCt + HasPersonality<P = Ct>> crate::traits::modular::CtModulusParams
724 for ModMathParams<T, Ct>
725{
726}
727
728#[cfg(test)]
729#[cfg(feature = "alloc")]
730mod tests {
731 use const_num_traits::Ct;
732 use fixed_bigint::FixedUInt;
733 use rand::rngs::ChaCha8Rng;
734 use rand_core::SeedableRng;
735 use sha1::Sha1;
736 use signature::hazmat::PrehashVerifier;
737
738 use super::{
739 public_key_ct_from_be_bytes, public_key_from_be_bytes, ModMathForm, ModMathParams,
740 ModMathValue,
741 };
742 use crate::key::GenericRsaPublicKey;
743 use crate::pkcs1v15::{GenericEncryptingKey, GenericSignature, GenericVerifyingKey};
744 use crate::{traits::RandomizedEncryptor, BoxedUint, Pkcs1v15Encrypt, RsaPublicKey};
745
746 type SmallU = FixedUInt<u8, 64>;
747 type SmallUCt = FixedUInt<u8, 64, Ct>;
748
749 #[test]
750 fn brand_round_trip() {
751 let params = ModMathParams::<SmallU>::new(SmallU::from(13u8)).unwrap();
752 let f = params.field();
753 let r = f.reduce(&SmallU::from(7u8));
754 assert_eq!(f.into_raw(&r), SmallU::from(7u8));
755 }
756
757 #[test]
758 fn brand_mul_exp() {
759 let params = ModMathParams::<SmallU>::new(SmallU::from(13u8)).unwrap();
760 let f = params.field();
761 let a = f.reduce(&SmallU::from(7u8));
763 let b = f.reduce(&SmallU::from(11u8));
764 assert_eq!(f.into_raw(&f.mul(&a, &b)), SmallU::from(12u8));
765 let base = f.reduce(&SmallU::from(2u8));
767 assert_eq!(
768 f.into_raw(&f.exp(&base, &SmallU::from(10u8))),
769 SmallU::from(10u8)
770 );
771 }
772
773 #[test]
774 fn brand_ct_matches_nct() {
775 let p_nct = ModMathParams::<SmallU>::new(SmallU::from(13u8)).unwrap();
776 let p_ct = ModMathParams::<SmallUCt, Ct>::new(SmallUCt::from(13u8)).unwrap();
777 let f_nct = p_nct.field();
778 let f_ct = p_ct.field();
779 let nct = f_nct.into_raw(&f_nct.mul(
780 &f_nct.reduce(&SmallU::from(7u8)),
781 &f_nct.reduce(&SmallU::from(11u8)),
782 ));
783 let ct = f_ct.into_raw(&f_ct.mul(
784 &f_ct.reduce(&SmallUCt::from(7u8)),
785 &f_ct.reduce(&SmallUCt::from(11u8)),
786 ));
787 let mut nct_bytes = [0u8; 64];
789 let mut ct_bytes = [0u8; 64];
790 let _ = nct.to_be_bytes(&mut nct_bytes);
791 let _ = ct.to_be_bytes(&mut ct_bytes);
792 assert_eq!(nct_bytes, ct_bytes);
793 }
794
795 #[test]
796 fn mod_math_form_zeroize_on_drop() {
797 fn assert_zeroize_on_drop<T: zeroize::ZeroizeOnDrop>() {}
798 assert_zeroize_on_drop::<ModMathForm<SmallU>>();
799 assert_zeroize_on_drop::<ModMathForm<SmallUCt, Ct>>();
800 }
801
802 #[test]
803 fn verify_pkcs1v15_signature_with_modmath_fixed_uint() {
804 type U512 = FixedUInt<u8, 64>;
805
806 let digest: [u8; 20] = [
807 0x43, 0x0c, 0xe3, 0x4d, 0x02, 0x07, 0x24, 0xed, 0x75, 0xa1, 0x96, 0xdf, 0xc2, 0xad,
808 0x67, 0xc7, 0x77, 0x72, 0xd1, 0x69,
809 ];
810 let modulus: [u8; 64] = [
811 0x96, 0x9D, 0x03, 0xFF, 0xA9, 0x8D, 0x88, 0x8F, 0x3A, 0xA4, 0xF2, 0xFE, 0xD2, 0x32,
812 0xE6, 0x1C, 0x4A, 0xCF, 0x06, 0x63, 0xA9, 0x2F, 0x99, 0x03, 0x4C, 0xF7, 0xB7, 0x24,
813 0x5A, 0x1A, 0x1E, 0x5E, 0xAF, 0xA5, 0x65, 0xAF, 0xB9, 0x0B, 0xAB, 0x22, 0x85, 0x71,
814 0x2F, 0xAA, 0x50, 0x39, 0x39, 0xA0, 0x65, 0xFB, 0x60, 0xDD, 0x08, 0x28, 0xA3, 0x84,
815 0xF2, 0x6D, 0x8A, 0xFC, 0x28, 0x6D, 0xF6, 0xCF,
816 ];
817 let signature: [u8; 64] = [
818 0x45, 0x53, 0xF3, 0xAF, 0x16, 0xAF, 0x63, 0x97, 0xB0, 0xD3, 0x2F, 0x8A, 0xEC, 0xD5,
819 0x4C, 0xF1, 0xF3, 0xD0, 0x0C, 0x9F, 0x42, 0xDC, 0x68, 0xCB, 0xD7, 0x05, 0xCE, 0xA5,
820 0xA9, 0x70, 0x95, 0x3E, 0xC0, 0xBC, 0x4A, 0x18, 0xED, 0x91, 0xA3, 0x5D, 0x66, 0xEC,
821 0xDA, 0x4A, 0x83, 0x32, 0xCF, 0xC3, 0xA3, 0xAB, 0x21, 0xAD, 0x59, 0xB2, 0x2E, 0x87,
822 0xC2, 0x73, 0xFF, 0x08, 0x88, 0xDD, 0x4D, 0xE0,
823 ];
824
825 let key = public_key_from_be_bytes::<U512>(&modulus, 3).unwrap();
826 let verifying_key = GenericVerifyingKey::<Sha1, _, _>::new(key);
827 let signature =
828 GenericSignature::from(ModMathValue::from_inner(U512::from_be_bytes(&signature)));
829 verifying_key.verify_prehash(&digest, &signature).unwrap();
830 }
831
832 #[test]
833 fn verify_pkcs1v15_signature_with_modmath_fixed_uint32() {
834 type U512 = FixedUInt<u32, 16>;
835
836 let digest: [u8; 20] = [
837 0x43, 0x0c, 0xe3, 0x4d, 0x02, 0x07, 0x24, 0xed, 0x75, 0xa1, 0x96, 0xdf, 0xc2, 0xad,
838 0x67, 0xc7, 0x77, 0x72, 0xd1, 0x69,
839 ];
840 let modulus: [u8; 64] = [
841 0x96, 0x9D, 0x03, 0xFF, 0xA9, 0x8D, 0x88, 0x8F, 0x3A, 0xA4, 0xF2, 0xFE, 0xD2, 0x32,
842 0xE6, 0x1C, 0x4A, 0xCF, 0x06, 0x63, 0xA9, 0x2F, 0x99, 0x03, 0x4C, 0xF7, 0xB7, 0x24,
843 0x5A, 0x1A, 0x1E, 0x5E, 0xAF, 0xA5, 0x65, 0xAF, 0xB9, 0x0B, 0xAB, 0x22, 0x85, 0x71,
844 0x2F, 0xAA, 0x50, 0x39, 0x39, 0xA0, 0x65, 0xFB, 0x60, 0xDD, 0x08, 0x28, 0xA3, 0x84,
845 0xF2, 0x6D, 0x8A, 0xFC, 0x28, 0x6D, 0xF6, 0xCF,
846 ];
847 let signature: [u8; 64] = [
848 0x45, 0x53, 0xF3, 0xAF, 0x16, 0xAF, 0x63, 0x97, 0xB0, 0xD3, 0x2F, 0x8A, 0xEC, 0xD5,
849 0x4C, 0xF1, 0xF3, 0xD0, 0x0C, 0x9F, 0x42, 0xDC, 0x68, 0xCB, 0xD7, 0x05, 0xCE, 0xA5,
850 0xA9, 0x70, 0x95, 0x3E, 0xC0, 0xBC, 0x4A, 0x18, 0xED, 0x91, 0xA3, 0x5D, 0x66, 0xEC,
851 0xDA, 0x4A, 0x83, 0x32, 0xCF, 0xC3, 0xA3, 0xAB, 0x21, 0xAD, 0x59, 0xB2, 0x2E, 0x87,
852 0xC2, 0x73, 0xFF, 0x08, 0x88, 0xDD, 0x4D, 0xE0,
853 ];
854
855 let n = U512::from_be_bytes(&modulus);
856 let e = U512::from(3u8);
857 let key = GenericRsaPublicKey::from_components(
861 ModMathValue::from_inner(n),
862 ModMathValue::from_inner(e),
863 ModMathParams::<U512, const_num_traits::Nct>::new(n).unwrap(),
864 )
865 .unwrap();
866 let verifying_key = GenericVerifyingKey::<Sha1, _, _>::new(key);
867 let signature =
868 GenericSignature::from(ModMathValue::from_inner(U512::from_be_bytes(&signature)));
869 verifying_key.verify_prehash(&digest, &signature).unwrap();
870 }
871
872 #[test]
873 fn encrypt_pkcs1v15_with_modmath_fixed_uint_matches_boxeduint() {
874 type U512 = FixedUInt<u8, 64, Ct>;
878
879 let modulus: [u8; 64] = [
880 0x96, 0x9D, 0x03, 0xFF, 0xA9, 0x8D, 0x88, 0x8F, 0x3A, 0xA4, 0xF2, 0xFE, 0xD2, 0x32,
881 0xE6, 0x1C, 0x4A, 0xCF, 0x06, 0x63, 0xA9, 0x2F, 0x99, 0x03, 0x4C, 0xF7, 0xB7, 0x24,
882 0x5A, 0x1A, 0x1E, 0x5E, 0xAF, 0xA5, 0x65, 0xAF, 0xB9, 0x0B, 0xAB, 0x22, 0x85, 0x71,
883 0x2F, 0xAA, 0x50, 0x39, 0x39, 0xA0, 0x65, 0xFB, 0x60, 0xDD, 0x08, 0x28, 0xA3, 0x84,
884 0xF2, 0x6D, 0x8A, 0xFC, 0x28, 0x6D, 0xF6, 0xCF,
885 ];
886 let msg = b"hello world!";
887
888 let modmath_key = public_key_ct_from_be_bytes::<U512>(&modulus, 3).unwrap();
889 let boxed_key = RsaPublicKey::new(
890 BoxedUint::from_be_slice(&modulus, 512).unwrap(),
891 3u64.into(),
892 )
893 .unwrap();
894
895 let mut modmath_rng = ChaCha8Rng::from_seed([42; 32]);
896 let mut boxed_rng = ChaCha8Rng::from_seed([42; 32]);
897 let mut storage = [0u8; 64];
898
899 let modmath_ciphertext = GenericEncryptingKey::new(modmath_key)
900 .encrypt_with_rng_into(&mut modmath_rng, msg, &mut storage)
901 .unwrap();
902 let boxed_ciphertext = boxed_key
903 .encrypt(&mut boxed_rng, Pkcs1v15Encrypt, msg)
904 .unwrap();
905
906 assert_eq!(modmath_ciphertext, boxed_ciphertext.as_slice());
907 }
908}
909
910#[cfg(test)]
914mod private_op_tests {
915 use super::*;
916 use const_num_traits::Ct;
917 use fixed_bigint::FixedUInt;
918
919 type SmallUCt = FixedUInt<u8, 64, Ct>;
920
921 fn toy_params() -> ModMathParams<SmallUCt, Ct> {
924 ModMathParams::<SmallUCt, Ct>::new(SmallUCt::from(35u8)).unwrap()
925 }
926
927 fn toy_params_wide() -> ModMathParams<SmallUCt, Ct> {
934 let mut bytes = [0u8; 64];
935 bytes[0] = 0x80;
936 bytes[63] = 0x01;
937 let n = <SmallUCt as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&bytes).unwrap();
938 ModMathParams::<SmallUCt, Ct>::new(n).unwrap()
939 }
940
941 #[test]
942 fn rsa_private_op_round_trip_heapless_ct() {
943 let n_params = toy_params();
944 let c = wrap_value(SmallUCt::from(32u8));
945 let d = wrap_value(SmallUCt::from(29u8));
946 let expected = wrap_value(SmallUCt::from(2u8));
947 let recovered = crate::algorithms::rsa::rsa_private_op(&c, &d, &n_params);
948 assert_eq!(recovered, expected);
949 }
950
951 #[test]
957 fn rsa_private_op_blinded_matches_unblinded_heapless_ct() {
958 let n_params = toy_params();
959 let c = wrap_value(SmallUCt::from(32u8));
960 let d = wrap_value(SmallUCt::from(29u8));
961 let e = wrap_value(SmallUCt::from(5u8));
962 let r = wrap_value(SmallUCt::from(6u8));
963 let expected = wrap_value(SmallUCt::from(2u8));
964 let recovered =
965 crate::algorithms::rsa::rsa_private_op_blinded(&r, &c, &d, &e, &n_params).unwrap();
966 assert_eq!(recovered, expected);
967 }
968
969 #[test]
974 fn rsa_private_op_blinded_rejects_non_coprime_r() {
975 let n_params = toy_params();
976 let c = wrap_value(SmallUCt::from(32u8));
977 let d = wrap_value(SmallUCt::from(29u8));
978 let e = wrap_value(SmallUCt::from(5u8));
979 let r_bad = wrap_value(SmallUCt::from(5u8));
980 let result = crate::algorithms::rsa::rsa_private_op_blinded(&r_bad, &c, &d, &e, &n_params);
981 assert!(result.is_err());
982 }
983
984 #[test]
991 fn rsa_private_op_and_check_blinded_round_trip_heapless_ct() {
992 use rand::rngs::ChaCha8Rng;
993 use rand_core::SeedableRng;
994
995 let n_params = toy_params();
996 let c = wrap_value(SmallUCt::from(32u8));
997 let d = wrap_value(SmallUCt::from(29u8));
998 let e = wrap_value(SmallUCt::from(5u8));
999 let expected = wrap_value(SmallUCt::from(2u8));
1000 let mut rng = ChaCha8Rng::from_seed([42; 32]);
1001 let recovered = crate::algorithms::rsa::rsa_private_op_and_check_blinded(
1002 &mut rng, &c, &d, &e, &n_params,
1003 )
1004 .unwrap();
1005 assert_eq!(recovered, expected);
1006 }
1007
1008 #[test]
1012 fn try_random_mod_modmath_stays_below_modulus() {
1013 use crate::traits::modular::TryRandomMod;
1014 use rand::rngs::ChaCha8Rng;
1015 use rand_core::SeedableRng;
1016
1017 let n_params = toy_params_wide();
1018 let n = *n_params.modulus().as_ref();
1019 let mut rng = ChaCha8Rng::from_seed([42; 32]);
1020
1021 let mut samples = [ModMathValue::<SmallUCt>::from(0u8); 16];
1024 for slot in samples.iter_mut() {
1025 let r = ModMathValue::<SmallUCt>::try_random_mod(&mut rng, &n).unwrap();
1026 assert!(r < n, "sample must be < modulus");
1027 *slot = r;
1028 }
1029 let first = samples[0];
1032 assert!(
1033 samples.iter().any(|s| *s != first),
1034 "samples are trivially all equal — RNG or sampler broken"
1035 );
1036 }
1037
1038 #[test]
1044 fn try_random_mod_modmath_succeeds_on_narrow_modulus_wide_carrier() {
1045 use crate::traits::modular::TryRandomMod;
1046 use rand::rngs::ChaCha8Rng;
1047 use rand_core::SeedableRng;
1048
1049 let n_params = toy_params(); let n = *n_params.modulus().as_ref();
1051 let mut rng = ChaCha8Rng::from_seed([42; 32]);
1052
1053 for _ in 0..64 {
1054 let r = ModMathValue::<SmallUCt>::try_random_mod(&mut rng, &n).unwrap();
1055 assert!(r < n);
1056 }
1057 }
1058
1059 #[test]
1060 fn rsa_private_op_and_check_round_trip_heapless_ct() {
1061 let n_params = toy_params();
1062 let c = wrap_value(SmallUCt::from(32u8));
1063 let d = wrap_value(SmallUCt::from(29u8));
1064 let e = wrap_value(SmallUCt::from(5u8));
1065 let expected = wrap_value(SmallUCt::from(2u8));
1066 let recovered =
1067 crate::algorithms::rsa::rsa_private_op_and_check(&c, &d, &e, &n_params).unwrap();
1068 assert_eq!(recovered, expected);
1069 }
1070
1071 #[test]
1075 fn invert_ct_modmath_known_answer() {
1076 use crate::traits::modular::{IntoMontyForm, InvertCt, PowBoundedExp};
1077 let n_params = toy_params();
1078 let three = wrap_value(SmallUCt::from(3u8));
1079 let mont_three = ModMathForm::<SmallUCt, Ct>::from_reduced(three, &n_params);
1080 let mont_inv = mont_three.invert_ct().expect("3 is coprime to 35");
1081 let recovered = PowBoundedExp::<ModMathParams<SmallUCt, Ct>>::retrieve(&mont_inv);
1082 assert_eq!(recovered, wrap_value(SmallUCt::from(12u8)));
1083 }
1084
1085 #[test]
1090 fn mul_ct_modmath_inverse_round_trip() {
1091 use crate::traits::modular::{IntoMontyForm, InvertCt, MulCt, PowBoundedExp};
1092 let n_params = toy_params();
1093 let three = wrap_value(SmallUCt::from(3u8));
1094 let mont_three = ModMathForm::<SmallUCt, Ct>::from_reduced(three, &n_params);
1095 let mont_inv = mont_three.invert_ct().expect("3 is coprime to 35");
1096 let product = mont_three.mul_ct(&mont_inv);
1097 let recovered = PowBoundedExp::<ModMathParams<SmallUCt, Ct>>::retrieve(&product);
1098 assert_eq!(recovered, wrap_value(SmallUCt::from(1u8)));
1099 }
1100
1101 #[test]
1102 fn rsa_private_op_and_check_rejects_wrong_exponent() {
1103 let n_params = toy_params();
1106 let c = wrap_value(SmallUCt::from(32u8));
1107 let bad_d = wrap_value(SmallUCt::from(11u8));
1108 let e = wrap_value(SmallUCt::from(5u8));
1109 let result = crate::algorithms::rsa::rsa_private_op_and_check(&c, &bad_d, &e, &n_params);
1110 assert!(result.is_err());
1111 }
1112
1113 const N_2048: [u8; 256] = hex_literal::hex!(
1118 "d397b84d98a4c26138ed1b695a8106ead91d553bf06041b62d3fdc50a041e222
1119 b8f4529689c1b82c5e71554f5dd69fa2f4b6158cf0dbeb57811a0fc327e1f28e
1120 74fe74d3bc166c1eabdc1b8b57b934ca8be5b00b4f29975bcc99acaf415b59bb
1121 28a6782bb41a2c3c2976b3c18dbadef62f00c6bb226640095096c0cc60d22fe7
1122 ef987d75c6a81b10d96bf292028af110dc7cc1bbc43d22adab379a0cd5d8078c
1123 c780ff5cd6209dea34c922cf784f7717e428d75b5aec8ff30e5f0141510766e2
1124 e0ab8d473c84e8710b2b98227c3db095337ad3452f19e2b9bfbccdd8148abf67
1125 76fa552775e6e75956e45229ae5a9c46949bab1e622f0e48f56524a84ed3483b"
1126 );
1127 const D_2048: [u8; 256] = hex_literal::hex!(
1128 "c4e70c689162c94c660828191b52b4d8392115df486a9adbe831e458d7395832
1129 0dc1b755456e93701e9702d76fb0b92f90e01d1fe248153281fe79aa9763a92f
1130 ae69d8d7ecd144de29fa135bd14f9573e349e45031e3b76982f583003826c552
1131 e89a397c1a06bd2163488630d92e8c2bb643d7abef700da95d685c941489a46f
1132 54b5316f62b5d2c3a7f1bbd134cb37353a44683fdc9d95d36458de22f6c44057
1133 fe74a0a436c4308f73f4da42f35c47ac16a7138d483afc91e41dc3a1127382e0
1134 c0f5119b0221b4fc639d6b9c38177a6de9b526ebd88c38d7982c07f98a0efd87
1135 7d508aae275b946915c02e2e1106d175d74ec6777f5e80d12c053d9c7be1e341"
1136 );
1137
1138 #[test]
1139 fn pkcs1v15_sign_into_round_trip_2048_sha1() {
1140 use crate::algorithms::pkcs1v15::{
1141 pkcs1v15_generate_prefix_into, pkcs1v15_sign_pad_into, sign_into,
1142 };
1143 use crate::traits::PublicKeyParts;
1144 use sha1::Sha1;
1145
1146 type U2048 = FixedUInt<u8, 256, Ct>;
1147 const K: usize = 256;
1148
1149 let key = public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1150 let d_int = <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap();
1151 let d = wrap_value(d_int);
1152 let e_int =
1153 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&[0x01, 0x00, 0x01])
1154 .unwrap();
1155 let e = wrap_value(e_int);
1156
1157 let digest = [0xAAu8; 20];
1158 let mut prefix_storage = [0u8; 32];
1159 let prefix = pkcs1v15_generate_prefix_into::<Sha1>(&mut prefix_storage).unwrap();
1160
1161 let mut em_storage = [0u8; K];
1162 let mut sig_storage = [0u8; K];
1163 let sig = sign_into(
1164 key.n_params(),
1165 &d,
1166 &e,
1167 prefix,
1168 &digest,
1169 K,
1170 &mut em_storage,
1171 &mut sig_storage,
1172 )
1173 .unwrap();
1174 assert_eq!(sig.len(), K);
1175
1176 let recovered = public_key_op_ct(&key, sig).unwrap();
1179 let mut expected_em_storage = [0u8; K];
1180 let expected_em =
1181 pkcs1v15_sign_pad_into(prefix, &digest, K, &mut expected_em_storage).unwrap();
1182 assert_eq!(recovered.as_ref(), expected_em);
1183 }
1184
1185 #[test]
1186 fn pss_sign_into_round_trip_2048_sha1() {
1187 use crate::algorithms::pss::{emsa_pss_verify, sign_into};
1188 use crate::traits::PublicKeyParts;
1189 use digest::Digest;
1190 use sha1::Sha1;
1191
1192 type U2048 = FixedUInt<u8, 256, Ct>;
1193 const K: usize = 256;
1194 const KEY_BITS: usize = 2048;
1195
1196 let key = public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1197 let d = wrap_value(
1198 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1199 );
1200 let e = wrap_value(
1201 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&[0x01, 0x00, 0x01])
1202 .unwrap(),
1203 );
1204
1205 let digest = [0xAAu8; 20];
1206 let salt: &[u8] = &[]; let mut hash = Sha1::new();
1208
1209 let mut em_storage = [0u8; K];
1210 let mut sig_storage = [0u8; K];
1211 let sig = sign_into(
1212 key.n_params(),
1213 &d,
1214 &e,
1215 &digest,
1216 salt,
1217 K,
1218 &mut hash,
1219 &mut em_storage,
1220 &mut sig_storage,
1221 )
1222 .unwrap();
1223 assert_eq!(sig.len(), K);
1224
1225 let recovered = public_key_op_ct(&key, sig).unwrap();
1229 let mut em_copy = [0u8; K];
1230 em_copy.copy_from_slice(recovered.as_ref());
1231 let mut verify_hash = Sha1::new();
1232 emsa_pss_verify(
1233 &digest,
1234 &mut em_copy,
1235 Some(salt.len()),
1236 &mut verify_hash,
1237 KEY_BITS,
1238 )
1239 .unwrap();
1240 }
1241
1242 fn public_key_op_ct<T>(
1244 key: &crate::key::GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Ct>>,
1245 input: &[u8],
1246 ) -> Result<<ModMathValue<T> as UnsignedModularInt>::Bytes>
1247 where
1248 T: ModMathIntCt + HasPersonality<P = Ct>,
1249 {
1250 crate::modmath_support::rsa_public_op_ct(key, input)
1251 }
1252
1253 fn dummy_de() -> (ModMathValue<SmallUCt>, ModMathValue<SmallUCt>) {
1260 (
1261 wrap_value(SmallUCt::from(1u8)),
1262 wrap_value(SmallUCt::from(1u8)),
1263 )
1264 }
1265
1266 const SMALL_K: usize = 64;
1268
1269 #[test]
1270 fn pkcs1v15_sign_into_rejects_wrong_k() {
1271 use crate::algorithms::pkcs1v15::sign_into;
1272 let n_params = toy_params();
1273 let (d, e) = dummy_de();
1274 let mut em = [0u8; SMALL_K];
1275 let mut sig = [0u8; SMALL_K];
1276 let result = sign_into(
1277 &n_params,
1278 &d,
1279 &e,
1280 &[],
1281 &[0u8; 20],
1282 SMALL_K - 1, &mut em,
1284 &mut sig,
1285 );
1286 assert!(matches!(result, Err(Error::InvalidArguments)));
1287 }
1288
1289 #[test]
1290 fn pkcs1v15_sign_into_rejects_small_sig_storage() {
1291 use crate::algorithms::pkcs1v15::sign_into;
1292 let n_params = toy_params_wide();
1293 let (d, e) = dummy_de();
1294 let mut em = [0u8; SMALL_K];
1295 let mut sig = [0u8; SMALL_K - 1]; let result = sign_into(
1297 &n_params,
1298 &d,
1299 &e,
1300 &[],
1301 &[0u8; 20],
1302 SMALL_K,
1303 &mut em,
1304 &mut sig,
1305 );
1306 assert!(matches!(result, Err(Error::OutputBufferTooSmall)));
1307 }
1308
1309 #[test]
1310 fn pkcs1v15_sign_into_propagates_message_too_long() {
1311 use crate::algorithms::pkcs1v15::sign_into;
1314 let n_params = toy_params_wide();
1315 let (d, e) = dummy_de();
1316 let mut em = [0u8; SMALL_K];
1317 let mut sig = [0u8; SMALL_K];
1318 let oversize_prefix = [0u8; SMALL_K]; let result = sign_into(
1320 &n_params,
1321 &d,
1322 &e,
1323 &oversize_prefix,
1324 &[0u8; 20],
1325 SMALL_K,
1326 &mut em,
1327 &mut sig,
1328 );
1329 assert!(matches!(result, Err(Error::MessageTooLong)));
1330 }
1331
1332 #[test]
1333 fn pss_sign_into_rejects_wrong_k() {
1334 use crate::algorithms::pss::sign_into;
1335 use digest::Digest;
1336 use sha1::Sha1;
1337 let n_params = toy_params_wide();
1338 let (d, e) = dummy_de();
1339 let mut em = [0u8; SMALL_K];
1340 let mut sig = [0u8; SMALL_K];
1341 let mut hash = Sha1::new();
1342 let result = sign_into(
1343 &n_params,
1344 &d,
1345 &e,
1346 &[0u8; 20],
1347 &[],
1348 SMALL_K - 1, &mut hash,
1350 &mut em,
1351 &mut sig,
1352 );
1353 assert!(matches!(result, Err(Error::InvalidArguments)));
1354 }
1355
1356 #[test]
1357 fn pss_sign_into_rejects_small_sig_storage() {
1358 use crate::algorithms::pss::sign_into;
1359 use digest::Digest;
1360 use sha1::Sha1;
1361 let n_params = toy_params_wide();
1362 let (d, e) = dummy_de();
1363 let mut em = [0u8; SMALL_K];
1364 let mut sig = [0u8; SMALL_K - 1];
1365 let mut hash = Sha1::new();
1366 let result = sign_into(
1367 &n_params,
1368 &d,
1369 &e,
1370 &[0u8; 20],
1371 &[],
1372 SMALL_K,
1373 &mut hash,
1374 &mut em,
1375 &mut sig,
1376 );
1377 assert!(matches!(result, Err(Error::OutputBufferTooSmall)));
1378 }
1379
1380 #[test]
1381 fn pss_sign_into_rejects_small_em_storage() {
1382 use crate::algorithms::pss::sign_into;
1383 use digest::Digest;
1384 use sha1::Sha1;
1385 let n_params = toy_params_wide();
1386 let (d, e) = dummy_de();
1387 let mut em = [0u8; SMALL_K - 1];
1389 let mut sig = [0u8; SMALL_K];
1390 let mut hash = Sha1::new();
1391 let result = sign_into(
1392 &n_params,
1393 &d,
1394 &e,
1395 &[0u8; 20],
1396 &[],
1397 SMALL_K,
1398 &mut hash,
1399 &mut em,
1400 &mut sig,
1401 );
1402 assert!(matches!(result, Err(Error::OutputBufferTooSmall)));
1403 }
1404
1405 #[test]
1406 fn pss_sign_into_rejects_wrong_hash_length() {
1407 use crate::algorithms::pss::sign_into;
1410 use digest::Digest;
1411 use sha1::Sha1;
1412 let n_params = toy_params_wide();
1413 let (d, e) = dummy_de();
1414 let mut em = [0u8; SMALL_K];
1415 let mut sig = [0u8; SMALL_K];
1416 let mut hash = Sha1::new();
1417 let result = sign_into(
1418 &n_params,
1419 &d,
1420 &e,
1421 &[0u8; 21], &[],
1423 SMALL_K,
1424 &mut hash,
1425 &mut em,
1426 &mut sig,
1427 );
1428 assert!(matches!(result, Err(Error::InputNotHashed)));
1429 }
1430
1431 #[test]
1436 fn pkcs1v15_sign_into_k_uses_modulus_bits_not_container() {
1437 use crate::algorithms::pkcs1v15::sign_into;
1438 type WideUCt = FixedUInt<u8, 128, Ct>;
1440 let mut mod_bytes = [0u8; 128];
1441 mod_bytes[64] = 0x80; mod_bytes[127] = 0x01; let n = <WideUCt as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&mod_bytes).unwrap();
1444 let n_params = ModMathParams::<WideUCt, Ct>::new(n).unwrap();
1445 let d = wrap_value(WideUCt::from(29u8));
1446 let e = wrap_value(WideUCt::from(5u8));
1447
1448 const CORRECT_K: usize = 64; const CONTAINER_K: usize = 128; let mut em = [0u8; CORRECT_K];
1455 let mut sig = [0u8; CORRECT_K];
1456 let result = sign_into(
1457 &n_params,
1458 &d,
1459 &e,
1460 &[],
1461 &[0u8; 20],
1462 CORRECT_K,
1463 &mut em,
1464 &mut sig,
1465 );
1466 assert!(
1467 !matches!(result, Err(Error::InvalidArguments)),
1468 "correct k (= modulus_bits.div_ceil(8)) must pass the width check, got {:?}",
1469 result
1470 );
1471
1472 let mut em = [0u8; CONTAINER_K];
1474 let mut sig = [0u8; CONTAINER_K];
1475 let result = sign_into(
1476 &n_params,
1477 &d,
1478 &e,
1479 &[],
1480 &[0u8; 20],
1481 CONTAINER_K,
1482 &mut em,
1483 &mut sig,
1484 );
1485 assert!(matches!(result, Err(Error::InvalidArguments)));
1486 }
1487
1488 #[test]
1491 fn pkcs1v15_signing_key_round_trip_2048_sha1() {
1492 use crate::key::{GenericRsaPrivateKey, GenericRsaPublicKey};
1493 use crate::pkcs1v15::{GenericSignature, GenericSigningKey, GenericVerifyingKey};
1494 use digest::Digest;
1495 use sha1::Sha1;
1496 use signature::hazmat::PrehashVerifier;
1497
1498 type U2048 = FixedUInt<u8, 256, Ct>;
1499 const K: usize = 256;
1500
1501 let public =
1502 crate::modmath_support::public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1503 let public_clone: GenericRsaPublicKey<ModMathValue<U2048>, ModMathParams<U2048, Ct>> =
1504 public.clone();
1505 let d = wrap_value(
1506 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1507 );
1508 let priv_key = GenericRsaPrivateKey::from_public_and_d(public, d);
1509
1510 let signing_key = GenericSigningKey::<Sha1, _, _>::new(priv_key);
1511 let verifying_key = GenericVerifyingKey::<Sha1, _, _>::new(public_clone);
1512
1513 let msg: &[u8] = b"deterministic test message";
1514 let mut em_storage = [0u8; K];
1515 let mut sig_storage = [0u8; K];
1516 let sig_slice = signing_key
1517 .try_sign_into(msg, &mut em_storage, &mut sig_storage)
1518 .unwrap();
1519 assert_eq!(sig_slice.len(), K);
1520
1521 let sig_int =
1524 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(sig_slice).unwrap();
1525 let sig = GenericSignature::from(wrap_value(sig_int));
1526 let digest = Sha1::digest(msg);
1527 verifying_key.verify_prehash(&digest, &sig).unwrap();
1528 }
1529
1530 #[test]
1531 fn pkcs1v15_signing_key_rejects_wrong_prehash_length() {
1532 use crate::key::GenericRsaPrivateKey;
1533 use crate::pkcs1v15::GenericSigningKey;
1534 use sha1::Sha1;
1535
1536 type U2048 = FixedUInt<u8, 256, Ct>;
1537 const K: usize = 256;
1538
1539 let public =
1540 crate::modmath_support::public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1541 let d = wrap_value(
1542 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1543 );
1544 let priv_key = GenericRsaPrivateKey::from_public_and_d(public, d);
1545 let signing_key = GenericSigningKey::<Sha1, _, _>::new(priv_key);
1546
1547 let bad_prehash = [0u8; 21]; let mut em_storage = [0u8; K];
1549 let mut sig_storage = [0u8; K];
1550 let result =
1551 signing_key.try_sign_prehash_into(&bad_prehash, &mut em_storage, &mut sig_storage);
1552 assert!(matches!(result, Err(Error::InputNotHashed)));
1553 }
1554
1555 #[test]
1556 fn pss_signing_key_round_trip_2048_sha1() {
1557 use crate::algorithms::pss::emsa_pss_verify;
1558 use crate::key::GenericRsaPrivateKey;
1559 use crate::pss::GenericSigningKey;
1560 use digest::Digest;
1561 use sha1::Sha1;
1562
1563 type U2048 = FixedUInt<u8, 256, Ct>;
1564 const K: usize = 256;
1565 const KEY_BITS: usize = 2048;
1566
1567 let key =
1568 crate::modmath_support::public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1569 let d = wrap_value(
1570 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1571 );
1572 let priv_key = GenericRsaPrivateKey::from_public_and_d(key.clone(), d);
1573 let signing_key = GenericSigningKey::<Sha1, _, _>::new_with_salt_len(priv_key, 0);
1575
1576 let msg: &[u8] = b"pss-roundtrip test message";
1577 let digest = Sha1::digest(msg);
1578 let mut em_storage = [0u8; K];
1579 let mut sig_storage = [0u8; K];
1580 let sig_slice = signing_key
1581 .try_sign_prehash_with_salt_into(&digest, &[], &mut em_storage, &mut sig_storage)
1582 .unwrap();
1583 assert_eq!(sig_slice.len(), K);
1584
1585 let recovered = public_key_op_ct(&key, sig_slice).unwrap();
1589 let mut em_copy = [0u8; K];
1590 em_copy.copy_from_slice(recovered.as_ref());
1591 let mut verify_hash = Sha1::new();
1592 emsa_pss_verify(&digest, &mut em_copy, Some(0), &mut verify_hash, KEY_BITS).unwrap();
1593 }
1594
1595 #[test]
1600 fn pss_signing_key_try_sign_prehash_with_rng_into_round_trip_2048_sha1() {
1601 use crate::algorithms::pss::emsa_pss_verify;
1602 use crate::key::GenericRsaPrivateKey;
1603 use crate::pss::GenericSigningKey;
1604 use digest::Digest;
1605 use rand::rngs::ChaCha8Rng;
1606 use rand_core::SeedableRng;
1607 use sha1::Sha1;
1608
1609 type U2048 = FixedUInt<u8, 256, Ct>;
1610 const K: usize = 256;
1611 const KEY_BITS: usize = 2048;
1612
1613 let key =
1614 crate::modmath_support::public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1615 let d = wrap_value(
1616 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1617 );
1618 let priv_key = GenericRsaPrivateKey::from_public_and_d(key.clone(), d);
1619 let signing_key = GenericSigningKey::<Sha1, _, _>::new_with_salt_len(priv_key, 0);
1621
1622 let msg: &[u8] = b"pss-blinded-roundtrip test message";
1623 let digest = Sha1::digest(msg);
1624 let mut rng = ChaCha8Rng::from_seed([42; 32]);
1625 let mut em_storage = [0u8; K];
1626 let mut sig_storage = [0u8; K];
1627 let mut salt_storage = [0u8; 0];
1628 let sig_slice = signing_key
1629 .try_sign_prehash_with_rng_into(
1630 &mut rng,
1631 &digest,
1632 &mut em_storage,
1633 &mut sig_storage,
1634 &mut salt_storage,
1635 )
1636 .unwrap();
1637 assert_eq!(sig_slice.len(), K);
1638
1639 let recovered = public_key_op_ct(&key, sig_slice).unwrap();
1640 let mut em_copy = [0u8; K];
1641 em_copy.copy_from_slice(recovered.as_ref());
1642 let mut verify_hash = Sha1::new();
1643 emsa_pss_verify(&digest, &mut em_copy, Some(0), &mut verify_hash, KEY_BITS).unwrap();
1644 }
1645
1646 #[test]
1647 fn pss_signing_key_rejects_wrong_prehash_length() {
1648 use crate::key::GenericRsaPrivateKey;
1649 use crate::pss::GenericSigningKey;
1650 use sha1::Sha1;
1651
1652 type U2048 = FixedUInt<u8, 256, Ct>;
1653 const K: usize = 256;
1654
1655 let key =
1656 crate::modmath_support::public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1657 let d = wrap_value(
1658 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1659 );
1660 let priv_key = GenericRsaPrivateKey::from_public_and_d(key, d);
1661 let signing_key = GenericSigningKey::<Sha1, _, _>::new_with_salt_len(priv_key, 0);
1662
1663 let bad_prehash = [0u8; 21]; let mut em_storage = [0u8; K];
1665 let mut sig_storage = [0u8; K];
1666 let result = signing_key.try_sign_prehash_with_salt_into(
1667 &bad_prehash,
1668 &[],
1669 &mut em_storage,
1670 &mut sig_storage,
1671 );
1672 assert!(matches!(result, Err(Error::InputNotHashed)));
1673 }
1674
1675 #[test]
1676 fn pss_signing_key_rejects_salt_len_mismatch() {
1677 use crate::key::GenericRsaPrivateKey;
1678 use crate::pss::GenericSigningKey;
1679 use sha1::Sha1;
1680
1681 type U2048 = FixedUInt<u8, 256, Ct>;
1682 const K: usize = 256;
1683
1684 let key =
1685 crate::modmath_support::public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1686 let d = wrap_value(
1687 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1688 );
1689 let priv_key = GenericRsaPrivateKey::from_public_and_d(key, d);
1690 let signing_key = GenericSigningKey::<Sha1, _, _>::new_with_salt_len(priv_key, 20);
1692
1693 let prehash = [0u8; 20];
1694 let wrong_salt = [0u8; 16];
1695 let mut em_storage = [0u8; K];
1696 let mut sig_storage = [0u8; K];
1697 let result = signing_key.try_sign_prehash_with_salt_into(
1698 &prehash,
1699 &wrong_salt,
1700 &mut em_storage,
1701 &mut sig_storage,
1702 );
1703 assert!(matches!(result, Err(Error::InvalidArguments)));
1704 }
1705
1706 #[test]
1707 fn pss_signing_key_satisfies_zeroize() {
1708 use crate::key::GenericRsaPrivateKey;
1709 use crate::pss::GenericSigningKey;
1710 use sha1::Sha1;
1711 fn assert_zeroize<Z: Zeroize>() {}
1712 assert_zeroize::<
1713 GenericSigningKey<Sha1, ModMathValue<SmallUCt>, ModMathParams<SmallUCt, Ct>>,
1714 >();
1715
1716 let public =
1717 crate::modmath_support::public_key_ct_from_be_bytes::<SmallUCt>(&[35u8], 5).unwrap();
1718 let priv_key =
1719 GenericRsaPrivateKey::from_public_and_d(public, wrap_value(SmallUCt::from(29u8)));
1720 let mut signing_key = GenericSigningKey::<Sha1, _, _>::new(priv_key);
1721 signing_key.zeroize();
1722 }
1723
1724 #[test]
1725 fn pkcs1v15_signing_key_satisfies_zeroize() {
1726 use crate::key::GenericRsaPrivateKey;
1727 use crate::pkcs1v15::GenericSigningKey;
1728 use sha1::Sha1;
1729 fn assert_zeroize<Z: Zeroize>() {}
1730 assert_zeroize::<
1731 GenericSigningKey<Sha1, ModMathValue<SmallUCt>, ModMathParams<SmallUCt, Ct>>,
1732 >();
1733
1734 let public =
1737 crate::modmath_support::public_key_ct_from_be_bytes::<SmallUCt>(&[35u8], 5).unwrap();
1738 let priv_key =
1739 GenericRsaPrivateKey::from_public_and_d(public, wrap_value(SmallUCt::from(29u8)));
1740 let mut signing_key = GenericSigningKey::<Sha1, _, _>::new(priv_key);
1741 signing_key.zeroize();
1742 }
1743
1744 #[test]
1745 fn generic_rsa_private_key_satisfies_traits() {
1746 use crate::key::GenericRsaPrivateKey;
1751 use crate::traits::keys::{PrivateKeyParts, PublicKeyParts};
1752 fn assert_pub_parts<K, T>(_: &K)
1753 where
1754 T: UnsignedModularInt,
1755 K: PublicKeyParts<T>,
1756 {
1757 }
1758 fn assert_priv_parts<K, T>(_: &K)
1759 where
1760 T: UnsignedModularInt,
1761 K: PrivateKeyParts<T>,
1762 {
1763 }
1764
1765 let public =
1768 crate::modmath_support::public_key_ct_from_be_bytes::<SmallUCt>(&[35u8], 5).unwrap();
1769 let key = GenericRsaPrivateKey::from_public_and_d(public, wrap_value(SmallUCt::from(29u8)));
1770
1771 assert_pub_parts::<_, ModMathValue<SmallUCt>>(&key);
1772 assert_priv_parts::<_, ModMathValue<SmallUCt>>(&key);
1773
1774 assert_eq!(PrivateKeyParts::d(&key), &wrap_value(SmallUCt::from(29u8)));
1776 assert_eq!(key.as_public().e(), &wrap_value(SmallUCt::from(5u8)));
1777 }
1778}