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