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::FromByteSlice;
14use const_num_traits::WithPrecision;
15use const_num_traits::{Ct, HasPersonality, Nct, Personality};
16use const_num_traits::{One, Zero};
17use modmath::{CiosMontMul, CiosMontMulCt, Field as ModmathField, Parity, WideMul};
18use zeroize::Zeroize;
19
20use crate::{
21 algorithms::rsa::rsa_encrypt,
22 errors::{Error, Result},
23 key::GenericRsaPublicKey,
24 traits::modular::{
25 FixedWidthUnsignedInt, IntegerResize, IntoMontyForm, ModulusParams, NonZero, Odd, Pow,
26 PowBoundedExp, TryFromBeBytes, UnsignedModularInt,
27 },
28};
29
30pub trait ModMathInt:
31 FixedWidthUnsignedInt
32 + From<u8>
33 + PartialEq
34 + PartialOrd
35 + One
36 + Zero
37 + Parity
38 + OverflowingAdd<Output = Self>
39 + WideMul
40 + CiosMontMul
41 + WrappingAdd<Output = Self>
42 + WrappingMul<Output = Self>
43 + WrappingSub<Output = Self>
44 + Shr<usize, Output = Self>
45 + ShrAssign<usize>
46 + HasPersonality
47{
48}
49
50impl<T> ModMathInt for T where
51 T: FixedWidthUnsignedInt
52 + From<u8>
53 + PartialEq
54 + PartialOrd
55 + One
56 + Zero
57 + Parity
58 + OverflowingAdd<Output = Self>
59 + WideMul
60 + CiosMontMul
61 + WrappingAdd<Output = Self>
62 + WrappingMul<Output = Self>
63 + WrappingSub<Output = Self>
64 + Shr<usize, Output = Self>
65 + ShrAssign<usize>
66 + HasPersonality
67{
68}
69
70pub trait ModMathIntCt:
71 FixedWidthUnsignedInt
72 + From<u8>
73 + PartialEq
74 + PartialOrd
75 + One
76 + Zero
77 + Parity
78 + OverflowingAdd<Output = Self>
79 + WideMul
80 + CiosMontMulCt
81 + WrappingAdd<Output = Self>
82 + WrappingMul<Output = Self>
83 + WrappingSub<Output = Self>
84 + Shr<usize, Output = Self>
85 + ShrAssign<usize>
86 + subtle::ConditionallySelectable
87 + subtle::ConstantTimeLess
88 + core::ops::BitAnd<Output = Self>
89 + HasPersonality
90 + const_num_traits::CtIsZero
91{
92}
93
94impl<T> ModMathIntCt for T where
95 T: FixedWidthUnsignedInt
96 + From<u8>
97 + PartialEq
98 + PartialOrd
99 + One
100 + Zero
101 + Parity
102 + OverflowingAdd<Output = Self>
103 + WideMul
104 + CiosMontMulCt
105 + WrappingAdd<Output = Self>
106 + WrappingMul<Output = Self>
107 + WrappingSub<Output = Self>
108 + Shr<usize, Output = Self>
109 + ShrAssign<usize>
110 + subtle::ConditionallySelectable
111 + subtle::ConstantTimeLess
112 + core::ops::BitAnd<Output = Self>
113 + HasPersonality
114 + const_num_traits::CtIsZero
115{
116}
117
118#[cfg(feature = "alloc")]
119fn wrap_value<T>(value: T) -> ModMathValue<T> {
120 ModMathValue(value)
121}
122
123#[cfg(not(feature = "alloc"))]
124fn wrap_value<T>(value: T) -> ModMathValue<T> {
125 value
126}
127
128#[cfg(feature = "alloc")]
136pub fn wrap<T>(value: T) -> ModMathValue<T> {
137 ModMathValue(value)
138}
139
140#[cfg(not(feature = "alloc"))]
142pub fn wrap<T>(value: T) -> ModMathValue<T> {
143 value
144}
145
146#[cfg(feature = "alloc")]
151pub fn into_inner<T>(value: ModMathValue<T>) -> T {
152 value.0
153}
154
155#[cfg(not(feature = "alloc"))]
157pub fn into_inner<T>(value: ModMathValue<T>) -> T {
158 value
159}
160
161#[cfg(feature = "alloc")]
162fn unwrap_value<T: Copy>(value: &ModMathValue<T>) -> T {
163 value.0
164}
165
166#[cfg(feature = "alloc")]
167fn unwrap_value_ref<T>(value: &ModMathValue<T>) -> &T {
168 &value.0
169}
170
171#[cfg(not(feature = "alloc"))]
172fn unwrap_value_ref<T>(value: &ModMathValue<T>) -> &T {
173 value
174}
175
176#[cfg(not(feature = "alloc"))]
177fn unwrap_value<T: Copy>(value: &ModMathValue<T>) -> T {
178 *value
179}
180
181#[cfg(feature = "alloc")]
182#[repr(transparent)]
183#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
184pub struct ModMathValue<T>(pub T);
185
186#[cfg(feature = "alloc")]
187impl<T> ModMathValue<T> {
188 pub fn from_inner(inner: T) -> Self {
189 Self(inner)
190 }
191
192 pub fn inner(&self) -> &T {
193 &self.0
194 }
195}
196
197#[cfg(feature = "alloc")]
198impl<T> Zeroize for ModMathValue<T>
199where
200 T: Zeroize,
201{
202 fn zeroize(&mut self) {
203 self.0.zeroize();
204 }
205}
206
207#[cfg(feature = "alloc")]
208impl<T> From<u8> for ModMathValue<T>
209where
210 T: From<u8>,
211{
212 fn from(value: u8) -> Self {
213 Self(<T as From<u8>>::from(value))
214 }
215}
216
217#[cfg(feature = "alloc")]
218impl<T> IntegerResize for ModMathValue<T>
219where
220 T: FixedWidthUnsignedInt + PartialOrd,
221{
222 type Output = Self;
223
224 fn resize_unchecked(self, at_least_bits_precision: u32) -> Self::Output {
225 Self(WithPrecision::widen_to_precision(
232 self.0,
233 at_least_bits_precision,
234 ))
235 }
236
237 fn try_resize(self, at_least_bits_precision: u32) -> Option<Self::Output> {
238 let value_bits = self.0.bit_length();
241 if value_bits <= at_least_bits_precision {
242 Some(Self(WithPrecision::widen_to_precision(
243 self.0,
244 at_least_bits_precision,
245 )))
246 } else {
247 None
248 }
249 }
250}
251
252#[cfg(feature = "alloc")]
253impl<T> UnsignedModularInt for ModMathValue<T>
254where
255 T: FixedWidthUnsignedInt + PartialOrd,
256{
257 type Bytes = <T as FixedWidthUnsignedInt>::Bytes;
258
259 fn to_be_bytes(&self) -> Self::Bytes {
260 FixedWidthUnsignedInt::to_be_bytes(&self.0)
261 }
262
263 #[cfg(feature = "alloc")]
264 fn to_be_bytes_trimmed_vartime(&self) -> Box<[u8]> {
265 let bytes = self.to_be_bytes();
266 let bytes = bytes.as_ref();
267 let first_non_zero = bytes
268 .iter()
269 .position(|b| *b != 0)
270 .unwrap_or(bytes.len().saturating_sub(1));
271 bytes[first_non_zero..].to_vec().into_boxed_slice()
272 }
273
274 fn as_nz_ref(&self) -> NonZero<Self> {
275 NonZero::new(*self).expect("value is non-zero")
276 }
277
278 fn bits(&self) -> u32 {
279 FixedWidthUnsignedInt::bit_length(&self.0)
280 }
281
282 fn bits_precision(&self) -> u32 {
283 FixedWidthUnsignedInt::bits_precision(&self.0)
284 }
285}
286
287#[cfg(feature = "alloc")]
288impl<T> TryFromBeBytes for ModMathValue<T>
289where
290 T: FixedWidthUnsignedInt,
291{
292 fn try_from_be_bytes_vartime(bytes: &[u8]) -> Result<Self> {
293 Ok(Self(
294 <T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(bytes)?,
295 ))
296 }
297}
298
299#[cfg(feature = "alloc")]
304impl<T> crate::traits::keys::RawPrivateKeyConstructible for ModMathValue<T> where
305 T: FixedWidthUnsignedInt + PartialOrd
306{
307}
308
309#[cfg(not(feature = "alloc"))]
310pub type ModMathValue<T> = T;
311
312#[cfg(feature = "modmath")]
327fn try_random_mod_masked<R, T, W, F>(
328 rng: &mut R,
329 modulus_bits: u32,
330 leading_zero_bits: u32,
331 modulus: &W,
332 wrap: F,
333) -> Result<W>
334where
335 R: rand_core::TryCryptoRng + ?Sized,
336 T: FixedWidthUnsignedInt,
337 W: PartialOrd,
338 F: Fn(T) -> W,
339{
340 let zero_bytes = (leading_zero_bits / 8) as usize;
341 let zero_bits_in_next = (leading_zero_bits % 8) as u8;
342
343 const MAX_TRIES: u32 = 128;
344 let mut bytes = <T as FixedWidthUnsignedInt>::Bytes::default();
345 let window_bytes = (modulus_bits as usize).div_ceil(8);
352 let lo = bytes.as_ref().len().saturating_sub(window_bytes);
353 for _ in 0..MAX_TRIES {
354 rng.try_fill_bytes(bytes.as_mut()).map_err(|_| Error::Rng)?;
355 let buf = bytes.as_mut();
360 for byte in buf.iter_mut().skip(lo).take(zero_bytes) {
361 *byte = 0;
362 }
363 if zero_bits_in_next > 0 {
364 if let Some(b) = buf.get_mut(lo + zero_bytes) {
365 *b &= 0xFFu8 >> zero_bits_in_next;
366 }
367 }
368 let window = bytes.as_ref().get(lo..).ok_or(Error::Internal)?;
369 let candidate = <T as FromByteSlice>::from_be_slice(window).map_err(|_| Error::Internal)?;
377 let wrapped = wrap(candidate);
378 if wrapped < *modulus {
379 return Ok(wrapped);
380 }
381 }
382 Err(Error::Internal)
383}
384
385#[cfg(feature = "alloc")]
386impl<T> crate::traits::modular::TryRandomMod for ModMathValue<T>
387where
388 T: FixedWidthUnsignedInt + PartialOrd,
389{
390 fn try_random_mod<R>(rng: &mut R, modulus: &Self) -> Result<Self>
391 where
392 R: rand_core::TryCryptoRng + ?Sized,
393 {
394 let container_bits = <T as FixedWidthUnsignedInt>::bits_precision(&modulus.0);
395 let leading_zero_bits = <T as FixedWidthUnsignedInt>::leading_zeros(&modulus.0);
396 if leading_zero_bits >= container_bits {
397 return Err(Error::InvalidModulus);
398 }
399 try_random_mod_masked::<R, T, _, _>(
400 rng,
401 container_bits,
402 leading_zero_bits,
403 modulus,
404 wrap_value::<T>,
405 )
406 }
407}
408
409#[cfg(not(feature = "alloc"))]
410impl<T> crate::traits::modular::TryRandomMod for T
411where
412 T: FixedWidthUnsignedInt + PartialOrd,
413{
414 fn try_random_mod<R>(rng: &mut R, modulus: &Self) -> Result<Self>
415 where
416 R: rand_core::TryCryptoRng + ?Sized,
417 {
418 let container_bits = <T as FixedWidthUnsignedInt>::bits_precision(modulus);
419 let leading_zero_bits = <T as FixedWidthUnsignedInt>::leading_zeros(modulus);
420 if leading_zero_bits >= container_bits {
421 return Err(Error::InvalidModulus);
422 }
423 try_random_mod_masked::<R, T, T, _>(rng, container_bits, leading_zero_bits, modulus, |x| x)
424 }
425}
426
427#[derive(Clone, Debug)]
428pub struct ModMathParams<T, P: Personality = Nct> {
429 field: ModmathField<T, P>,
433 modulus_odd: Odd<ModMathValue<T>>,
439}
440
441impl<T: ModMathInt + HasPersonality<P = Nct>> ModMathParams<T, Nct> {
442 pub fn new(modulus: T) -> Result<Self> {
443 let field = ModmathField::<T, Nct>::new(modulus).ok_or(Error::InvalidModulus)?;
444 let modulus_odd = Odd::new(wrap_value(modulus)).ok_or(Error::InvalidModulus)?;
445 Ok(Self { field, modulus_odd })
446 }
447}
448
449impl<T: ModMathIntCt + HasPersonality<P = Ct>> ModMathParams<T, Ct> {
450 pub fn new(modulus: T) -> Result<Self> {
453 let field = ModmathField::<T, Ct>::new(modulus).ok_or(Error::InvalidModulus)?;
454 let modulus_odd = Odd::new(wrap_value(modulus)).ok_or(Error::InvalidModulus)?;
455 Ok(Self { field, modulus_odd })
456 }
457}
458
459impl<T, P: Personality> ModMathParams<T, P> {
460 pub(crate) fn field(&self) -> &ModmathField<T, P> {
461 &self.field
462 }
463}
464
465pub fn public_key_from_be_bytes<T>(
468 modulus: &[u8],
469 exponent: u32,
470) -> Result<GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Nct>>>
471where
472 T: ModMathInt + HasPersonality<P = Nct>,
473{
474 let n = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
475 modulus,
476 )?);
477 let exponent = exponent.to_be_bytes();
478 let e = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
479 &exponent,
480 )?);
481 GenericRsaPublicKey::from_components(n, e, ModMathParams::<T, Nct>::new(unwrap_value(&n))?)
482}
483
484pub fn rsa_public_op<T>(
487 key: &GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Nct>>,
488 input: &[u8],
489) -> Result<<ModMathValue<T> as UnsignedModularInt>::Bytes>
490where
491 T: ModMathInt + HasPersonality<P = Nct>,
492{
493 let input = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
494 input,
495 )?);
496 Ok(rsa_encrypt(key, &input)?.to_be_bytes())
497}
498
499pub fn public_key_ct_from_be_bytes<T>(
504 modulus: &[u8],
505 exponent: u32,
506) -> Result<GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Ct>>>
507where
508 T: ModMathIntCt + HasPersonality<P = Ct>,
509{
510 let n = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
511 modulus,
512 )?);
513 let exponent = exponent.to_be_bytes();
514 let e = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
515 &exponent,
516 )?);
517 GenericRsaPublicKey::from_components(n, e, ModMathParams::<T, Ct>::new(unwrap_value(&n))?)
518}
519
520pub fn rsa_public_op_ct<T>(
521 key: &GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Ct>>,
522 input: &[u8],
523) -> Result<<ModMathValue<T> as UnsignedModularInt>::Bytes>
524where
525 T: ModMathIntCt + HasPersonality<P = Ct>,
526{
527 let input = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
528 input,
529 )?);
530 Ok(rsa_encrypt(key, &input)?.to_be_bytes())
531}
532
533#[derive(Clone, Debug)]
536pub struct ModMathForm<T, P: Personality = Nct>
537where
538 T: Clone + Zeroize,
539{
540 integer_mont: ModMathValue<T>,
541 params: ModMathParams<T, P>,
542}
543
544impl<T, P: Personality> Zeroize for ModMathForm<T, P>
546where
547 T: Clone + Zeroize,
548{
549 fn zeroize(&mut self) {
550 self.integer_mont.zeroize();
551 }
552}
553
554impl<T, P: Personality> Drop for ModMathForm<T, P>
555where
556 T: Clone + Zeroize,
557{
558 fn drop(&mut self) {
559 self.zeroize();
560 }
561}
562
563impl<T, P: Personality> zeroize::ZeroizeOnDrop for ModMathForm<T, P> where T: Clone + Zeroize {}
564
565impl<T: ModMathInt + HasPersonality<P = Nct>> IntoMontyForm<ModMathParams<T, Nct>>
566 for ModMathForm<T, Nct>
567{
568 fn from_reduced(integer: ModMathValue<T>, params: &ModMathParams<T, Nct>) -> Self {
569 let field = params.field();
570 let r = field.reduce(unwrap_value_ref(&integer));
571 Self {
572 integer_mont: wrap_value(*r.mont_value()),
573 params: params.clone(),
574 }
575 }
576
577 fn from_value(integer: ModMathValue<T>, params: &ModMathParams<T, Nct>) -> Self {
581 Self::from_reduced(integer, params)
582 }
583}
584
585impl<T: ModMathInt + HasPersonality<P = Nct>> ModMathForm<T, Nct> {
586 fn pow_loop(&self, exp_raw: T) -> T {
587 let field = self.params.field();
588 let base = field.residue_from_mont(unwrap_value(&self.integer_mont));
589 *field.exp(&base, &exp_raw).mont_value()
590 }
591
592 fn to_reduced(&self) -> T {
593 let field = self.params.field();
594 let r = field.residue_from_mont(unwrap_value(&self.integer_mont));
595 field.into_raw(&r)
596 }
597}
598
599impl<T: ModMathInt + HasPersonality<P = Nct>> Pow<ModMathParams<T, Nct>> for ModMathForm<T, Nct> {
600 fn pow(&self, exp: &ModMathValue<T>) -> Self {
601 let result_mont = self.pow_loop(unwrap_value(exp));
602 Self {
603 integer_mont: wrap_value(result_mont),
604 params: self.params.clone(),
605 }
606 }
607}
608
609impl<T: ModMathInt + HasPersonality<P = Nct>> PowBoundedExp<ModMathParams<T, Nct>>
610 for ModMathForm<T, Nct>
611{
612 fn pow_bounded_exp(&self, exp: &ModMathValue<T>, _exp_bits: u32) -> Self {
613 let result_mont = self.pow_loop(unwrap_value(exp));
616 Self {
617 integer_mont: wrap_value(result_mont),
618 params: self.params.clone(),
619 }
620 }
621
622 fn retrieve(&self) -> ModMathValue<T> {
623 wrap_value(self.to_reduced())
624 }
625}
626
627impl<T: ModMathInt + HasPersonality<P = Nct>> ModulusParams for ModMathParams<T, Nct> {
628 type Modulus = ModMathValue<T>;
629 type MontgomeryForm = ModMathForm<T, Nct>;
630
631 fn modulus(&self) -> &Odd<Self::Modulus> {
632 &self.modulus_odd
633 }
634
635 fn bits_precision(&self) -> u32 {
636 FixedWidthUnsignedInt::bits_precision(self.field.modulus())
637 }
638}
639
640impl<T: ModMathIntCt + HasPersonality<P = Ct>> IntoMontyForm<ModMathParams<T, Ct>>
641 for ModMathForm<T, Ct>
642{
643 fn from_reduced(integer: ModMathValue<T>, params: &ModMathParams<T, Ct>) -> Self {
644 let field = params.field();
645 let r = field.reduce(unwrap_value_ref(&integer));
646 Self {
647 integer_mont: wrap_value(*r.mont_value()),
648 params: params.clone(),
649 }
650 }
651
652 fn from_value(integer: ModMathValue<T>, params: &ModMathParams<T, Ct>) -> Self {
655 Self::from_reduced(integer, params)
656 }
657}
658
659impl<T: ModMathIntCt + HasPersonality<P = Ct>> ModMathForm<T, Ct> {
660 fn pow_loop_ct(&self, exp_raw: T) -> T {
665 let field = self.params.field();
666 let base = field.residue_from_mont(unwrap_value(&self.integer_mont));
667 *field.exp(&base, &exp_raw).mont_value()
668 }
669
670 fn pow_loop_public_exp(&self, exp_raw: T) -> T {
675 let field = self.params.field();
676 let base = field.residue_from_mont(unwrap_value(&self.integer_mont));
677 *field.exp_public_exp(&base, &exp_raw).mont_value()
678 }
679
680 fn to_reduced(&self) -> T {
681 let field = self.params.field();
682 let r = field.residue_from_mont(unwrap_value(&self.integer_mont));
683 field.into_raw(&r)
684 }
685}
686
687impl<T: ModMathIntCt + HasPersonality<P = Ct>> Pow<ModMathParams<T, Ct>> for ModMathForm<T, Ct> {
688 fn pow(&self, exp: &ModMathValue<T>) -> Self {
689 let result_mont = self.pow_loop_ct(unwrap_value(exp));
690 Self {
691 integer_mont: wrap_value(result_mont),
692 params: self.params.clone(),
693 }
694 }
695}
696
697impl<T: ModMathIntCt + HasPersonality<P = Ct>> PowBoundedExp<ModMathParams<T, Ct>>
698 for ModMathForm<T, Ct>
699{
700 fn pow_bounded_exp(&self, exp: &ModMathValue<T>, _exp_bits: u32) -> Self {
701 let result_mont = self.pow_loop_public_exp(unwrap_value(exp));
702 Self {
703 integer_mont: wrap_value(result_mont),
704 params: self.params.clone(),
705 }
706 }
707
708 fn retrieve(&self) -> ModMathValue<T> {
709 wrap_value(self.to_reduced())
710 }
711}
712
713impl<T> crate::traits::modular::InvertCt<ModMathParams<T, Ct>> for ModMathForm<T, Ct>
718where
719 T: ModMathIntCt
720 + HasPersonality<P = Ct>
721 + modmath_cios::CiosRowOps
722 + core::ops::BitOr<Output = T>,
723 <T as modmath_cios::CiosRowOps>::Word: const_num_traits::CtParity,
724{
725 fn invert_ct(&self) -> Option<Self> {
726 let field = self.params.field();
727 let residue = field.residue_from_mont(unwrap_value(&self.integer_mont));
728 let ct_option = field.inv_safegcd_ct(&residue);
729 ct_option.into_option().map(|inv_res| Self {
730 integer_mont: wrap_value(*inv_res.mont_value()),
731 params: self.params.clone(),
732 })
733 }
734}
735
736impl<T: ModMathIntCt + HasPersonality<P = Ct>> crate::traits::modular::MulCt<ModMathParams<T, Ct>>
741 for ModMathForm<T, Ct>
742{
743 fn mul_ct(&self, rhs: &Self) -> Self {
744 debug_assert!(
750 self.params.modulus_odd == rhs.params.modulus_odd,
751 "MulCt operands must share the same modulus"
752 );
753 let field = self.params.field();
754 let lhs_res = field.residue_from_mont(unwrap_value(&self.integer_mont));
755 let rhs_res = field.residue_from_mont(unwrap_value(&rhs.integer_mont));
756 let product = field.mul(&lhs_res, &rhs_res);
757 Self {
758 integer_mont: wrap_value(*product.mont_value()),
759 params: self.params.clone(),
760 }
761 }
762}
763
764impl<T: ModMathIntCt + HasPersonality<P = Ct>> ModulusParams for ModMathParams<T, Ct> {
765 type Modulus = ModMathValue<T>;
766 type MontgomeryForm = ModMathForm<T, Ct>;
767
768 fn modulus(&self) -> &Odd<Self::Modulus> {
769 &self.modulus_odd
770 }
771
772 fn bits_precision(&self) -> u32 {
773 FixedWidthUnsignedInt::bits_precision(self.field.modulus())
774 }
775}
776
777impl<T: ModMathIntCt + HasPersonality<P = Ct>> crate::traits::modular::sealed::CtModulusParamsSealed
783 for ModMathParams<T, Ct>
784{
785}
786impl<T: ModMathIntCt + HasPersonality<P = Ct>> crate::traits::modular::CtModulusParams
787 for ModMathParams<T, Ct>
788{
789}
790
791#[cfg(test)]
792#[cfg(feature = "alloc")]
793mod tests {
794 use const_num_traits::Ct;
795 use fixed_bigint::FixedUInt;
796 use rand::rngs::ChaCha8Rng;
797 use rand_core::SeedableRng;
798 use sha1::Sha1;
799 use signature::hazmat::PrehashVerifier;
800
801 use super::{
802 public_key_ct_from_be_bytes, public_key_from_be_bytes, ModMathForm, ModMathParams,
803 ModMathValue,
804 };
805 use crate::key::GenericRsaPublicKey;
806 use crate::pkcs1v15::{GenericEncryptingKey, GenericSignature, GenericVerifyingKey};
807 use crate::{traits::RandomizedEncryptor, BoxedUint, Pkcs1v15Encrypt, RsaPublicKey};
808
809 type SmallU = FixedUInt<u8, 64>;
810 type SmallUCt = FixedUInt<u8, 64, Ct>;
811
812 #[test]
813 fn brand_round_trip() {
814 let params = ModMathParams::<SmallU>::new(SmallU::from(13u8)).unwrap();
815 let f = params.field();
816 let r = f.reduce(&SmallU::from(7u8));
817 assert_eq!(f.into_raw(&r), SmallU::from(7u8));
818 }
819
820 #[test]
821 fn brand_mul_exp() {
822 let params = ModMathParams::<SmallU>::new(SmallU::from(13u8)).unwrap();
823 let f = params.field();
824 let a = f.reduce(&SmallU::from(7u8));
826 let b = f.reduce(&SmallU::from(11u8));
827 assert_eq!(f.into_raw(&f.mul(&a, &b)), SmallU::from(12u8));
828 let base = f.reduce(&SmallU::from(2u8));
830 assert_eq!(
831 f.into_raw(&f.exp(&base, &SmallU::from(10u8))),
832 SmallU::from(10u8)
833 );
834 }
835
836 #[test]
837 fn brand_ct_matches_nct() {
838 let p_nct = ModMathParams::<SmallU>::new(SmallU::from(13u8)).unwrap();
839 let p_ct = ModMathParams::<SmallUCt, Ct>::new(SmallUCt::from(13u8)).unwrap();
840 let f_nct = p_nct.field();
841 let f_ct = p_ct.field();
842 let nct = f_nct.into_raw(&f_nct.mul(
843 &f_nct.reduce(&SmallU::from(7u8)),
844 &f_nct.reduce(&SmallU::from(11u8)),
845 ));
846 let ct = f_ct.into_raw(&f_ct.mul(
847 &f_ct.reduce(&SmallUCt::from(7u8)),
848 &f_ct.reduce(&SmallUCt::from(11u8)),
849 ));
850 let mut nct_bytes = [0u8; 64];
852 let mut ct_bytes = [0u8; 64];
853 let _ = nct.to_be_bytes(&mut nct_bytes);
854 let _ = ct.to_be_bytes(&mut ct_bytes);
855 assert_eq!(nct_bytes, ct_bytes);
856 }
857
858 #[test]
859 fn mod_math_form_zeroize_on_drop() {
860 fn assert_zeroize_on_drop<T: zeroize::ZeroizeOnDrop>() {}
861 assert_zeroize_on_drop::<ModMathForm<SmallU>>();
862 assert_zeroize_on_drop::<ModMathForm<SmallUCt, Ct>>();
863 }
864
865 #[test]
866 fn verify_pkcs1v15_signature_with_modmath_fixed_uint() {
867 type U512 = FixedUInt<u8, 64>;
868
869 let digest: [u8; 20] = [
870 0x43, 0x0c, 0xe3, 0x4d, 0x02, 0x07, 0x24, 0xed, 0x75, 0xa1, 0x96, 0xdf, 0xc2, 0xad,
871 0x67, 0xc7, 0x77, 0x72, 0xd1, 0x69,
872 ];
873 let modulus: [u8; 64] = [
874 0x96, 0x9D, 0x03, 0xFF, 0xA9, 0x8D, 0x88, 0x8F, 0x3A, 0xA4, 0xF2, 0xFE, 0xD2, 0x32,
875 0xE6, 0x1C, 0x4A, 0xCF, 0x06, 0x63, 0xA9, 0x2F, 0x99, 0x03, 0x4C, 0xF7, 0xB7, 0x24,
876 0x5A, 0x1A, 0x1E, 0x5E, 0xAF, 0xA5, 0x65, 0xAF, 0xB9, 0x0B, 0xAB, 0x22, 0x85, 0x71,
877 0x2F, 0xAA, 0x50, 0x39, 0x39, 0xA0, 0x65, 0xFB, 0x60, 0xDD, 0x08, 0x28, 0xA3, 0x84,
878 0xF2, 0x6D, 0x8A, 0xFC, 0x28, 0x6D, 0xF6, 0xCF,
879 ];
880 let signature: [u8; 64] = [
881 0x45, 0x53, 0xF3, 0xAF, 0x16, 0xAF, 0x63, 0x97, 0xB0, 0xD3, 0x2F, 0x8A, 0xEC, 0xD5,
882 0x4C, 0xF1, 0xF3, 0xD0, 0x0C, 0x9F, 0x42, 0xDC, 0x68, 0xCB, 0xD7, 0x05, 0xCE, 0xA5,
883 0xA9, 0x70, 0x95, 0x3E, 0xC0, 0xBC, 0x4A, 0x18, 0xED, 0x91, 0xA3, 0x5D, 0x66, 0xEC,
884 0xDA, 0x4A, 0x83, 0x32, 0xCF, 0xC3, 0xA3, 0xAB, 0x21, 0xAD, 0x59, 0xB2, 0x2E, 0x87,
885 0xC2, 0x73, 0xFF, 0x08, 0x88, 0xDD, 0x4D, 0xE0,
886 ];
887
888 let key = public_key_from_be_bytes::<U512>(&modulus, 3).unwrap();
889 let verifying_key = GenericVerifyingKey::<Sha1, _, _>::new(key);
890 let signature =
891 GenericSignature::from(ModMathValue::from_inner(U512::from_be_bytes(&signature)));
892 verifying_key.verify_prehash(&digest, &signature).unwrap();
893 }
894
895 #[test]
896 fn verify_pkcs1v15_signature_with_modmath_fixed_uint32() {
897 type U512 = FixedUInt<u32, 16>;
898
899 let digest: [u8; 20] = [
900 0x43, 0x0c, 0xe3, 0x4d, 0x02, 0x07, 0x24, 0xed, 0x75, 0xa1, 0x96, 0xdf, 0xc2, 0xad,
901 0x67, 0xc7, 0x77, 0x72, 0xd1, 0x69,
902 ];
903 let modulus: [u8; 64] = [
904 0x96, 0x9D, 0x03, 0xFF, 0xA9, 0x8D, 0x88, 0x8F, 0x3A, 0xA4, 0xF2, 0xFE, 0xD2, 0x32,
905 0xE6, 0x1C, 0x4A, 0xCF, 0x06, 0x63, 0xA9, 0x2F, 0x99, 0x03, 0x4C, 0xF7, 0xB7, 0x24,
906 0x5A, 0x1A, 0x1E, 0x5E, 0xAF, 0xA5, 0x65, 0xAF, 0xB9, 0x0B, 0xAB, 0x22, 0x85, 0x71,
907 0x2F, 0xAA, 0x50, 0x39, 0x39, 0xA0, 0x65, 0xFB, 0x60, 0xDD, 0x08, 0x28, 0xA3, 0x84,
908 0xF2, 0x6D, 0x8A, 0xFC, 0x28, 0x6D, 0xF6, 0xCF,
909 ];
910 let signature: [u8; 64] = [
911 0x45, 0x53, 0xF3, 0xAF, 0x16, 0xAF, 0x63, 0x97, 0xB0, 0xD3, 0x2F, 0x8A, 0xEC, 0xD5,
912 0x4C, 0xF1, 0xF3, 0xD0, 0x0C, 0x9F, 0x42, 0xDC, 0x68, 0xCB, 0xD7, 0x05, 0xCE, 0xA5,
913 0xA9, 0x70, 0x95, 0x3E, 0xC0, 0xBC, 0x4A, 0x18, 0xED, 0x91, 0xA3, 0x5D, 0x66, 0xEC,
914 0xDA, 0x4A, 0x83, 0x32, 0xCF, 0xC3, 0xA3, 0xAB, 0x21, 0xAD, 0x59, 0xB2, 0x2E, 0x87,
915 0xC2, 0x73, 0xFF, 0x08, 0x88, 0xDD, 0x4D, 0xE0,
916 ];
917
918 let n = U512::from_be_bytes(&modulus);
919 let e = U512::from(3u8);
920 let key = GenericRsaPublicKey::from_components(
924 ModMathValue::from_inner(n),
925 ModMathValue::from_inner(e),
926 ModMathParams::<U512, const_num_traits::Nct>::new(n).unwrap(),
927 )
928 .unwrap();
929 let verifying_key = GenericVerifyingKey::<Sha1, _, _>::new(key);
930 let signature =
931 GenericSignature::from(ModMathValue::from_inner(U512::from_be_bytes(&signature)));
932 verifying_key.verify_prehash(&digest, &signature).unwrap();
933 }
934
935 #[test]
936 fn encrypt_pkcs1v15_with_modmath_fixed_uint_matches_boxeduint() {
937 type U512 = FixedUInt<u8, 64, Ct>;
941
942 let modulus: [u8; 64] = [
943 0x96, 0x9D, 0x03, 0xFF, 0xA9, 0x8D, 0x88, 0x8F, 0x3A, 0xA4, 0xF2, 0xFE, 0xD2, 0x32,
944 0xE6, 0x1C, 0x4A, 0xCF, 0x06, 0x63, 0xA9, 0x2F, 0x99, 0x03, 0x4C, 0xF7, 0xB7, 0x24,
945 0x5A, 0x1A, 0x1E, 0x5E, 0xAF, 0xA5, 0x65, 0xAF, 0xB9, 0x0B, 0xAB, 0x22, 0x85, 0x71,
946 0x2F, 0xAA, 0x50, 0x39, 0x39, 0xA0, 0x65, 0xFB, 0x60, 0xDD, 0x08, 0x28, 0xA3, 0x84,
947 0xF2, 0x6D, 0x8A, 0xFC, 0x28, 0x6D, 0xF6, 0xCF,
948 ];
949 let msg = b"hello world!";
950
951 let modmath_key = public_key_ct_from_be_bytes::<U512>(&modulus, 3).unwrap();
952 let boxed_key = RsaPublicKey::new(
953 BoxedUint::from_be_slice(&modulus, 512).unwrap(),
954 3u64.into(),
955 )
956 .unwrap();
957
958 let mut modmath_rng = ChaCha8Rng::from_seed([42; 32]);
959 let mut boxed_rng = ChaCha8Rng::from_seed([42; 32]);
960 let mut storage = [0u8; 64];
961
962 let modmath_ciphertext = GenericEncryptingKey::new(modmath_key)
963 .encrypt_with_rng_into(&mut modmath_rng, msg, &mut storage)
964 .unwrap();
965 let boxed_ciphertext = boxed_key
966 .encrypt(&mut boxed_rng, Pkcs1v15Encrypt, msg)
967 .unwrap();
968
969 assert_eq!(modmath_ciphertext, boxed_ciphertext.as_slice());
970 }
971}
972
973#[cfg(test)]
977mod private_op_tests {
978 use super::*;
979 use const_num_traits::Ct;
980 use fixed_bigint::FixedUInt;
981
982 type SmallUCt = FixedUInt<u8, 64, Ct>;
983
984 type HeaplessCt = fixed_bigint::HeaplessBigInt<u8, 64, Ct>;
992
993 trait TestCt:
998 super::ModMathIntCt
999 + HasPersonality<P = Ct>
1000 + modmath_cios::CiosRowOps
1001 + core::ops::BitOr<Output = Self>
1002 + core::fmt::Debug
1003 where
1004 <Self as modmath_cios::CiosRowOps>::Word: const_num_traits::CtParity,
1005 {
1006 }
1007 impl<T> TestCt for T
1008 where
1009 T: super::ModMathIntCt
1010 + HasPersonality<P = Ct>
1011 + modmath_cios::CiosRowOps
1012 + core::ops::BitOr<Output = T>
1013 + core::fmt::Debug,
1014 <T as modmath_cios::CiosRowOps>::Word: const_num_traits::CtParity,
1015 {
1016 }
1017
1018 fn toy_params<T: TestCt>() -> ModMathParams<T, Ct>
1021 where
1022 <T as modmath_cios::CiosRowOps>::Word: const_num_traits::CtParity,
1023 {
1024 ModMathParams::<T, Ct>::new(
1025 <T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&[35]).unwrap(),
1026 )
1027 .unwrap()
1028 }
1029
1030 fn toy_params_wide() -> ModMathParams<SmallUCt, Ct> {
1038 let mut bytes = [0u8; 64];
1039 bytes[0] = 0x80;
1040 bytes[63] = 0x01;
1041 let n = <SmallUCt as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&bytes).unwrap();
1042 ModMathParams::<SmallUCt, Ct>::new(n).unwrap()
1043 }
1044
1045 #[test]
1046 fn rsa_private_op_round_trip() {
1047 fn inner<T: TestCt>()
1048 where
1049 <T as modmath_cios::CiosRowOps>::Word: const_num_traits::CtParity,
1050 {
1051 let n_params = toy_params::<T>();
1052 let c = wrap_value(T::from(32u8));
1053 let d = wrap_value(T::from(29u8));
1054 let expected = wrap_value(T::from(2u8));
1055 let recovered = crate::algorithms::rsa::rsa_private_op(&c, &d, &n_params);
1056 assert_eq!(recovered, expected);
1057 }
1058 inner::<SmallUCt>();
1059 inner::<HeaplessCt>();
1060 }
1061
1062 fn blinded_matches_unblinded_inner<T: TestCt>()
1068 where
1069 <T as modmath_cios::CiosRowOps>::Word: const_num_traits::CtParity,
1070 {
1071 let n_params = toy_params::<T>();
1072 let c = wrap_value(T::from(32u8));
1073 let d = wrap_value(T::from(29u8));
1074 let e = wrap_value(T::from(5u8));
1075 let r = wrap_value(T::from(6u8));
1076 let expected = wrap_value(T::from(2u8));
1077 let recovered =
1078 crate::algorithms::rsa::rsa_private_op_blinded(&r, &c, &d, &e, &n_params).unwrap();
1079 assert_eq!(recovered, expected);
1080 }
1081 #[test]
1082 fn rsa_private_op_blinded_matches_unblinded() {
1083 blinded_matches_unblinded_inner::<SmallUCt>();
1084 blinded_matches_unblinded_inner::<HeaplessCt>();
1085 }
1086
1087 #[test]
1094 fn rsa_private_op_blinded_rejects_non_coprime_r() {
1095 fn inner<T: TestCt>()
1096 where
1097 <T as modmath_cios::CiosRowOps>::Word: const_num_traits::CtParity,
1098 {
1099 let n_params = toy_params::<T>();
1100 let c = wrap_value(T::from(32u8));
1101 let d = wrap_value(T::from(29u8));
1102 let e = wrap_value(T::from(5u8));
1103 let r_bad = wrap_value(T::from(5u8)); let result =
1105 crate::algorithms::rsa::rsa_private_op_blinded(&r_bad, &c, &d, &e, &n_params);
1106 assert!(result.is_err());
1107 }
1108 inner::<SmallUCt>();
1109 inner::<HeaplessCt>();
1110 }
1111
1112 fn and_check_blinded_round_trip_inner<T: TestCt>()
1119 where
1120 <T as modmath_cios::CiosRowOps>::Word: const_num_traits::CtParity,
1121 {
1122 use rand::rngs::ChaCha8Rng;
1123 use rand_core::SeedableRng;
1124
1125 let n_params = toy_params::<T>();
1126 let c = wrap_value(T::from(32u8));
1127 let d = wrap_value(T::from(29u8));
1128 let e = wrap_value(T::from(5u8));
1129 let expected = wrap_value(T::from(2u8));
1130 let mut rng = ChaCha8Rng::from_seed([42; 32]);
1131 let recovered = crate::algorithms::rsa::rsa_private_op_and_check_blinded(
1132 &mut rng, &c, &d, &e, &n_params,
1133 )
1134 .unwrap();
1135 assert_eq!(recovered, expected);
1136 }
1137 #[test]
1138 fn rsa_private_op_and_check_blinded_round_trip() {
1139 and_check_blinded_round_trip_inner::<SmallUCt>();
1140 and_check_blinded_round_trip_inner::<HeaplessCt>();
1141 }
1142
1143 fn try_random_mod_below_modulus_inner<T: TestCt>(n: ModMathValue<T>)
1147 where
1148 <T as modmath_cios::CiosRowOps>::Word: const_num_traits::CtParity,
1149 {
1150 use crate::traits::modular::TryRandomMod;
1151 use rand::rngs::ChaCha8Rng;
1152 use rand_core::SeedableRng;
1153
1154 let mut rng = ChaCha8Rng::from_seed([42; 32]);
1155 let mut samples = [ModMathValue::<T>::from(0u8); 16];
1156 for slot in samples.iter_mut() {
1157 let r = ModMathValue::<T>::try_random_mod(&mut rng, &n).unwrap();
1158 assert!(r < n, "sample must be < modulus");
1159 *slot = r;
1160 }
1161 let first = samples[0];
1162 assert!(
1163 samples.iter().any(|s| *s != first),
1164 "samples are trivially all equal — RNG or sampler broken"
1165 );
1166 }
1167 #[test]
1168 fn try_random_mod_modmath_stays_below_modulus() {
1169 let mut bytes = [0u8; 64];
1171 bytes[0] = 0x80;
1172 bytes[63] = 0x01;
1173 let nf = <SmallUCt as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&bytes).unwrap();
1174 try_random_mod_below_modulus_inner::<SmallUCt>(wrap_value(nf));
1175 let nh = <HeaplessCt as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&bytes).unwrap();
1176 try_random_mod_below_modulus_inner::<HeaplessCt>(wrap_value(nh));
1177 }
1178
1179 #[test]
1190 fn try_random_mod_modmath_succeeds_on_narrow_modulus_wide_carrier() {
1191 fn inner<T: TestCt>()
1192 where
1193 <T as modmath_cios::CiosRowOps>::Word: const_num_traits::CtParity,
1194 {
1195 use crate::traits::modular::TryRandomMod;
1196 use rand::rngs::ChaCha8Rng;
1197 use rand_core::SeedableRng;
1198
1199 let n_params = toy_params::<T>();
1200 let n = *n_params.modulus().as_ref();
1201 let mut rng = ChaCha8Rng::from_seed([42; 32]);
1202 for _ in 0..64 {
1203 let r = ModMathValue::<T>::try_random_mod(&mut rng, &n).unwrap();
1204 assert!(r < n);
1205 }
1206 }
1207 inner::<SmallUCt>();
1208 inner::<HeaplessCt>();
1209 }
1210
1211 #[test]
1212 fn rsa_private_op_and_check_round_trip() {
1213 fn inner<T: TestCt>()
1214 where
1215 <T as modmath_cios::CiosRowOps>::Word: const_num_traits::CtParity,
1216 {
1217 let n_params = toy_params::<T>();
1218 let c = wrap_value(T::from(32u8));
1219 let d = wrap_value(T::from(29u8));
1220 let e = wrap_value(T::from(5u8));
1221 let expected = wrap_value(T::from(2u8));
1222 let recovered =
1223 crate::algorithms::rsa::rsa_private_op_and_check(&c, &d, &e, &n_params).unwrap();
1224 assert_eq!(recovered, expected);
1225 }
1226 inner::<SmallUCt>();
1227 inner::<HeaplessCt>();
1228 }
1229
1230 fn invert_ct_known_answer_inner<T: TestCt>()
1234 where
1235 <T as modmath_cios::CiosRowOps>::Word: const_num_traits::CtParity,
1236 {
1237 use crate::traits::modular::{IntoMontyForm, InvertCt, PowBoundedExp};
1238 let n_params = toy_params::<T>();
1239 let three = wrap_value(T::from(3u8));
1240 let mont_three = ModMathForm::<T, Ct>::from_reduced(three, &n_params);
1241 let mont_inv = mont_three.invert_ct().expect("3 is coprime to 35");
1242 let recovered = PowBoundedExp::<ModMathParams<T, Ct>>::retrieve(&mont_inv);
1243 assert_eq!(recovered, wrap_value(T::from(12u8)));
1244 }
1245 #[test]
1249 fn invert_ct_modmath_known_answer() {
1250 invert_ct_known_answer_inner::<SmallUCt>();
1251 invert_ct_known_answer_inner::<HeaplessCt>();
1252 }
1253
1254 fn mul_ct_inverse_round_trip_inner<T: TestCt>()
1259 where
1260 <T as modmath_cios::CiosRowOps>::Word: const_num_traits::CtParity,
1261 {
1262 use crate::traits::modular::{IntoMontyForm, InvertCt, MulCt, PowBoundedExp};
1263 let n_params = toy_params::<T>();
1264 let three = wrap_value(T::from(3u8));
1265 let mont_three = ModMathForm::<T, Ct>::from_reduced(three, &n_params);
1266 let mont_inv = mont_three.invert_ct().expect("3 is coprime to 35");
1267 let product = mont_three.mul_ct(&mont_inv);
1268 let recovered = PowBoundedExp::<ModMathParams<T, Ct>>::retrieve(&product);
1269 assert_eq!(recovered, wrap_value(T::from(1u8)));
1270 }
1271 #[test]
1272 fn mul_ct_modmath_inverse_round_trip() {
1273 mul_ct_inverse_round_trip_inner::<SmallUCt>();
1274 mul_ct_inverse_round_trip_inner::<HeaplessCt>();
1275 }
1276
1277 #[test]
1291 fn try_random_mod_accepts_narrow_modulus() {
1292 use crate::traits::modular::TryRandomMod;
1293 use rand::rngs::ChaCha8Rng;
1294 use rand_core::SeedableRng;
1295 let n_nat = HeaplessCt::from_be_bytes(&[35]); assert_eq!(
1297 const_num_traits::BitsPrecision::bits_precision(&n_nat),
1298 8,
1299 "modulus must be at its natural width, not padded to CAP"
1300 );
1301 let n = wrap_value(n_nat);
1302 let mut rng = ChaCha8Rng::from_seed([7; 32]);
1303 for _ in 0..16 {
1304 let r = <ModMathValue<HeaplessCt> as TryRandomMod>::try_random_mod(&mut rng, &n)
1305 .expect("sampler must accept on a narrow modulus");
1306 assert!(r < n, "sampled r must be < modulus");
1307 }
1308 }
1309
1310 #[test]
1314 fn narrow_modulus_private_ops_round_trip() {
1315 let n_params =
1316 ModMathParams::<HeaplessCt, Ct>::new(HeaplessCt::from_be_bytes(&[35])).unwrap();
1317 let c = wrap_value(HeaplessCt::from(32u8));
1318 let d = wrap_value(HeaplessCt::from(29u8));
1319 let e = wrap_value(HeaplessCt::from(5u8));
1320 let two = HeaplessCt::from(2u8);
1321
1322 let m = crate::algorithms::rsa::rsa_private_op(&c, &d, &n_params);
1323 assert_eq!(into_inner(m), two);
1324
1325 let r_nat = wrap_value(HeaplessCt::from(6u8)); let blinded =
1327 crate::algorithms::rsa::rsa_private_op_blinded(&r_nat, &c, &d, &e, &n_params).unwrap();
1328 assert_eq!(into_inner(blinded), two);
1329 }
1330
1331 #[test]
1337 fn narrow_modulus_sampled_blinded_op() {
1338 use rand::rngs::ChaCha8Rng;
1339 use rand_core::SeedableRng;
1340 let n_params =
1341 ModMathParams::<HeaplessCt, Ct>::new(HeaplessCt::from_be_bytes(&[35])).unwrap();
1342 let c = wrap_value(HeaplessCt::from(32u8));
1343 let d = wrap_value(HeaplessCt::from(29u8));
1344 let e = wrap_value(HeaplessCt::from(5u8));
1345 let mut rng = ChaCha8Rng::from_seed([9; 32]);
1346 let recovered = crate::algorithms::rsa::rsa_private_op_and_check_blinded(
1347 &mut rng, &c, &d, &e, &n_params,
1348 )
1349 .unwrap();
1350 assert_eq!(into_inner(recovered), HeaplessCt::from(2u8));
1351 }
1352
1353 #[test]
1354 fn rsa_private_op_and_check_rejects_wrong_exponent() {
1355 fn inner<T: TestCt>()
1356 where
1357 <T as modmath_cios::CiosRowOps>::Word: const_num_traits::CtParity,
1358 {
1359 let n_params = toy_params::<T>();
1362 let c = wrap_value(T::from(32u8));
1363 let bad_d = wrap_value(T::from(11u8));
1364 let e = wrap_value(T::from(5u8));
1365 let result =
1366 crate::algorithms::rsa::rsa_private_op_and_check(&c, &bad_d, &e, &n_params);
1367 assert!(result.is_err());
1368 }
1369 inner::<SmallUCt>();
1370 inner::<HeaplessCt>();
1371 }
1372
1373 const N_2048: [u8; 256] = hex_literal::hex!(
1378 "d397b84d98a4c26138ed1b695a8106ead91d553bf06041b62d3fdc50a041e222
1379 b8f4529689c1b82c5e71554f5dd69fa2f4b6158cf0dbeb57811a0fc327e1f28e
1380 74fe74d3bc166c1eabdc1b8b57b934ca8be5b00b4f29975bcc99acaf415b59bb
1381 28a6782bb41a2c3c2976b3c18dbadef62f00c6bb226640095096c0cc60d22fe7
1382 ef987d75c6a81b10d96bf292028af110dc7cc1bbc43d22adab379a0cd5d8078c
1383 c780ff5cd6209dea34c922cf784f7717e428d75b5aec8ff30e5f0141510766e2
1384 e0ab8d473c84e8710b2b98227c3db095337ad3452f19e2b9bfbccdd8148abf67
1385 76fa552775e6e75956e45229ae5a9c46949bab1e622f0e48f56524a84ed3483b"
1386 );
1387 const D_2048: [u8; 256] = hex_literal::hex!(
1388 "c4e70c689162c94c660828191b52b4d8392115df486a9adbe831e458d7395832
1389 0dc1b755456e93701e9702d76fb0b92f90e01d1fe248153281fe79aa9763a92f
1390 ae69d8d7ecd144de29fa135bd14f9573e349e45031e3b76982f583003826c552
1391 e89a397c1a06bd2163488630d92e8c2bb643d7abef700da95d685c941489a46f
1392 54b5316f62b5d2c3a7f1bbd134cb37353a44683fdc9d95d36458de22f6c44057
1393 fe74a0a436c4308f73f4da42f35c47ac16a7138d483afc91e41dc3a1127382e0
1394 c0f5119b0221b4fc639d6b9c38177a6de9b526ebd88c38d7982c07f98a0efd87
1395 7d508aae275b946915c02e2e1106d175d74ec6777f5e80d12c053d9c7be1e341"
1396 );
1397
1398 #[test]
1399 fn pkcs1v15_sign_into_round_trip_2048_sha1() {
1400 use crate::algorithms::pkcs1v15::{
1401 pkcs1v15_generate_prefix_into, pkcs1v15_sign_pad_into, sign_into,
1402 };
1403 use crate::traits::PublicKeyParts;
1404 use sha1::Sha1;
1405
1406 type U2048 = FixedUInt<u8, 256, Ct>;
1407 const K: usize = 256;
1408
1409 let key = public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1410 let d_int = <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap();
1411 let d = wrap_value(d_int);
1412 let e_int =
1413 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&[0x01, 0x00, 0x01])
1414 .unwrap();
1415 let e = wrap_value(e_int);
1416
1417 let digest = [0xAAu8; 20];
1418 let mut prefix_storage = [0u8; 32];
1419 let prefix = pkcs1v15_generate_prefix_into::<Sha1>(&mut prefix_storage).unwrap();
1420
1421 let mut em_storage = [0u8; K];
1422 let mut sig_storage = [0u8; K];
1423 let sig = sign_into(
1424 key.n_params(),
1425 &d,
1426 &e,
1427 prefix,
1428 &digest,
1429 K,
1430 &mut em_storage,
1431 &mut sig_storage,
1432 )
1433 .unwrap();
1434 assert_eq!(sig.len(), K);
1435
1436 let recovered = public_key_op_ct(&key, sig).unwrap();
1439 let mut expected_em_storage = [0u8; K];
1440 let expected_em =
1441 pkcs1v15_sign_pad_into(prefix, &digest, K, &mut expected_em_storage).unwrap();
1442 assert_eq!(recovered.as_ref(), expected_em);
1443 }
1444
1445 #[test]
1446 fn pss_sign_into_round_trip_2048_sha1() {
1447 use crate::algorithms::pss::{emsa_pss_verify, sign_into};
1448 use crate::traits::PublicKeyParts;
1449 use digest::Digest;
1450 use sha1::Sha1;
1451
1452 type U2048 = FixedUInt<u8, 256, Ct>;
1453 const K: usize = 256;
1454 const KEY_BITS: usize = 2048;
1455
1456 let key = public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1457 let d = wrap_value(
1458 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1459 );
1460 let e = wrap_value(
1461 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&[0x01, 0x00, 0x01])
1462 .unwrap(),
1463 );
1464
1465 let digest = [0xAAu8; 20];
1466 let salt: &[u8] = &[]; let mut hash = Sha1::new();
1468
1469 let mut em_storage = [0u8; K];
1470 let mut sig_storage = [0u8; K];
1471 let sig = sign_into(
1472 key.n_params(),
1473 &d,
1474 &e,
1475 &digest,
1476 salt,
1477 K,
1478 &mut hash,
1479 &mut em_storage,
1480 &mut sig_storage,
1481 )
1482 .unwrap();
1483 assert_eq!(sig.len(), K);
1484
1485 let recovered = public_key_op_ct(&key, sig).unwrap();
1489 let mut em_copy = [0u8; K];
1490 em_copy.copy_from_slice(recovered.as_ref());
1491 let mut verify_hash = Sha1::new();
1492 emsa_pss_verify(
1493 &digest,
1494 &mut em_copy,
1495 Some(salt.len()),
1496 &mut verify_hash,
1497 KEY_BITS,
1498 )
1499 .unwrap();
1500 }
1501
1502 fn public_key_op_ct<T>(
1504 key: &crate::key::GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Ct>>,
1505 input: &[u8],
1506 ) -> Result<<ModMathValue<T> as UnsignedModularInt>::Bytes>
1507 where
1508 T: ModMathIntCt + HasPersonality<P = Ct>,
1509 {
1510 crate::modmath_support::rsa_public_op_ct(key, input)
1511 }
1512
1513 fn dummy_de() -> (ModMathValue<SmallUCt>, ModMathValue<SmallUCt>) {
1520 (
1521 wrap_value(SmallUCt::from(1u8)),
1522 wrap_value(SmallUCt::from(1u8)),
1523 )
1524 }
1525
1526 const SMALL_K: usize = 64;
1528
1529 #[test]
1530 fn pkcs1v15_sign_into_rejects_wrong_k() {
1531 use crate::algorithms::pkcs1v15::sign_into;
1532 let n_params = toy_params();
1533 let (d, e) = dummy_de();
1534 let mut em = [0u8; SMALL_K];
1535 let mut sig = [0u8; SMALL_K];
1536 let result = sign_into(
1537 &n_params,
1538 &d,
1539 &e,
1540 &[],
1541 &[0u8; 20],
1542 SMALL_K - 1, &mut em,
1544 &mut sig,
1545 );
1546 assert!(matches!(result, Err(Error::InvalidArguments)));
1547 }
1548
1549 #[test]
1550 fn pkcs1v15_sign_into_rejects_small_sig_storage() {
1551 use crate::algorithms::pkcs1v15::sign_into;
1552 let n_params = toy_params_wide();
1553 let (d, e) = dummy_de();
1554 let mut em = [0u8; SMALL_K];
1555 let mut sig = [0u8; SMALL_K - 1]; let result = sign_into(
1557 &n_params,
1558 &d,
1559 &e,
1560 &[],
1561 &[0u8; 20],
1562 SMALL_K,
1563 &mut em,
1564 &mut sig,
1565 );
1566 assert!(matches!(result, Err(Error::OutputBufferTooSmall)));
1567 }
1568
1569 #[test]
1570 fn pkcs1v15_sign_into_propagates_message_too_long() {
1571 use crate::algorithms::pkcs1v15::sign_into;
1574 let n_params = toy_params_wide();
1575 let (d, e) = dummy_de();
1576 let mut em = [0u8; SMALL_K];
1577 let mut sig = [0u8; SMALL_K];
1578 let oversize_prefix = [0u8; SMALL_K]; let result = sign_into(
1580 &n_params,
1581 &d,
1582 &e,
1583 &oversize_prefix,
1584 &[0u8; 20],
1585 SMALL_K,
1586 &mut em,
1587 &mut sig,
1588 );
1589 assert!(matches!(result, Err(Error::MessageTooLong)));
1590 }
1591
1592 #[test]
1593 fn pss_sign_into_rejects_wrong_k() {
1594 use crate::algorithms::pss::sign_into;
1595 use digest::Digest;
1596 use sha1::Sha1;
1597 let n_params = toy_params_wide();
1598 let (d, e) = dummy_de();
1599 let mut em = [0u8; SMALL_K];
1600 let mut sig = [0u8; SMALL_K];
1601 let mut hash = Sha1::new();
1602 let result = sign_into(
1603 &n_params,
1604 &d,
1605 &e,
1606 &[0u8; 20],
1607 &[],
1608 SMALL_K - 1, &mut hash,
1610 &mut em,
1611 &mut sig,
1612 );
1613 assert!(matches!(result, Err(Error::InvalidArguments)));
1614 }
1615
1616 #[test]
1617 fn pss_sign_into_rejects_small_sig_storage() {
1618 use crate::algorithms::pss::sign_into;
1619 use digest::Digest;
1620 use sha1::Sha1;
1621 let n_params = toy_params_wide();
1622 let (d, e) = dummy_de();
1623 let mut em = [0u8; SMALL_K];
1624 let mut sig = [0u8; SMALL_K - 1];
1625 let mut hash = Sha1::new();
1626 let result = sign_into(
1627 &n_params,
1628 &d,
1629 &e,
1630 &[0u8; 20],
1631 &[],
1632 SMALL_K,
1633 &mut hash,
1634 &mut em,
1635 &mut sig,
1636 );
1637 assert!(matches!(result, Err(Error::OutputBufferTooSmall)));
1638 }
1639
1640 #[test]
1641 fn pss_sign_into_rejects_small_em_storage() {
1642 use crate::algorithms::pss::sign_into;
1643 use digest::Digest;
1644 use sha1::Sha1;
1645 let n_params = toy_params_wide();
1646 let (d, e) = dummy_de();
1647 let mut em = [0u8; SMALL_K - 1];
1649 let mut sig = [0u8; SMALL_K];
1650 let mut hash = Sha1::new();
1651 let result = sign_into(
1652 &n_params,
1653 &d,
1654 &e,
1655 &[0u8; 20],
1656 &[],
1657 SMALL_K,
1658 &mut hash,
1659 &mut em,
1660 &mut sig,
1661 );
1662 assert!(matches!(result, Err(Error::OutputBufferTooSmall)));
1663 }
1664
1665 #[test]
1666 fn pss_sign_into_rejects_wrong_hash_length() {
1667 use crate::algorithms::pss::sign_into;
1670 use digest::Digest;
1671 use sha1::Sha1;
1672 let n_params = toy_params_wide();
1673 let (d, e) = dummy_de();
1674 let mut em = [0u8; SMALL_K];
1675 let mut sig = [0u8; SMALL_K];
1676 let mut hash = Sha1::new();
1677 let result = sign_into(
1678 &n_params,
1679 &d,
1680 &e,
1681 &[0u8; 21], &[],
1683 SMALL_K,
1684 &mut hash,
1685 &mut em,
1686 &mut sig,
1687 );
1688 assert!(matches!(result, Err(Error::InputNotHashed)));
1689 }
1690
1691 #[test]
1696 fn pkcs1v15_sign_into_k_uses_modulus_bits_not_container() {
1697 use crate::algorithms::pkcs1v15::sign_into;
1698 type WideUCt = FixedUInt<u8, 128, Ct>;
1700 let mut mod_bytes = [0u8; 128];
1701 mod_bytes[64] = 0x80; mod_bytes[127] = 0x01; let n = <WideUCt as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&mod_bytes).unwrap();
1704 let n_params = ModMathParams::<WideUCt, Ct>::new(n).unwrap();
1705 let d = wrap_value(WideUCt::from(29u8));
1706 let e = wrap_value(WideUCt::from(5u8));
1707
1708 const CORRECT_K: usize = 64; const CONTAINER_K: usize = 128; let mut em = [0u8; CORRECT_K];
1715 let mut sig = [0u8; CORRECT_K];
1716 let result = sign_into(
1717 &n_params,
1718 &d,
1719 &e,
1720 &[],
1721 &[0u8; 20],
1722 CORRECT_K,
1723 &mut em,
1724 &mut sig,
1725 );
1726 assert!(
1727 !matches!(result, Err(Error::InvalidArguments)),
1728 "correct k (= modulus_bits.div_ceil(8)) must pass the width check, got {:?}",
1729 result
1730 );
1731
1732 let mut em = [0u8; CONTAINER_K];
1734 let mut sig = [0u8; CONTAINER_K];
1735 let result = sign_into(
1736 &n_params,
1737 &d,
1738 &e,
1739 &[],
1740 &[0u8; 20],
1741 CONTAINER_K,
1742 &mut em,
1743 &mut sig,
1744 );
1745 assert!(matches!(result, Err(Error::InvalidArguments)));
1746 }
1747
1748 #[test]
1751 fn pkcs1v15_signing_key_round_trip_2048_sha1() {
1752 use crate::key::{GenericRsaPrivateKey, GenericRsaPublicKey};
1753 use crate::pkcs1v15::{GenericSignature, GenericSigningKey, GenericVerifyingKey};
1754 use digest::Digest;
1755 use sha1::Sha1;
1756 use signature::hazmat::PrehashVerifier;
1757
1758 type U2048 = FixedUInt<u8, 256, Ct>;
1759 const K: usize = 256;
1760
1761 let public =
1762 crate::modmath_support::public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1763 let public_clone: GenericRsaPublicKey<ModMathValue<U2048>, ModMathParams<U2048, Ct>> =
1764 public.clone();
1765 let d = wrap_value(
1766 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1767 );
1768 let priv_key = GenericRsaPrivateKey::from_public_and_d(public, d);
1769
1770 let signing_key = GenericSigningKey::<Sha1, _, _>::new(priv_key);
1771 let verifying_key = GenericVerifyingKey::<Sha1, _, _>::new(public_clone);
1772
1773 let msg: &[u8] = b"deterministic test message";
1774 let mut em_storage = [0u8; K];
1775 let mut sig_storage = [0u8; K];
1776 let sig_slice = signing_key
1777 .try_sign_into(msg, &mut em_storage, &mut sig_storage)
1778 .unwrap();
1779 assert_eq!(sig_slice.len(), K);
1780
1781 let sig_int =
1784 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(sig_slice).unwrap();
1785 let sig = GenericSignature::from(wrap_value(sig_int));
1786 let digest = Sha1::digest(msg);
1787 verifying_key.verify_prehash(&digest, &sig).unwrap();
1788 }
1789
1790 #[test]
1791 fn pkcs1v15_signing_key_rejects_wrong_prehash_length() {
1792 use crate::key::GenericRsaPrivateKey;
1793 use crate::pkcs1v15::GenericSigningKey;
1794 use sha1::Sha1;
1795
1796 type U2048 = FixedUInt<u8, 256, Ct>;
1797 const K: usize = 256;
1798
1799 let public =
1800 crate::modmath_support::public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1801 let d = wrap_value(
1802 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1803 );
1804 let priv_key = GenericRsaPrivateKey::from_public_and_d(public, d);
1805 let signing_key = GenericSigningKey::<Sha1, _, _>::new(priv_key);
1806
1807 let bad_prehash = [0u8; 21]; let mut em_storage = [0u8; K];
1809 let mut sig_storage = [0u8; K];
1810 let result =
1811 signing_key.try_sign_prehash_into(&bad_prehash, &mut em_storage, &mut sig_storage);
1812 assert!(matches!(result, Err(Error::InputNotHashed)));
1813 }
1814
1815 #[test]
1816 fn pss_signing_key_round_trip_2048_sha1() {
1817 use crate::algorithms::pss::emsa_pss_verify;
1818 use crate::key::GenericRsaPrivateKey;
1819 use crate::pss::GenericSigningKey;
1820 use digest::Digest;
1821 use sha1::Sha1;
1822
1823 type U2048 = FixedUInt<u8, 256, Ct>;
1824 const K: usize = 256;
1825 const KEY_BITS: usize = 2048;
1826
1827 let key =
1828 crate::modmath_support::public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1829 let d = wrap_value(
1830 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1831 );
1832 let priv_key = GenericRsaPrivateKey::from_public_and_d(key.clone(), d);
1833 let signing_key = GenericSigningKey::<Sha1, _, _>::new_with_salt_len(priv_key, 0);
1835
1836 let msg: &[u8] = b"pss-roundtrip test message";
1837 let digest = Sha1::digest(msg);
1838 let mut em_storage = [0u8; K];
1839 let mut sig_storage = [0u8; K];
1840 let sig_slice = signing_key
1841 .try_sign_prehash_with_salt_into(&digest, &[], &mut em_storage, &mut sig_storage)
1842 .unwrap();
1843 assert_eq!(sig_slice.len(), K);
1844
1845 let recovered = public_key_op_ct(&key, sig_slice).unwrap();
1849 let mut em_copy = [0u8; K];
1850 em_copy.copy_from_slice(recovered.as_ref());
1851 let mut verify_hash = Sha1::new();
1852 emsa_pss_verify(&digest, &mut em_copy, Some(0), &mut verify_hash, KEY_BITS).unwrap();
1853 }
1854
1855 #[test]
1860 fn pss_signing_key_try_sign_prehash_with_rng_into_round_trip_2048_sha1() {
1861 use crate::algorithms::pss::emsa_pss_verify;
1862 use crate::key::GenericRsaPrivateKey;
1863 use crate::pss::GenericSigningKey;
1864 use digest::Digest;
1865 use rand::rngs::ChaCha8Rng;
1866 use rand_core::SeedableRng;
1867 use sha1::Sha1;
1868
1869 type U2048 = FixedUInt<u8, 256, Ct>;
1870 const K: usize = 256;
1871 const KEY_BITS: usize = 2048;
1872
1873 let key =
1874 crate::modmath_support::public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1875 let d = wrap_value(
1876 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1877 );
1878 let priv_key = GenericRsaPrivateKey::from_public_and_d(key.clone(), d);
1879 let signing_key = GenericSigningKey::<Sha1, _, _>::new_with_salt_len(priv_key, 0);
1881
1882 let msg: &[u8] = b"pss-blinded-roundtrip test message";
1883 let digest = Sha1::digest(msg);
1884 let mut rng = ChaCha8Rng::from_seed([42; 32]);
1885 let mut em_storage = [0u8; K];
1886 let mut sig_storage = [0u8; K];
1887 let mut salt_storage = [0u8; 0];
1888 let sig_slice = signing_key
1889 .try_sign_prehash_with_rng_into(
1890 &mut rng,
1891 &digest,
1892 &mut em_storage,
1893 &mut sig_storage,
1894 &mut salt_storage,
1895 )
1896 .unwrap();
1897 assert_eq!(sig_slice.len(), K);
1898
1899 let recovered = public_key_op_ct(&key, sig_slice).unwrap();
1900 let mut em_copy = [0u8; K];
1901 em_copy.copy_from_slice(recovered.as_ref());
1902 let mut verify_hash = Sha1::new();
1903 emsa_pss_verify(&digest, &mut em_copy, Some(0), &mut verify_hash, KEY_BITS).unwrap();
1904 }
1905
1906 #[test]
1907 fn pss_signing_key_rejects_wrong_prehash_length() {
1908 use crate::key::GenericRsaPrivateKey;
1909 use crate::pss::GenericSigningKey;
1910 use sha1::Sha1;
1911
1912 type U2048 = FixedUInt<u8, 256, Ct>;
1913 const K: usize = 256;
1914
1915 let key =
1916 crate::modmath_support::public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1917 let d = wrap_value(
1918 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1919 );
1920 let priv_key = GenericRsaPrivateKey::from_public_and_d(key, d);
1921 let signing_key = GenericSigningKey::<Sha1, _, _>::new_with_salt_len(priv_key, 0);
1922
1923 let bad_prehash = [0u8; 21]; let mut em_storage = [0u8; K];
1925 let mut sig_storage = [0u8; K];
1926 let result = signing_key.try_sign_prehash_with_salt_into(
1927 &bad_prehash,
1928 &[],
1929 &mut em_storage,
1930 &mut sig_storage,
1931 );
1932 assert!(matches!(result, Err(Error::InputNotHashed)));
1933 }
1934
1935 #[test]
1936 fn pss_signing_key_rejects_salt_len_mismatch() {
1937 use crate::key::GenericRsaPrivateKey;
1938 use crate::pss::GenericSigningKey;
1939 use sha1::Sha1;
1940
1941 type U2048 = FixedUInt<u8, 256, Ct>;
1942 const K: usize = 256;
1943
1944 let key =
1945 crate::modmath_support::public_key_ct_from_be_bytes::<U2048>(&N_2048, 65537).unwrap();
1946 let d = wrap_value(
1947 <U2048 as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(&D_2048).unwrap(),
1948 );
1949 let priv_key = GenericRsaPrivateKey::from_public_and_d(key, d);
1950 let signing_key = GenericSigningKey::<Sha1, _, _>::new_with_salt_len(priv_key, 20);
1952
1953 let prehash = [0u8; 20];
1954 let wrong_salt = [0u8; 16];
1955 let mut em_storage = [0u8; K];
1956 let mut sig_storage = [0u8; K];
1957 let result = signing_key.try_sign_prehash_with_salt_into(
1958 &prehash,
1959 &wrong_salt,
1960 &mut em_storage,
1961 &mut sig_storage,
1962 );
1963 assert!(matches!(result, Err(Error::InvalidArguments)));
1964 }
1965
1966 #[test]
1967 fn pss_signing_key_satisfies_zeroize() {
1968 use crate::key::GenericRsaPrivateKey;
1969 use crate::pss::GenericSigningKey;
1970 use sha1::Sha1;
1971 fn assert_zeroize<Z: Zeroize>() {}
1972 assert_zeroize::<
1973 GenericSigningKey<Sha1, ModMathValue<SmallUCt>, ModMathParams<SmallUCt, Ct>>,
1974 >();
1975
1976 let public =
1977 crate::modmath_support::public_key_ct_from_be_bytes::<SmallUCt>(&[35u8], 5).unwrap();
1978 let priv_key =
1979 GenericRsaPrivateKey::from_public_and_d(public, wrap_value(SmallUCt::from(29u8)));
1980 let mut signing_key = GenericSigningKey::<Sha1, _, _>::new(priv_key);
1981 signing_key.zeroize();
1982 }
1983
1984 #[test]
1985 fn pkcs1v15_signing_key_satisfies_zeroize() {
1986 use crate::key::GenericRsaPrivateKey;
1987 use crate::pkcs1v15::GenericSigningKey;
1988 use sha1::Sha1;
1989 fn assert_zeroize<Z: Zeroize>() {}
1990 assert_zeroize::<
1991 GenericSigningKey<Sha1, ModMathValue<SmallUCt>, ModMathParams<SmallUCt, Ct>>,
1992 >();
1993
1994 let public =
1997 crate::modmath_support::public_key_ct_from_be_bytes::<SmallUCt>(&[35u8], 5).unwrap();
1998 let priv_key =
1999 GenericRsaPrivateKey::from_public_and_d(public, wrap_value(SmallUCt::from(29u8)));
2000 let mut signing_key = GenericSigningKey::<Sha1, _, _>::new(priv_key);
2001 signing_key.zeroize();
2002 }
2003
2004 #[test]
2005 fn generic_rsa_private_key_satisfies_traits() {
2006 use crate::key::GenericRsaPrivateKey;
2011 use crate::traits::keys::{PrivateKeyParts, PublicKeyParts};
2012 fn assert_pub_parts<K, T>(_: &K)
2013 where
2014 T: UnsignedModularInt,
2015 K: PublicKeyParts<T>,
2016 {
2017 }
2018 fn assert_priv_parts<K, T>(_: &K)
2019 where
2020 T: UnsignedModularInt,
2021 K: PrivateKeyParts<T>,
2022 {
2023 }
2024
2025 let public =
2028 crate::modmath_support::public_key_ct_from_be_bytes::<SmallUCt>(&[35u8], 5).unwrap();
2029 let key = GenericRsaPrivateKey::from_public_and_d(public, wrap_value(SmallUCt::from(29u8)));
2030
2031 assert_pub_parts::<_, ModMathValue<SmallUCt>>(&key);
2032 assert_priv_parts::<_, ModMathValue<SmallUCt>>(&key);
2033
2034 assert_eq!(PrivateKeyParts::d(&key), &wrap_value(SmallUCt::from(29u8)));
2036 assert_eq!(key.as_public().e(), &wrap_value(SmallUCt::from(5u8)));
2037 }
2038}