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_bytes < buf.len() && zero_bits_in_next > 0 {
312 buf[zero_bytes] &= 0xFFu8 >> zero_bits_in_next;
313 }
314 let candidate = <T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(bytes.as_ref())?;
315 let wrapped = wrap(candidate);
316 if wrapped < *modulus {
317 return Ok(wrapped);
318 }
319 }
320 Err(Error::Internal)
321}
322
323#[cfg(feature = "alloc")]
324impl<T> crate::traits::modular::TryRandomMod for ModMathValue<T>
325where
326 T: FixedWidthUnsignedInt + PartialOrd,
327{
328 fn try_random_mod<R>(rng: &mut R, modulus: &Self) -> Result<Self>
329 where
330 R: rand_core::TryCryptoRng + ?Sized,
331 {
332 let container_bits = <T as FixedWidthUnsignedInt>::bits_precision(&modulus.0);
333 let leading_zero_bits = <T as FixedWidthUnsignedInt>::leading_zeros(&modulus.0);
334 if leading_zero_bits >= container_bits {
335 return Err(Error::InvalidModulus);
336 }
337 try_random_mod_masked::<R, T, _, _>(rng, leading_zero_bits, modulus, wrap_value::<T>)
338 }
339}
340
341#[cfg(not(feature = "alloc"))]
342impl<T> crate::traits::modular::TryRandomMod for T
343where
344 T: FixedWidthUnsignedInt + PartialOrd,
345{
346 fn try_random_mod<R>(rng: &mut R, modulus: &Self) -> Result<Self>
347 where
348 R: rand_core::TryCryptoRng + ?Sized,
349 {
350 let container_bits = <T as FixedWidthUnsignedInt>::bits_precision(modulus);
351 let leading_zero_bits = <T as FixedWidthUnsignedInt>::leading_zeros(modulus);
352 if leading_zero_bits >= container_bits {
353 return Err(Error::InvalidModulus);
354 }
355 try_random_mod_masked::<R, T, T, _>(rng, leading_zero_bits, modulus, |x| x)
356 }
357}
358
359#[derive(Clone, Debug)]
360pub struct ModMathParams<T, P: Personality = Nct> {
361 field: ModmathField<T, P>,
365 modulus_odd: Odd<ModMathValue<T>>,
371}
372
373impl<T: ModMathInt + HasPersonality<P = Nct>> ModMathParams<T, Nct> {
374 pub fn new(modulus: T) -> Result<Self> {
375 let field = ModmathField::<T, Nct>::new(modulus).ok_or(Error::InvalidModulus)?;
376 let modulus_odd = Odd::new(wrap_value(modulus)).ok_or(Error::InvalidModulus)?;
377 Ok(Self { field, modulus_odd })
378 }
379}
380
381impl<T: ModMathIntCt + HasPersonality<P = Ct>> ModMathParams<T, Ct> {
382 pub fn new(modulus: T) -> Result<Self> {
385 let field = ModmathField::<T, Ct>::new(modulus).ok_or(Error::InvalidModulus)?;
386 let modulus_odd = Odd::new(wrap_value(modulus)).ok_or(Error::InvalidModulus)?;
387 Ok(Self { field, modulus_odd })
388 }
389}
390
391impl<T, P: Personality> ModMathParams<T, P> {
392 pub(crate) fn field(&self) -> &ModmathField<T, P> {
393 &self.field
394 }
395}
396
397pub fn public_key_from_be_bytes<T>(
400 modulus: &[u8],
401 exponent: u32,
402) -> Result<GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Nct>>>
403where
404 T: ModMathInt + HasPersonality<P = Nct>,
405{
406 let n = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
407 modulus,
408 )?);
409 let exponent = exponent.to_be_bytes();
410 let e = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
411 &exponent,
412 )?);
413 GenericRsaPublicKey::from_components(n, e, ModMathParams::<T, Nct>::new(unwrap_value(&n))?)
414}
415
416pub fn rsa_public_op<T>(
419 key: &GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Nct>>,
420 input: &[u8],
421) -> Result<<ModMathValue<T> as UnsignedModularInt>::Bytes>
422where
423 T: ModMathInt + HasPersonality<P = Nct>,
424{
425 let input = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
426 input,
427 )?);
428 Ok(rsa_encrypt(key, &input)?.to_be_bytes())
429}
430
431pub fn public_key_ct_from_be_bytes<T>(
436 modulus: &[u8],
437 exponent: u32,
438) -> Result<GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Ct>>>
439where
440 T: ModMathIntCt + HasPersonality<P = Ct>,
441{
442 let n = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
443 modulus,
444 )?);
445 let exponent = exponent.to_be_bytes();
446 let e = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
447 &exponent,
448 )?);
449 GenericRsaPublicKey::from_components(n, e, ModMathParams::<T, Ct>::new(unwrap_value(&n))?)
450}
451
452pub fn rsa_public_op_ct<T>(
453 key: &GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Ct>>,
454 input: &[u8],
455) -> Result<<ModMathValue<T> as UnsignedModularInt>::Bytes>
456where
457 T: ModMathIntCt + HasPersonality<P = Ct>,
458{
459 let input = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
460 input,
461 )?);
462 Ok(rsa_encrypt(key, &input)?.to_be_bytes())
463}
464
465#[derive(Clone, Debug)]
468pub struct ModMathForm<T, P: Personality = Nct>
469where
470 T: Clone + Zeroize,
471{
472 integer_mont: ModMathValue<T>,
473 params: ModMathParams<T, P>,
474}
475
476impl<T, P: Personality> Zeroize for ModMathForm<T, P>
478where
479 T: Clone + Zeroize,
480{
481 fn zeroize(&mut self) {
482 self.integer_mont.zeroize();
483 }
484}
485
486impl<T, P: Personality> Drop for ModMathForm<T, P>
487where
488 T: Clone + Zeroize,
489{
490 fn drop(&mut self) {
491 self.zeroize();
492 }
493}
494
495impl<T, P: Personality> zeroize::ZeroizeOnDrop for ModMathForm<T, P> where T: Clone + Zeroize {}
496
497impl<T: ModMathInt + HasPersonality<P = Nct>> IntoMontyForm<ModMathParams<T, Nct>>
498 for ModMathForm<T, Nct>
499{
500 fn from_reduced(integer: ModMathValue<T>, params: &ModMathParams<T, Nct>) -> Self {
501 let field = params.field();
502 let r = field.reduce(unwrap_value_ref(&integer));
503 Self {
504 integer_mont: wrap_value(*r.mont_value()),
505 params: params.clone(),
506 }
507 }
508
509 fn from_value(integer: ModMathValue<T>, params: &ModMathParams<T, Nct>) -> Self {
513 Self::from_reduced(integer, params)
514 }
515}
516
517impl<T: ModMathInt + HasPersonality<P = Nct>> ModMathForm<T, Nct> {
518 fn pow_loop(&self, exp_raw: T) -> T {
519 let field = self.params.field();
520 let base = field.residue_from_mont(unwrap_value(&self.integer_mont));
521 *field.exp(&base, &exp_raw).mont_value()
522 }
523
524 fn to_reduced(&self) -> T {
525 let field = self.params.field();
526 let r = field.residue_from_mont(unwrap_value(&self.integer_mont));
527 field.into_raw(&r)
528 }
529}
530
531impl<T: ModMathInt + HasPersonality<P = Nct>> Pow<ModMathParams<T, Nct>> for ModMathForm<T, Nct> {
532 fn pow(&self, exp: &ModMathValue<T>) -> Self {
533 let result_mont = self.pow_loop(unwrap_value(exp));
534 Self {
535 integer_mont: wrap_value(result_mont),
536 params: self.params.clone(),
537 }
538 }
539}
540
541impl<T: ModMathInt + HasPersonality<P = Nct>> PowBoundedExp<ModMathParams<T, Nct>>
542 for ModMathForm<T, Nct>
543{
544 fn pow_bounded_exp(&self, exp: &ModMathValue<T>, _exp_bits: u32) -> Self {
545 let result_mont = self.pow_loop(unwrap_value(exp));
548 Self {
549 integer_mont: wrap_value(result_mont),
550 params: self.params.clone(),
551 }
552 }
553
554 fn retrieve(&self) -> ModMathValue<T> {
555 wrap_value(self.to_reduced())
556 }
557}
558
559impl<T: ModMathInt + HasPersonality<P = Nct>> ModulusParams for ModMathParams<T, Nct> {
560 type Modulus = ModMathValue<T>;
561 type MontgomeryForm = ModMathForm<T, Nct>;
562
563 fn modulus(&self) -> &Odd<Self::Modulus> {
564 &self.modulus_odd
565 }
566
567 fn bits_precision(&self) -> u32 {
568 FixedWidthUnsignedInt::bits_precision(self.field.modulus())
569 }
570}
571
572impl<T: ModMathIntCt + HasPersonality<P = Ct>> IntoMontyForm<ModMathParams<T, Ct>>
573 for ModMathForm<T, Ct>
574{
575 fn from_reduced(integer: ModMathValue<T>, params: &ModMathParams<T, Ct>) -> Self {
576 let field = params.field();
577 let r = field.reduce(unwrap_value_ref(&integer));
578 Self {
579 integer_mont: wrap_value(*r.mont_value()),
580 params: params.clone(),
581 }
582 }
583
584 fn from_value(integer: ModMathValue<T>, params: &ModMathParams<T, Ct>) -> Self {
587 Self::from_reduced(integer, params)
588 }
589}
590
591impl<T: ModMathIntCt + HasPersonality<P = Ct>> ModMathForm<T, Ct> {
592 fn pow_loop_ct(&self, exp_raw: T) -> T {
597 let field = self.params.field();
598 let base = field.residue_from_mont(unwrap_value(&self.integer_mont));
599 *field.exp(&base, &exp_raw).mont_value()
600 }
601
602 fn pow_loop_public_exp(&self, exp_raw: T) -> T {
607 let field = self.params.field();
608 let base = field.residue_from_mont(unwrap_value(&self.integer_mont));
609 *field.exp_public_exp(&base, &exp_raw).mont_value()
610 }
611
612 fn to_reduced(&self) -> T {
613 let field = self.params.field();
614 let r = field.residue_from_mont(unwrap_value(&self.integer_mont));
615 field.into_raw(&r)
616 }
617}
618
619impl<T: ModMathIntCt + HasPersonality<P = Ct>> Pow<ModMathParams<T, Ct>> for ModMathForm<T, Ct> {
620 fn pow(&self, exp: &ModMathValue<T>) -> Self {
621 let result_mont = self.pow_loop_ct(unwrap_value(exp));
622 Self {
623 integer_mont: wrap_value(result_mont),
624 params: self.params.clone(),
625 }
626 }
627}
628
629impl<T: ModMathIntCt + HasPersonality<P = Ct>> PowBoundedExp<ModMathParams<T, Ct>>
630 for ModMathForm<T, Ct>
631{
632 fn pow_bounded_exp(&self, exp: &ModMathValue<T>, _exp_bits: u32) -> Self {
633 let result_mont = self.pow_loop_public_exp(unwrap_value(exp));
634 Self {
635 integer_mont: wrap_value(result_mont),
636 params: self.params.clone(),
637 }
638 }
639
640 fn retrieve(&self) -> ModMathValue<T> {
641 wrap_value(self.to_reduced())
642 }
643}
644
645impl<T> crate::traits::modular::InvertCt<ModMathParams<T, Ct>> for ModMathForm<T, Ct>
650where
651 T: ModMathIntCt
652 + HasPersonality<P = Ct>
653 + modmath_cios::CiosRowOps
654 + core::ops::BitOr<Output = T>,
655 <T as modmath_cios::CiosRowOps>::Word: const_num_traits::CtParity,
656{
657 fn invert_ct(&self) -> Option<Self> {
658 let field = self.params.field();
659 let residue = field.residue_from_mont(unwrap_value(&self.integer_mont));
660 let ct_option = field.inv_safegcd_ct(&residue);
661 ct_option.into_option().map(|inv_res| Self {
662 integer_mont: wrap_value(*inv_res.mont_value()),
663 params: self.params.clone(),
664 })
665 }
666}
667
668impl<T: ModMathIntCt + HasPersonality<P = Ct>> crate::traits::modular::MulCt<ModMathParams<T, Ct>>
673 for ModMathForm<T, Ct>
674{
675 fn mul_ct(&self, rhs: &Self) -> Self {
676 debug_assert!(
682 self.params.modulus_odd == rhs.params.modulus_odd,
683 "MulCt operands must share the same modulus"
684 );
685 let field = self.params.field();
686 let lhs_res = field.residue_from_mont(unwrap_value(&self.integer_mont));
687 let rhs_res = field.residue_from_mont(unwrap_value(&rhs.integer_mont));
688 let product = field.mul(&lhs_res, &rhs_res);
689 Self {
690 integer_mont: wrap_value(*product.mont_value()),
691 params: self.params.clone(),
692 }
693 }
694}
695
696impl<T: ModMathIntCt + HasPersonality<P = Ct>> ModulusParams for ModMathParams<T, Ct> {
697 type Modulus = ModMathValue<T>;
698 type MontgomeryForm = ModMathForm<T, Ct>;
699
700 fn modulus(&self) -> &Odd<Self::Modulus> {
701 &self.modulus_odd
702 }
703
704 fn bits_precision(&self) -> u32 {
705 FixedWidthUnsignedInt::bits_precision(self.field.modulus())
706 }
707}
708
709impl<T: ModMathIntCt + HasPersonality<P = Ct>> crate::traits::modular::sealed::CtModulusParamsSealed
715 for ModMathParams<T, Ct>
716{
717}
718impl<T: ModMathIntCt + HasPersonality<P = Ct>> crate::traits::modular::CtModulusParams
719 for ModMathParams<T, Ct>
720{
721}
722
723#[cfg(test)]
724#[cfg(feature = "alloc")]
725mod tests {
726 use const_num_traits::Ct;
727 use fixed_bigint::FixedUInt;
728 use rand::rngs::ChaCha8Rng;
729 use rand_core::SeedableRng;
730 use sha1::Sha1;
731 use signature::hazmat::PrehashVerifier;
732
733 use super::{
734 public_key_ct_from_be_bytes, public_key_from_be_bytes, ModMathForm, ModMathParams,
735 ModMathValue,
736 };
737 use crate::key::GenericRsaPublicKey;
738 use crate::pkcs1v15::{GenericEncryptingKey, GenericSignature, GenericVerifyingKey};
739 use crate::{traits::RandomizedEncryptor, BoxedUint, Pkcs1v15Encrypt, RsaPublicKey};
740
741 type SmallU = FixedUInt<u8, 64>;
742 type SmallUCt = FixedUInt<u8, 64, Ct>;
743
744 #[test]
745 fn brand_round_trip() {
746 let params = ModMathParams::<SmallU>::new(SmallU::from(13u8)).unwrap();
747 let f = params.field();
748 let r = f.reduce(&SmallU::from(7u8));
749 assert_eq!(f.into_raw(&r), SmallU::from(7u8));
750 }
751
752 #[test]
753 fn brand_mul_exp() {
754 let params = ModMathParams::<SmallU>::new(SmallU::from(13u8)).unwrap();
755 let f = params.field();
756 let a = f.reduce(&SmallU::from(7u8));
758 let b = f.reduce(&SmallU::from(11u8));
759 assert_eq!(f.into_raw(&f.mul(&a, &b)), SmallU::from(12u8));
760 let base = f.reduce(&SmallU::from(2u8));
762 assert_eq!(
763 f.into_raw(&f.exp(&base, &SmallU::from(10u8))),
764 SmallU::from(10u8)
765 );
766 }
767
768 #[test]
769 fn brand_ct_matches_nct() {
770 let p_nct = ModMathParams::<SmallU>::new(SmallU::from(13u8)).unwrap();
771 let p_ct = ModMathParams::<SmallUCt, Ct>::new(SmallUCt::from(13u8)).unwrap();
772 let f_nct = p_nct.field();
773 let f_ct = p_ct.field();
774 let nct = f_nct.into_raw(&f_nct.mul(
775 &f_nct.reduce(&SmallU::from(7u8)),
776 &f_nct.reduce(&SmallU::from(11u8)),
777 ));
778 let ct = f_ct.into_raw(&f_ct.mul(
779 &f_ct.reduce(&SmallUCt::from(7u8)),
780 &f_ct.reduce(&SmallUCt::from(11u8)),
781 ));
782 let mut nct_bytes = [0u8; 64];
784 let mut ct_bytes = [0u8; 64];
785 let _ = nct.to_be_bytes(&mut nct_bytes);
786 let _ = ct.to_be_bytes(&mut ct_bytes);
787 assert_eq!(nct_bytes, ct_bytes);
788 }
789
790 #[test]
791 fn mod_math_form_zeroize_on_drop() {
792 fn assert_zeroize_on_drop<T: zeroize::ZeroizeOnDrop>() {}
793 assert_zeroize_on_drop::<ModMathForm<SmallU>>();
794 assert_zeroize_on_drop::<ModMathForm<SmallUCt, Ct>>();
795 }
796
797 #[test]
798 fn verify_pkcs1v15_signature_with_modmath_fixed_uint() {
799 type U512 = FixedUInt<u8, 64>;
800
801 let digest: [u8; 20] = [
802 0x43, 0x0c, 0xe3, 0x4d, 0x02, 0x07, 0x24, 0xed, 0x75, 0xa1, 0x96, 0xdf, 0xc2, 0xad,
803 0x67, 0xc7, 0x77, 0x72, 0xd1, 0x69,
804 ];
805 let modulus: [u8; 64] = [
806 0x96, 0x9D, 0x03, 0xFF, 0xA9, 0x8D, 0x88, 0x8F, 0x3A, 0xA4, 0xF2, 0xFE, 0xD2, 0x32,
807 0xE6, 0x1C, 0x4A, 0xCF, 0x06, 0x63, 0xA9, 0x2F, 0x99, 0x03, 0x4C, 0xF7, 0xB7, 0x24,
808 0x5A, 0x1A, 0x1E, 0x5E, 0xAF, 0xA5, 0x65, 0xAF, 0xB9, 0x0B, 0xAB, 0x22, 0x85, 0x71,
809 0x2F, 0xAA, 0x50, 0x39, 0x39, 0xA0, 0x65, 0xFB, 0x60, 0xDD, 0x08, 0x28, 0xA3, 0x84,
810 0xF2, 0x6D, 0x8A, 0xFC, 0x28, 0x6D, 0xF6, 0xCF,
811 ];
812 let signature: [u8; 64] = [
813 0x45, 0x53, 0xF3, 0xAF, 0x16, 0xAF, 0x63, 0x97, 0xB0, 0xD3, 0x2F, 0x8A, 0xEC, 0xD5,
814 0x4C, 0xF1, 0xF3, 0xD0, 0x0C, 0x9F, 0x42, 0xDC, 0x68, 0xCB, 0xD7, 0x05, 0xCE, 0xA5,
815 0xA9, 0x70, 0x95, 0x3E, 0xC0, 0xBC, 0x4A, 0x18, 0xED, 0x91, 0xA3, 0x5D, 0x66, 0xEC,
816 0xDA, 0x4A, 0x83, 0x32, 0xCF, 0xC3, 0xA3, 0xAB, 0x21, 0xAD, 0x59, 0xB2, 0x2E, 0x87,
817 0xC2, 0x73, 0xFF, 0x08, 0x88, 0xDD, 0x4D, 0xE0,
818 ];
819
820 let key = public_key_from_be_bytes::<U512>(&modulus, 3).unwrap();
821 let verifying_key = GenericVerifyingKey::<Sha1, _, _>::new(key);
822 let signature =
823 GenericSignature::from(ModMathValue::from_inner(U512::from_be_bytes(&signature)));
824 verifying_key.verify_prehash(&digest, &signature).unwrap();
825 }
826
827 #[test]
828 fn verify_pkcs1v15_signature_with_modmath_fixed_uint32() {
829 type U512 = FixedUInt<u32, 16>;
830
831 let digest: [u8; 20] = [
832 0x43, 0x0c, 0xe3, 0x4d, 0x02, 0x07, 0x24, 0xed, 0x75, 0xa1, 0x96, 0xdf, 0xc2, 0xad,
833 0x67, 0xc7, 0x77, 0x72, 0xd1, 0x69,
834 ];
835 let modulus: [u8; 64] = [
836 0x96, 0x9D, 0x03, 0xFF, 0xA9, 0x8D, 0x88, 0x8F, 0x3A, 0xA4, 0xF2, 0xFE, 0xD2, 0x32,
837 0xE6, 0x1C, 0x4A, 0xCF, 0x06, 0x63, 0xA9, 0x2F, 0x99, 0x03, 0x4C, 0xF7, 0xB7, 0x24,
838 0x5A, 0x1A, 0x1E, 0x5E, 0xAF, 0xA5, 0x65, 0xAF, 0xB9, 0x0B, 0xAB, 0x22, 0x85, 0x71,
839 0x2F, 0xAA, 0x50, 0x39, 0x39, 0xA0, 0x65, 0xFB, 0x60, 0xDD, 0x08, 0x28, 0xA3, 0x84,
840 0xF2, 0x6D, 0x8A, 0xFC, 0x28, 0x6D, 0xF6, 0xCF,
841 ];
842 let signature: [u8; 64] = [
843 0x45, 0x53, 0xF3, 0xAF, 0x16, 0xAF, 0x63, 0x97, 0xB0, 0xD3, 0x2F, 0x8A, 0xEC, 0xD5,
844 0x4C, 0xF1, 0xF3, 0xD0, 0x0C, 0x9F, 0x42, 0xDC, 0x68, 0xCB, 0xD7, 0x05, 0xCE, 0xA5,
845 0xA9, 0x70, 0x95, 0x3E, 0xC0, 0xBC, 0x4A, 0x18, 0xED, 0x91, 0xA3, 0x5D, 0x66, 0xEC,
846 0xDA, 0x4A, 0x83, 0x32, 0xCF, 0xC3, 0xA3, 0xAB, 0x21, 0xAD, 0x59, 0xB2, 0x2E, 0x87,
847 0xC2, 0x73, 0xFF, 0x08, 0x88, 0xDD, 0x4D, 0xE0,
848 ];
849
850 let n = U512::from_be_bytes(&modulus);
851 let e = U512::from(3u8);
852 let key = GenericRsaPublicKey::from_components(
856 ModMathValue::from_inner(n),
857 ModMathValue::from_inner(e),
858 ModMathParams::<U512, const_num_traits::Nct>::new(n).unwrap(),
859 )
860 .unwrap();
861 let verifying_key = GenericVerifyingKey::<Sha1, _, _>::new(key);
862 let signature =
863 GenericSignature::from(ModMathValue::from_inner(U512::from_be_bytes(&signature)));
864 verifying_key.verify_prehash(&digest, &signature).unwrap();
865 }
866
867 #[test]
868 fn encrypt_pkcs1v15_with_modmath_fixed_uint_matches_boxeduint() {
869 type U512 = FixedUInt<u8, 64, Ct>;
873
874 let modulus: [u8; 64] = [
875 0x96, 0x9D, 0x03, 0xFF, 0xA9, 0x8D, 0x88, 0x8F, 0x3A, 0xA4, 0xF2, 0xFE, 0xD2, 0x32,
876 0xE6, 0x1C, 0x4A, 0xCF, 0x06, 0x63, 0xA9, 0x2F, 0x99, 0x03, 0x4C, 0xF7, 0xB7, 0x24,
877 0x5A, 0x1A, 0x1E, 0x5E, 0xAF, 0xA5, 0x65, 0xAF, 0xB9, 0x0B, 0xAB, 0x22, 0x85, 0x71,
878 0x2F, 0xAA, 0x50, 0x39, 0x39, 0xA0, 0x65, 0xFB, 0x60, 0xDD, 0x08, 0x28, 0xA3, 0x84,
879 0xF2, 0x6D, 0x8A, 0xFC, 0x28, 0x6D, 0xF6, 0xCF,
880 ];
881 let msg = b"hello world!";
882
883 let modmath_key = public_key_ct_from_be_bytes::<U512>(&modulus, 3).unwrap();
884 let boxed_key = RsaPublicKey::new(
885 BoxedUint::from_be_slice(&modulus, 512).unwrap(),
886 3u64.into(),
887 )
888 .unwrap();
889
890 let mut modmath_rng = ChaCha8Rng::from_seed([42; 32]);
891 let mut boxed_rng = ChaCha8Rng::from_seed([42; 32]);
892 let mut storage = [0u8; 64];
893
894 let modmath_ciphertext = GenericEncryptingKey::new(modmath_key)
895 .encrypt_with_rng_into(&mut modmath_rng, msg, &mut storage)
896 .unwrap();
897 let boxed_ciphertext = boxed_key
898 .encrypt(&mut boxed_rng, Pkcs1v15Encrypt, msg)
899 .unwrap();
900
901 assert_eq!(modmath_ciphertext, boxed_ciphertext.as_slice());
902 }
903}
904
905#[cfg(test)]
909mod private_op_tests {
910 use super::*;
911 use const_num_traits::Ct;
912 use fixed_bigint::FixedUInt;
913
914 type SmallUCt = FixedUInt<u8, 64, Ct>;
915
916 fn toy_params() -> ModMathParams<SmallUCt, Ct> {
919 ModMathParams::<SmallUCt, Ct>::new(SmallUCt::from(35u8)).unwrap()
920 }
921
922 fn toy_params_wide() -> ModMathParams<SmallUCt, Ct> {
929 let mut bytes = [0u8; 64];
930 bytes[0] = 0x80;
931 bytes[63] = 0x01;
932 let n = <SmallUCt as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&bytes).unwrap();
933 ModMathParams::<SmallUCt, Ct>::new(n).unwrap()
934 }
935
936 #[test]
937 fn rsa_private_op_round_trip_heapless_ct() {
938 let n_params = toy_params();
939 let c = wrap_value(SmallUCt::from(32u8));
940 let d = wrap_value(SmallUCt::from(29u8));
941 let expected = wrap_value(SmallUCt::from(2u8));
942 let recovered = crate::algorithms::rsa::rsa_private_op(&c, &d, &n_params);
943 assert_eq!(recovered, expected);
944 }
945
946 #[test]
952 fn rsa_private_op_blinded_matches_unblinded_heapless_ct() {
953 let n_params = toy_params();
954 let c = wrap_value(SmallUCt::from(32u8));
955 let d = wrap_value(SmallUCt::from(29u8));
956 let e = wrap_value(SmallUCt::from(5u8));
957 let r = wrap_value(SmallUCt::from(6u8));
958 let expected = wrap_value(SmallUCt::from(2u8));
959 let recovered =
960 crate::algorithms::rsa::rsa_private_op_blinded(&r, &c, &d, &e, &n_params).unwrap();
961 assert_eq!(recovered, expected);
962 }
963
964 #[test]
969 fn rsa_private_op_blinded_rejects_non_coprime_r() {
970 let n_params = toy_params();
971 let c = wrap_value(SmallUCt::from(32u8));
972 let d = wrap_value(SmallUCt::from(29u8));
973 let e = wrap_value(SmallUCt::from(5u8));
974 let r_bad = wrap_value(SmallUCt::from(5u8));
975 let result = crate::algorithms::rsa::rsa_private_op_blinded(&r_bad, &c, &d, &e, &n_params);
976 assert!(result.is_err());
977 }
978
979 #[test]
986 fn rsa_private_op_and_check_blinded_round_trip_heapless_ct() {
987 use rand::rngs::ChaCha8Rng;
988 use rand_core::SeedableRng;
989
990 let n_params = toy_params();
991 let c = wrap_value(SmallUCt::from(32u8));
992 let d = wrap_value(SmallUCt::from(29u8));
993 let e = wrap_value(SmallUCt::from(5u8));
994 let expected = wrap_value(SmallUCt::from(2u8));
995 let mut rng = ChaCha8Rng::from_seed([42; 32]);
996 let recovered = crate::algorithms::rsa::rsa_private_op_and_check_blinded(
997 &mut rng, &c, &d, &e, &n_params,
998 )
999 .unwrap();
1000 assert_eq!(recovered, expected);
1001 }
1002
1003 #[test]
1007 fn try_random_mod_modmath_stays_below_modulus() {
1008 use crate::traits::modular::TryRandomMod;
1009 use rand::rngs::ChaCha8Rng;
1010 use rand_core::SeedableRng;
1011
1012 let n_params = toy_params_wide();
1013 let n = *n_params.modulus().as_ref();
1014 let mut rng = ChaCha8Rng::from_seed([42; 32]);
1015
1016 let mut samples = [ModMathValue::<SmallUCt>::from(0u8); 16];
1019 for slot in samples.iter_mut() {
1020 let r = ModMathValue::<SmallUCt>::try_random_mod(&mut rng, &n).unwrap();
1021 assert!(r < n, "sample must be < modulus");
1022 *slot = r;
1023 }
1024 let first = samples[0];
1027 assert!(
1028 samples.iter().any(|s| *s != first),
1029 "samples are trivially all equal — RNG or sampler broken"
1030 );
1031 }
1032
1033 #[test]
1039 fn try_random_mod_modmath_succeeds_on_narrow_modulus_wide_carrier() {
1040 use crate::traits::modular::TryRandomMod;
1041 use rand::rngs::ChaCha8Rng;
1042 use rand_core::SeedableRng;
1043
1044 let n_params = toy_params(); let n = *n_params.modulus().as_ref();
1046 let mut rng = ChaCha8Rng::from_seed([42; 32]);
1047
1048 for _ in 0..64 {
1049 let r = ModMathValue::<SmallUCt>::try_random_mod(&mut rng, &n).unwrap();
1050 assert!(r < n);
1051 }
1052 }
1053
1054 #[test]
1055 fn rsa_private_op_and_check_round_trip_heapless_ct() {
1056 let n_params = toy_params();
1057 let c = wrap_value(SmallUCt::from(32u8));
1058 let d = wrap_value(SmallUCt::from(29u8));
1059 let e = wrap_value(SmallUCt::from(5u8));
1060 let expected = wrap_value(SmallUCt::from(2u8));
1061 let recovered =
1062 crate::algorithms::rsa::rsa_private_op_and_check(&c, &d, &e, &n_params).unwrap();
1063 assert_eq!(recovered, expected);
1064 }
1065
1066 #[test]
1070 fn invert_ct_modmath_known_answer() {
1071 use crate::traits::modular::{IntoMontyForm, InvertCt, PowBoundedExp};
1072 let n_params = toy_params();
1073 let three = wrap_value(SmallUCt::from(3u8));
1074 let mont_three = ModMathForm::<SmallUCt, Ct>::from_reduced(three, &n_params);
1075 let mont_inv = mont_three.invert_ct().expect("3 is coprime to 35");
1076 let recovered = PowBoundedExp::<ModMathParams<SmallUCt, Ct>>::retrieve(&mont_inv);
1077 assert_eq!(recovered, wrap_value(SmallUCt::from(12u8)));
1078 }
1079
1080 #[test]
1085 fn mul_ct_modmath_inverse_round_trip() {
1086 use crate::traits::modular::{IntoMontyForm, InvertCt, MulCt, PowBoundedExp};
1087 let n_params = toy_params();
1088 let three = wrap_value(SmallUCt::from(3u8));
1089 let mont_three = ModMathForm::<SmallUCt, Ct>::from_reduced(three, &n_params);
1090 let mont_inv = mont_three.invert_ct().expect("3 is coprime to 35");
1091 let product = mont_three.mul_ct(&mont_inv);
1092 let recovered = PowBoundedExp::<ModMathParams<SmallUCt, Ct>>::retrieve(&product);
1093 assert_eq!(recovered, wrap_value(SmallUCt::from(1u8)));
1094 }
1095
1096 #[test]
1097 fn rsa_private_op_and_check_rejects_wrong_exponent() {
1098 let n_params = toy_params();
1101 let c = wrap_value(SmallUCt::from(32u8));
1102 let bad_d = wrap_value(SmallUCt::from(11u8));
1103 let e = wrap_value(SmallUCt::from(5u8));
1104 let result = crate::algorithms::rsa::rsa_private_op_and_check(&c, &bad_d, &e, &n_params);
1105 assert!(result.is_err());
1106 }
1107
1108 const N_2048: [u8; 256] = hex_literal::hex!(
1113 "d397b84d98a4c26138ed1b695a8106ead91d553bf06041b62d3fdc50a041e222
1114 b8f4529689c1b82c5e71554f5dd69fa2f4b6158cf0dbeb57811a0fc327e1f28e
1115 74fe74d3bc166c1eabdc1b8b57b934ca8be5b00b4f29975bcc99acaf415b59bb
1116 28a6782bb41a2c3c2976b3c18dbadef62f00c6bb226640095096c0cc60d22fe7
1117 ef987d75c6a81b10d96bf292028af110dc7cc1bbc43d22adab379a0cd5d8078c
1118 c780ff5cd6209dea34c922cf784f7717e428d75b5aec8ff30e5f0141510766e2
1119 e0ab8d473c84e8710b2b98227c3db095337ad3452f19e2b9bfbccdd8148abf67
1120 76fa552775e6e75956e45229ae5a9c46949bab1e622f0e48f56524a84ed3483b"
1121 );
1122 const D_2048: [u8; 256] = hex_literal::hex!(
1123 "c4e70c689162c94c660828191b52b4d8392115df486a9adbe831e458d7395832
1124 0dc1b755456e93701e9702d76fb0b92f90e01d1fe248153281fe79aa9763a92f
1125 ae69d8d7ecd144de29fa135bd14f9573e349e45031e3b76982f583003826c552
1126 e89a397c1a06bd2163488630d92e8c2bb643d7abef700da95d685c941489a46f
1127 54b5316f62b5d2c3a7f1bbd134cb37353a44683fdc9d95d36458de22f6c44057
1128 fe74a0a436c4308f73f4da42f35c47ac16a7138d483afc91e41dc3a1127382e0
1129 c0f5119b0221b4fc639d6b9c38177a6de9b526ebd88c38d7982c07f98a0efd87
1130 7d508aae275b946915c02e2e1106d175d74ec6777f5e80d12c053d9c7be1e341"
1131 );
1132
1133 #[test]
1134 fn pkcs1v15_sign_into_round_trip_2048_sha1() {
1135 use crate::algorithms::pkcs1v15::{
1136 pkcs1v15_generate_prefix_into, pkcs1v15_sign_pad_into, sign_into,
1137 };
1138 use crate::traits::PublicKeyParts;
1139 use sha1::Sha1;
1140
1141 type U2048 = FixedUInt<u8, 256, Ct>;
1142 const K: usize = 256;
1143
1144 let key = public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1145 let d_int = <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap();
1146 let d = wrap_value(d_int);
1147 let e_int =
1148 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&[0x01, 0x00, 0x01])
1149 .unwrap();
1150 let e = wrap_value(e_int);
1151
1152 let digest = [0xAAu8; 20];
1153 let mut prefix_storage = [0u8; 32];
1154 let prefix = pkcs1v15_generate_prefix_into::<Sha1>(&mut prefix_storage).unwrap();
1155
1156 let mut em_storage = [0u8; K];
1157 let mut sig_storage = [0u8; K];
1158 let sig = sign_into(
1159 key.n_params(),
1160 &d,
1161 &e,
1162 prefix,
1163 &digest,
1164 K,
1165 &mut em_storage,
1166 &mut sig_storage,
1167 )
1168 .unwrap();
1169 assert_eq!(sig.len(), K);
1170
1171 let recovered = public_key_op_ct(&key, sig).unwrap();
1174 let mut expected_em_storage = [0u8; K];
1175 let expected_em =
1176 pkcs1v15_sign_pad_into(prefix, &digest, K, &mut expected_em_storage).unwrap();
1177 assert_eq!(recovered.as_ref(), expected_em);
1178 }
1179
1180 #[test]
1181 fn pss_sign_into_round_trip_2048_sha1() {
1182 use crate::algorithms::pss::{emsa_pss_verify, sign_into};
1183 use crate::traits::PublicKeyParts;
1184 use digest::Digest;
1185 use sha1::Sha1;
1186
1187 type U2048 = FixedUInt<u8, 256, Ct>;
1188 const K: usize = 256;
1189 const KEY_BITS: usize = 2048;
1190
1191 let key = public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1192 let d = wrap_value(
1193 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1194 );
1195 let e = wrap_value(
1196 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&[0x01, 0x00, 0x01])
1197 .unwrap(),
1198 );
1199
1200 let digest = [0xAAu8; 20];
1201 let salt: &[u8] = &[]; let mut hash = Sha1::new();
1203
1204 let mut em_storage = [0u8; K];
1205 let mut sig_storage = [0u8; K];
1206 let sig = sign_into(
1207 key.n_params(),
1208 &d,
1209 &e,
1210 &digest,
1211 salt,
1212 K,
1213 &mut hash,
1214 &mut em_storage,
1215 &mut sig_storage,
1216 )
1217 .unwrap();
1218 assert_eq!(sig.len(), K);
1219
1220 let recovered = public_key_op_ct(&key, sig).unwrap();
1224 let mut em_copy = [0u8; K];
1225 em_copy.copy_from_slice(recovered.as_ref());
1226 let mut verify_hash = Sha1::new();
1227 emsa_pss_verify(
1228 &digest,
1229 &mut em_copy,
1230 Some(salt.len()),
1231 &mut verify_hash,
1232 KEY_BITS,
1233 )
1234 .unwrap();
1235 }
1236
1237 fn public_key_op_ct<T>(
1239 key: &crate::key::GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Ct>>,
1240 input: &[u8],
1241 ) -> Result<<ModMathValue<T> as UnsignedModularInt>::Bytes>
1242 where
1243 T: ModMathIntCt + HasPersonality<P = Ct>,
1244 {
1245 crate::modmath_support::rsa_public_op_ct(key, input)
1246 }
1247
1248 fn dummy_de() -> (ModMathValue<SmallUCt>, ModMathValue<SmallUCt>) {
1255 (
1256 wrap_value(SmallUCt::from(1u8)),
1257 wrap_value(SmallUCt::from(1u8)),
1258 )
1259 }
1260
1261 const SMALL_K: usize = 64;
1263
1264 #[test]
1265 fn pkcs1v15_sign_into_rejects_wrong_k() {
1266 use crate::algorithms::pkcs1v15::sign_into;
1267 let n_params = toy_params();
1268 let (d, e) = dummy_de();
1269 let mut em = [0u8; SMALL_K];
1270 let mut sig = [0u8; SMALL_K];
1271 let result = sign_into(
1272 &n_params,
1273 &d,
1274 &e,
1275 &[],
1276 &[0u8; 20],
1277 SMALL_K - 1, &mut em,
1279 &mut sig,
1280 );
1281 assert!(matches!(result, Err(Error::InvalidArguments)));
1282 }
1283
1284 #[test]
1285 fn pkcs1v15_sign_into_rejects_small_sig_storage() {
1286 use crate::algorithms::pkcs1v15::sign_into;
1287 let n_params = toy_params_wide();
1288 let (d, e) = dummy_de();
1289 let mut em = [0u8; SMALL_K];
1290 let mut sig = [0u8; SMALL_K - 1]; let result = sign_into(
1292 &n_params,
1293 &d,
1294 &e,
1295 &[],
1296 &[0u8; 20],
1297 SMALL_K,
1298 &mut em,
1299 &mut sig,
1300 );
1301 assert!(matches!(result, Err(Error::OutputBufferTooSmall)));
1302 }
1303
1304 #[test]
1305 fn pkcs1v15_sign_into_propagates_message_too_long() {
1306 use crate::algorithms::pkcs1v15::sign_into;
1309 let n_params = toy_params_wide();
1310 let (d, e) = dummy_de();
1311 let mut em = [0u8; SMALL_K];
1312 let mut sig = [0u8; SMALL_K];
1313 let oversize_prefix = [0u8; SMALL_K]; let result = sign_into(
1315 &n_params,
1316 &d,
1317 &e,
1318 &oversize_prefix,
1319 &[0u8; 20],
1320 SMALL_K,
1321 &mut em,
1322 &mut sig,
1323 );
1324 assert!(matches!(result, Err(Error::MessageTooLong)));
1325 }
1326
1327 #[test]
1328 fn pss_sign_into_rejects_wrong_k() {
1329 use crate::algorithms::pss::sign_into;
1330 use digest::Digest;
1331 use sha1::Sha1;
1332 let n_params = toy_params_wide();
1333 let (d, e) = dummy_de();
1334 let mut em = [0u8; SMALL_K];
1335 let mut sig = [0u8; SMALL_K];
1336 let mut hash = Sha1::new();
1337 let result = sign_into(
1338 &n_params,
1339 &d,
1340 &e,
1341 &[0u8; 20],
1342 &[],
1343 SMALL_K - 1, &mut hash,
1345 &mut em,
1346 &mut sig,
1347 );
1348 assert!(matches!(result, Err(Error::InvalidArguments)));
1349 }
1350
1351 #[test]
1352 fn pss_sign_into_rejects_small_sig_storage() {
1353 use crate::algorithms::pss::sign_into;
1354 use digest::Digest;
1355 use sha1::Sha1;
1356 let n_params = toy_params_wide();
1357 let (d, e) = dummy_de();
1358 let mut em = [0u8; SMALL_K];
1359 let mut sig = [0u8; SMALL_K - 1];
1360 let mut hash = Sha1::new();
1361 let result = sign_into(
1362 &n_params,
1363 &d,
1364 &e,
1365 &[0u8; 20],
1366 &[],
1367 SMALL_K,
1368 &mut hash,
1369 &mut em,
1370 &mut sig,
1371 );
1372 assert!(matches!(result, Err(Error::OutputBufferTooSmall)));
1373 }
1374
1375 #[test]
1376 fn pss_sign_into_rejects_small_em_storage() {
1377 use crate::algorithms::pss::sign_into;
1378 use digest::Digest;
1379 use sha1::Sha1;
1380 let n_params = toy_params_wide();
1381 let (d, e) = dummy_de();
1382 let mut em = [0u8; SMALL_K - 1];
1384 let mut sig = [0u8; SMALL_K];
1385 let mut hash = Sha1::new();
1386 let result = sign_into(
1387 &n_params,
1388 &d,
1389 &e,
1390 &[0u8; 20],
1391 &[],
1392 SMALL_K,
1393 &mut hash,
1394 &mut em,
1395 &mut sig,
1396 );
1397 assert!(matches!(result, Err(Error::OutputBufferTooSmall)));
1398 }
1399
1400 #[test]
1401 fn pss_sign_into_rejects_wrong_hash_length() {
1402 use crate::algorithms::pss::sign_into;
1405 use digest::Digest;
1406 use sha1::Sha1;
1407 let n_params = toy_params_wide();
1408 let (d, e) = dummy_de();
1409 let mut em = [0u8; SMALL_K];
1410 let mut sig = [0u8; SMALL_K];
1411 let mut hash = Sha1::new();
1412 let result = sign_into(
1413 &n_params,
1414 &d,
1415 &e,
1416 &[0u8; 21], &[],
1418 SMALL_K,
1419 &mut hash,
1420 &mut em,
1421 &mut sig,
1422 );
1423 assert!(matches!(result, Err(Error::InputNotHashed)));
1424 }
1425
1426 #[test]
1431 fn pkcs1v15_sign_into_k_uses_modulus_bits_not_container() {
1432 use crate::algorithms::pkcs1v15::sign_into;
1433 type WideUCt = FixedUInt<u8, 128, Ct>;
1435 let mut mod_bytes = [0u8; 128];
1436 mod_bytes[64] = 0x80; mod_bytes[127] = 0x01; let n = <WideUCt as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&mod_bytes).unwrap();
1439 let n_params = ModMathParams::<WideUCt, Ct>::new(n).unwrap();
1440 let d = wrap_value(WideUCt::from(29u8));
1441 let e = wrap_value(WideUCt::from(5u8));
1442
1443 const CORRECT_K: usize = 64; const CONTAINER_K: usize = 128; let mut em = [0u8; CORRECT_K];
1450 let mut sig = [0u8; CORRECT_K];
1451 let result = sign_into(
1452 &n_params,
1453 &d,
1454 &e,
1455 &[],
1456 &[0u8; 20],
1457 CORRECT_K,
1458 &mut em,
1459 &mut sig,
1460 );
1461 assert!(
1462 !matches!(result, Err(Error::InvalidArguments)),
1463 "correct k (= modulus_bits.div_ceil(8)) must pass the width check, got {:?}",
1464 result
1465 );
1466
1467 let mut em = [0u8; CONTAINER_K];
1469 let mut sig = [0u8; CONTAINER_K];
1470 let result = sign_into(
1471 &n_params,
1472 &d,
1473 &e,
1474 &[],
1475 &[0u8; 20],
1476 CONTAINER_K,
1477 &mut em,
1478 &mut sig,
1479 );
1480 assert!(matches!(result, Err(Error::InvalidArguments)));
1481 }
1482
1483 #[test]
1486 fn pkcs1v15_signing_key_round_trip_2048_sha1() {
1487 use crate::key::{GenericRsaPrivateKey, GenericRsaPublicKey};
1488 use crate::pkcs1v15::{GenericSignature, GenericSigningKey, GenericVerifyingKey};
1489 use digest::Digest;
1490 use sha1::Sha1;
1491 use signature::hazmat::PrehashVerifier;
1492
1493 type U2048 = FixedUInt<u8, 256, Ct>;
1494 const K: usize = 256;
1495
1496 let public =
1497 crate::modmath_support::public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1498 let public_clone: GenericRsaPublicKey<ModMathValue<U2048>, ModMathParams<U2048, Ct>> =
1499 public.clone();
1500 let d = wrap_value(
1501 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1502 );
1503 let priv_key = GenericRsaPrivateKey::from_public_and_d(public, d);
1504
1505 let signing_key = GenericSigningKey::<Sha1, _, _>::new(priv_key);
1506 let verifying_key = GenericVerifyingKey::<Sha1, _, _>::new(public_clone);
1507
1508 let msg: &[u8] = b"deterministic test message";
1509 let mut em_storage = [0u8; K];
1510 let mut sig_storage = [0u8; K];
1511 let sig_slice = signing_key
1512 .try_sign_into(msg, &mut em_storage, &mut sig_storage)
1513 .unwrap();
1514 assert_eq!(sig_slice.len(), K);
1515
1516 let sig_int =
1519 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(sig_slice).unwrap();
1520 let sig = GenericSignature::from(wrap_value(sig_int));
1521 let digest = Sha1::digest(msg);
1522 verifying_key.verify_prehash(&digest, &sig).unwrap();
1523 }
1524
1525 #[test]
1526 fn pkcs1v15_signing_key_rejects_wrong_prehash_length() {
1527 use crate::key::GenericRsaPrivateKey;
1528 use crate::pkcs1v15::GenericSigningKey;
1529 use sha1::Sha1;
1530
1531 type U2048 = FixedUInt<u8, 256, Ct>;
1532 const K: usize = 256;
1533
1534 let public =
1535 crate::modmath_support::public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1536 let d = wrap_value(
1537 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1538 );
1539 let priv_key = GenericRsaPrivateKey::from_public_and_d(public, d);
1540 let signing_key = GenericSigningKey::<Sha1, _, _>::new(priv_key);
1541
1542 let bad_prehash = [0u8; 21]; let mut em_storage = [0u8; K];
1544 let mut sig_storage = [0u8; K];
1545 let result =
1546 signing_key.try_sign_prehash_into(&bad_prehash, &mut em_storage, &mut sig_storage);
1547 assert!(matches!(result, Err(Error::InputNotHashed)));
1548 }
1549
1550 #[test]
1551 fn pss_signing_key_round_trip_2048_sha1() {
1552 use crate::algorithms::pss::emsa_pss_verify;
1553 use crate::key::GenericRsaPrivateKey;
1554 use crate::pss::GenericSigningKey;
1555 use digest::Digest;
1556 use sha1::Sha1;
1557
1558 type U2048 = FixedUInt<u8, 256, Ct>;
1559 const K: usize = 256;
1560 const KEY_BITS: usize = 2048;
1561
1562 let key =
1563 crate::modmath_support::public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1564 let d = wrap_value(
1565 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1566 );
1567 let priv_key = GenericRsaPrivateKey::from_public_and_d(key.clone(), d);
1568 let signing_key = GenericSigningKey::<Sha1, _, _>::new_with_salt_len(priv_key, 0);
1570
1571 let msg: &[u8] = b"pss-roundtrip test message";
1572 let digest = Sha1::digest(msg);
1573 let mut em_storage = [0u8; K];
1574 let mut sig_storage = [0u8; K];
1575 let sig_slice = signing_key
1576 .try_sign_prehash_with_salt_into(&digest, &[], &mut em_storage, &mut sig_storage)
1577 .unwrap();
1578 assert_eq!(sig_slice.len(), K);
1579
1580 let recovered = public_key_op_ct(&key, sig_slice).unwrap();
1584 let mut em_copy = [0u8; K];
1585 em_copy.copy_from_slice(recovered.as_ref());
1586 let mut verify_hash = Sha1::new();
1587 emsa_pss_verify(&digest, &mut em_copy, Some(0), &mut verify_hash, KEY_BITS).unwrap();
1588 }
1589
1590 #[test]
1595 fn pss_signing_key_try_sign_prehash_with_rng_into_round_trip_2048_sha1() {
1596 use crate::algorithms::pss::emsa_pss_verify;
1597 use crate::key::GenericRsaPrivateKey;
1598 use crate::pss::GenericSigningKey;
1599 use digest::Digest;
1600 use rand::rngs::ChaCha8Rng;
1601 use rand_core::SeedableRng;
1602 use sha1::Sha1;
1603
1604 type U2048 = FixedUInt<u8, 256, Ct>;
1605 const K: usize = 256;
1606 const KEY_BITS: usize = 2048;
1607
1608 let key =
1609 crate::modmath_support::public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1610 let d = wrap_value(
1611 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1612 );
1613 let priv_key = GenericRsaPrivateKey::from_public_and_d(key.clone(), d);
1614 let signing_key = GenericSigningKey::<Sha1, _, _>::new_with_salt_len(priv_key, 0);
1616
1617 let msg: &[u8] = b"pss-blinded-roundtrip test message";
1618 let digest = Sha1::digest(msg);
1619 let mut rng = ChaCha8Rng::from_seed([42; 32]);
1620 let mut em_storage = [0u8; K];
1621 let mut sig_storage = [0u8; K];
1622 let mut salt_storage = [0u8; 0];
1623 let sig_slice = signing_key
1624 .try_sign_prehash_with_rng_into(
1625 &mut rng,
1626 &digest,
1627 &mut em_storage,
1628 &mut sig_storage,
1629 &mut salt_storage,
1630 )
1631 .unwrap();
1632 assert_eq!(sig_slice.len(), K);
1633
1634 let recovered = public_key_op_ct(&key, sig_slice).unwrap();
1635 let mut em_copy = [0u8; K];
1636 em_copy.copy_from_slice(recovered.as_ref());
1637 let mut verify_hash = Sha1::new();
1638 emsa_pss_verify(&digest, &mut em_copy, Some(0), &mut verify_hash, KEY_BITS).unwrap();
1639 }
1640
1641 #[test]
1642 fn pss_signing_key_rejects_wrong_prehash_length() {
1643 use crate::key::GenericRsaPrivateKey;
1644 use crate::pss::GenericSigningKey;
1645 use sha1::Sha1;
1646
1647 type U2048 = FixedUInt<u8, 256, Ct>;
1648 const K: usize = 256;
1649
1650 let key =
1651 crate::modmath_support::public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1652 let d = wrap_value(
1653 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1654 );
1655 let priv_key = GenericRsaPrivateKey::from_public_and_d(key, d);
1656 let signing_key = GenericSigningKey::<Sha1, _, _>::new_with_salt_len(priv_key, 0);
1657
1658 let bad_prehash = [0u8; 21]; let mut em_storage = [0u8; K];
1660 let mut sig_storage = [0u8; K];
1661 let result = signing_key.try_sign_prehash_with_salt_into(
1662 &bad_prehash,
1663 &[],
1664 &mut em_storage,
1665 &mut sig_storage,
1666 );
1667 assert!(matches!(result, Err(Error::InputNotHashed)));
1668 }
1669
1670 #[test]
1671 fn pss_signing_key_rejects_salt_len_mismatch() {
1672 use crate::key::GenericRsaPrivateKey;
1673 use crate::pss::GenericSigningKey;
1674 use sha1::Sha1;
1675
1676 type U2048 = FixedUInt<u8, 256, Ct>;
1677 const K: usize = 256;
1678
1679 let key =
1680 crate::modmath_support::public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1681 let d = wrap_value(
1682 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1683 );
1684 let priv_key = GenericRsaPrivateKey::from_public_and_d(key, d);
1685 let signing_key = GenericSigningKey::<Sha1, _, _>::new_with_salt_len(priv_key, 20);
1687
1688 let prehash = [0u8; 20];
1689 let wrong_salt = [0u8; 16];
1690 let mut em_storage = [0u8; K];
1691 let mut sig_storage = [0u8; K];
1692 let result = signing_key.try_sign_prehash_with_salt_into(
1693 &prehash,
1694 &wrong_salt,
1695 &mut em_storage,
1696 &mut sig_storage,
1697 );
1698 assert!(matches!(result, Err(Error::InvalidArguments)));
1699 }
1700
1701 #[test]
1702 fn pss_signing_key_satisfies_zeroize() {
1703 use crate::key::GenericRsaPrivateKey;
1704 use crate::pss::GenericSigningKey;
1705 use sha1::Sha1;
1706 fn assert_zeroize<Z: Zeroize>() {}
1707 assert_zeroize::<
1708 GenericSigningKey<Sha1, ModMathValue<SmallUCt>, ModMathParams<SmallUCt, Ct>>,
1709 >();
1710
1711 let public =
1712 crate::modmath_support::public_key_ct_from_be_bytes::<SmallUCt>(&[35u8], 5).unwrap();
1713 let priv_key =
1714 GenericRsaPrivateKey::from_public_and_d(public, wrap_value(SmallUCt::from(29u8)));
1715 let mut signing_key = GenericSigningKey::<Sha1, _, _>::new(priv_key);
1716 signing_key.zeroize();
1717 }
1718
1719 #[test]
1720 fn pkcs1v15_signing_key_satisfies_zeroize() {
1721 use crate::key::GenericRsaPrivateKey;
1722 use crate::pkcs1v15::GenericSigningKey;
1723 use sha1::Sha1;
1724 fn assert_zeroize<Z: Zeroize>() {}
1725 assert_zeroize::<
1726 GenericSigningKey<Sha1, ModMathValue<SmallUCt>, ModMathParams<SmallUCt, Ct>>,
1727 >();
1728
1729 let public =
1732 crate::modmath_support::public_key_ct_from_be_bytes::<SmallUCt>(&[35u8], 5).unwrap();
1733 let priv_key =
1734 GenericRsaPrivateKey::from_public_and_d(public, wrap_value(SmallUCt::from(29u8)));
1735 let mut signing_key = GenericSigningKey::<Sha1, _, _>::new(priv_key);
1736 signing_key.zeroize();
1737 }
1738
1739 #[test]
1740 fn generic_rsa_private_key_satisfies_traits() {
1741 use crate::key::GenericRsaPrivateKey;
1746 use crate::traits::keys::{PrivateKeyParts, PublicKeyParts};
1747 fn assert_pub_parts<K, T>(_: &K)
1748 where
1749 T: UnsignedModularInt,
1750 K: PublicKeyParts<T>,
1751 {
1752 }
1753 fn assert_priv_parts<K, T>(_: &K)
1754 where
1755 T: UnsignedModularInt,
1756 K: PrivateKeyParts<T>,
1757 {
1758 }
1759
1760 let public =
1763 crate::modmath_support::public_key_ct_from_be_bytes::<SmallUCt>(&[35u8], 5).unwrap();
1764 let key = GenericRsaPrivateKey::from_public_and_d(public, wrap_value(SmallUCt::from(29u8)));
1765
1766 assert_pub_parts::<_, ModMathValue<SmallUCt>>(&key);
1767 assert_priv_parts::<_, ModMathValue<SmallUCt>>(&key);
1768
1769 assert_eq!(PrivateKeyParts::d(&key), &wrap_value(SmallUCt::from(29u8)));
1771 assert_eq!(key.as_public().e(), &wrap_value(SmallUCt::from(5u8)));
1772 }
1773}