1#![allow(missing_docs)]
6
7#[cfg(feature = "alloc")]
8use alloc::boxed::Box;
9use core::ops::{Shr, ShrAssign};
10
11use fixed_bigint::{Ct, Nct, Personality};
12use modmath::{CiosMontMul, CiosMontMulCt, Field as ModmathField, Parity, WideMul};
13use num_traits::ops::overflowing::OverflowingAdd;
14use num_traits::ops::wrapping::{WrappingAdd, WrappingMul, WrappingSub};
15use num_traits::{One, Zero};
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{
45}
46
47impl<T> ModMathInt for T where
48 T: FixedWidthUnsignedInt
49 + From<u8>
50 + PartialEq
51 + PartialOrd
52 + One
53 + Zero
54 + Parity
55 + OverflowingAdd
56 + WideMul
57 + CiosMontMul
58 + WrappingAdd
59 + WrappingMul
60 + WrappingSub
61 + Shr<usize, Output = Self>
62 + ShrAssign<usize>
63{
64}
65
66pub trait ModMathIntCt:
67 FixedWidthUnsignedInt
68 + From<u8>
69 + PartialEq
70 + PartialOrd
71 + One
72 + Zero
73 + Parity
74 + OverflowingAdd
75 + WideMul
76 + CiosMontMulCt
77 + WrappingAdd
78 + WrappingMul
79 + WrappingSub
80 + Shr<usize, Output = Self>
81 + ShrAssign<usize>
82 + subtle::ConditionallySelectable
83 + subtle::ConstantTimeLess
84 + core::ops::BitAnd<Output = Self>
85{
86}
87
88impl<T> ModMathIntCt for T where
89 T: FixedWidthUnsignedInt
90 + From<u8>
91 + PartialEq
92 + PartialOrd
93 + One
94 + Zero
95 + Parity
96 + OverflowingAdd
97 + WideMul
98 + CiosMontMulCt
99 + WrappingAdd
100 + WrappingMul
101 + WrappingSub
102 + Shr<usize, Output = Self>
103 + ShrAssign<usize>
104 + subtle::ConditionallySelectable
105 + subtle::ConstantTimeLess
106 + core::ops::BitAnd<Output = Self>
107{
108}
109
110#[cfg(feature = "alloc")]
111fn wrap_value<T>(value: T) -> ModMathValue<T> {
112 ModMathValue(value)
113}
114
115#[cfg(not(feature = "alloc"))]
116fn wrap_value<T>(value: T) -> ModMathValue<T> {
117 value
118}
119
120#[cfg(feature = "alloc")]
121fn unwrap_value<T: Copy>(value: &ModMathValue<T>) -> T {
122 value.0
123}
124
125#[cfg(feature = "alloc")]
126fn unwrap_value_ref<T>(value: &ModMathValue<T>) -> &T {
127 &value.0
128}
129
130#[cfg(not(feature = "alloc"))]
131fn unwrap_value_ref<T>(value: &ModMathValue<T>) -> &T {
132 value
133}
134
135#[cfg(not(feature = "alloc"))]
136fn unwrap_value<T: Copy>(value: &ModMathValue<T>) -> T {
137 *value
138}
139
140#[cfg(feature = "alloc")]
141#[repr(transparent)]
142#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
143pub struct ModMathValue<T>(pub T);
144
145#[cfg(feature = "alloc")]
146impl<T> ModMathValue<T> {
147 pub fn from_inner(inner: T) -> Self {
148 Self(inner)
149 }
150
151 pub fn inner(&self) -> &T {
152 &self.0
153 }
154}
155
156#[cfg(feature = "alloc")]
157impl<T> Zeroize for ModMathValue<T>
158where
159 T: Zeroize,
160{
161 fn zeroize(&mut self) {
162 self.0.zeroize();
163 }
164}
165
166#[cfg(feature = "alloc")]
167impl<T> From<u8> for ModMathValue<T>
168where
169 T: From<u8>,
170{
171 fn from(value: u8) -> Self {
172 Self(<T as From<u8>>::from(value))
173 }
174}
175
176#[cfg(feature = "alloc")]
177impl<T> IntegerResize for ModMathValue<T>
178where
179 T: FixedWidthUnsignedInt + PartialOrd,
180{
181 type Output = Self;
182
183 fn resize_unchecked(self, _at_least_bits_precision: u32) -> Self::Output {
184 self
185 }
186
187 fn try_resize(self, at_least_bits_precision: u32) -> Option<Self::Output> {
188 let value_bits = self.bits_precision() - self.leading_zeros();
194 if value_bits <= at_least_bits_precision {
195 Some(self)
196 } else {
197 None
198 }
199 }
200}
201
202#[cfg(feature = "alloc")]
203impl<T> UnsignedModularInt for ModMathValue<T>
204where
205 T: FixedWidthUnsignedInt + PartialOrd,
206{
207 type Bytes = <T as FixedWidthUnsignedInt>::Bytes;
208
209 fn leading_zeros(&self) -> u32 {
210 FixedWidthUnsignedInt::leading_zeros(&self.0)
211 }
212
213 fn to_be_bytes(&self) -> Self::Bytes {
214 FixedWidthUnsignedInt::to_be_bytes(&self.0)
215 }
216
217 #[cfg(feature = "alloc")]
218 fn to_be_bytes_trimmed_vartime(&self) -> Box<[u8]> {
219 let bytes = self.to_be_bytes();
220 let bytes = bytes.as_ref();
221 let first_non_zero = bytes
222 .iter()
223 .position(|b| *b != 0)
224 .unwrap_or(bytes.len().saturating_sub(1));
225 bytes[first_non_zero..].to_vec().into_boxed_slice()
226 }
227
228 fn as_nz_ref(&self) -> NonZero<Self> {
229 NonZero::new(*self).expect("value is non-zero")
230 }
231
232 fn bits(&self) -> u32 {
233 self.bits_precision() - self.leading_zeros()
234 }
235
236 fn bits_precision(&self) -> u32 {
237 FixedWidthUnsignedInt::bits_precision(&self.0)
238 }
239}
240
241#[cfg(feature = "alloc")]
242impl<T> TryFromBeBytes for ModMathValue<T>
243where
244 T: FixedWidthUnsignedInt,
245{
246 fn try_from_be_bytes_vartime(bytes: &[u8]) -> Result<Self> {
247 Ok(Self(
248 <T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(bytes)?,
249 ))
250 }
251}
252
253#[cfg(not(feature = "alloc"))]
254pub type ModMathValue<T> = T;
255
256#[derive(Clone, Debug)]
257pub struct ModMathParams<T, P: Personality = Nct> {
258 field: ModmathField<T, P>,
262 modulus_odd: Odd<ModMathValue<T>>,
268}
269
270impl<T: ModMathInt> ModMathParams<T, Nct> {
271 pub fn new(modulus: T) -> Result<Self> {
272 let field = ModmathField::<T, Nct>::new(modulus).ok_or(Error::InvalidModulus)?;
273 let modulus_odd = Odd::new(wrap_value(modulus)).ok_or(Error::InvalidModulus)?;
274 Ok(Self { field, modulus_odd })
275 }
276}
277
278impl<T: ModMathIntCt> ModMathParams<T, Ct> {
279 pub fn new(modulus: T) -> Result<Self> {
282 let field = ModmathField::<T, Ct>::new(modulus).ok_or(Error::InvalidModulus)?;
283 let modulus_odd = Odd::new(wrap_value(modulus)).ok_or(Error::InvalidModulus)?;
284 Ok(Self { field, modulus_odd })
285 }
286}
287
288impl<T, P: Personality> ModMathParams<T, P> {
289 pub(crate) fn field(&self) -> &ModmathField<T, P> {
290 &self.field
291 }
292}
293
294pub fn public_key_from_be_bytes<T>(
297 modulus: &[u8],
298 exponent: u32,
299) -> Result<GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Nct>>>
300where
301 T: ModMathInt,
302{
303 let n = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
304 modulus,
305 )?);
306 let exponent = exponent.to_be_bytes();
307 let e = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
308 &exponent,
309 )?);
310 GenericRsaPublicKey::from_components(n, e, ModMathParams::<T, Nct>::new(unwrap_value(&n))?)
311}
312
313pub fn rsa_public_op<T>(
316 key: &GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Nct>>,
317 input: &[u8],
318) -> Result<<ModMathValue<T> as UnsignedModularInt>::Bytes>
319where
320 T: ModMathInt,
321{
322 let input = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
323 input,
324 )?);
325 Ok(rsa_encrypt(key, &input)?.to_be_bytes())
326}
327
328pub fn public_key_ct_from_be_bytes<T>(
333 modulus: &[u8],
334 exponent: u32,
335) -> Result<GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Ct>>>
336where
337 T: ModMathIntCt,
338{
339 let n = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
340 modulus,
341 )?);
342 let exponent = exponent.to_be_bytes();
343 let e = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
344 &exponent,
345 )?);
346 GenericRsaPublicKey::from_components(n, e, ModMathParams::<T, Ct>::new(unwrap_value(&n))?)
347}
348
349pub fn rsa_public_op_ct<T>(
350 key: &GenericRsaPublicKey<ModMathValue<T>, ModMathParams<T, Ct>>,
351 input: &[u8],
352) -> Result<<ModMathValue<T> as UnsignedModularInt>::Bytes>
353where
354 T: ModMathIntCt,
355{
356 let input = wrap_value(<T as FixedWidthUnsignedInt>::try_from_be_bytes_vartime(
357 input,
358 )?);
359 Ok(rsa_encrypt(key, &input)?.to_be_bytes())
360}
361
362#[derive(Clone, Debug)]
363pub struct ModMathForm<T, P: Personality = Nct>
364where
365 T: Clone,
366{
367 integer_mont: ModMathValue<T>,
368 params: ModMathParams<T, P>,
369}
370
371impl<T: ModMathInt> IntoMontyForm<ModMathParams<T, Nct>> for ModMathForm<T, Nct> {
372 fn from_reduced(integer: ModMathValue<T>, params: &ModMathParams<T, Nct>) -> Self {
373 let field = params.field();
374 let r = field.reduce(unwrap_value_ref(&integer));
375 Self {
376 integer_mont: wrap_value(r.mont_value()),
377 params: params.clone(),
378 }
379 }
380
381 fn from_value(integer: ModMathValue<T>, params: &ModMathParams<T, Nct>) -> Self {
385 Self::from_reduced(integer, params)
386 }
387}
388
389impl<T: ModMathInt> ModMathForm<T, Nct> {
390 fn pow_loop(&self, exp_raw: T) -> T {
391 let field = self.params.field();
392 let base = field.residue_from_mont(unwrap_value(&self.integer_mont));
393 field.exp(&base, &exp_raw).mont_value()
394 }
395
396 fn to_reduced(&self) -> T {
397 let field = self.params.field();
398 let r = field.residue_from_mont(unwrap_value(&self.integer_mont));
399 field.into_raw(&r)
400 }
401}
402
403impl<T: ModMathInt> Pow<ModMathParams<T, Nct>> for ModMathForm<T, Nct> {
404 fn pow(&self, exp: &ModMathValue<T>) -> Self {
405 let result_mont = self.pow_loop(unwrap_value(exp));
406 Self {
407 integer_mont: wrap_value(result_mont),
408 params: self.params.clone(),
409 }
410 }
411}
412
413impl<T: ModMathInt> PowBoundedExp<ModMathParams<T, Nct>> for ModMathForm<T, Nct> {
414 fn pow_bounded_exp(&self, exp: &ModMathValue<T>, _exp_bits: u32) -> Self {
415 let result_mont = self.pow_loop(unwrap_value(exp));
418 Self {
419 integer_mont: wrap_value(result_mont),
420 params: self.params.clone(),
421 }
422 }
423
424 fn retrieve(&self) -> ModMathValue<T> {
425 wrap_value(self.to_reduced())
426 }
427}
428
429impl<T: ModMathInt> ModulusParams for ModMathParams<T, Nct> {
430 type Modulus = ModMathValue<T>;
431 type MontgomeryForm = ModMathForm<T, Nct>;
432
433 fn modulus(&self) -> &Odd<Self::Modulus> {
434 &self.modulus_odd
435 }
436
437 fn bits_precision(&self) -> u32 {
438 FixedWidthUnsignedInt::bits_precision(self.field.modulus())
439 }
440}
441
442impl<T: ModMathIntCt> IntoMontyForm<ModMathParams<T, Ct>> for ModMathForm<T, Ct> {
443 fn from_reduced(integer: ModMathValue<T>, params: &ModMathParams<T, Ct>) -> Self {
444 let field = params.field();
445 let r = field.reduce(unwrap_value_ref(&integer));
446 Self {
447 integer_mont: wrap_value(r.mont_value()),
448 params: params.clone(),
449 }
450 }
451
452 fn from_value(integer: ModMathValue<T>, params: &ModMathParams<T, Ct>) -> Self {
455 Self::from_reduced(integer, params)
456 }
457}
458
459impl<T: ModMathIntCt> ModMathForm<T, Ct> {
460 fn pow_loop(&self, exp_raw: T) -> T {
461 let field = self.params.field();
462 let base = field.residue_from_mont(unwrap_value(&self.integer_mont));
463 field.exp_public_exp(&base, &exp_raw).mont_value()
464 }
465
466 fn to_reduced(&self) -> T {
467 let field = self.params.field();
468 let r = field.residue_from_mont(unwrap_value(&self.integer_mont));
469 field.into_raw(&r)
470 }
471}
472
473impl<T: ModMathIntCt> Pow<ModMathParams<T, Ct>> for ModMathForm<T, Ct> {
474 fn pow(&self, exp: &ModMathValue<T>) -> Self {
475 let result_mont = self.pow_loop(unwrap_value(exp));
476 Self {
477 integer_mont: wrap_value(result_mont),
478 params: self.params.clone(),
479 }
480 }
481}
482
483impl<T: ModMathIntCt> PowBoundedExp<ModMathParams<T, Ct>> for ModMathForm<T, Ct> {
484 fn pow_bounded_exp(&self, exp: &ModMathValue<T>, _exp_bits: u32) -> Self {
485 let result_mont = self.pow_loop(unwrap_value(exp));
486 Self {
487 integer_mont: wrap_value(result_mont),
488 params: self.params.clone(),
489 }
490 }
491
492 fn retrieve(&self) -> ModMathValue<T> {
493 wrap_value(self.to_reduced())
494 }
495}
496
497impl<T: ModMathIntCt> ModulusParams for ModMathParams<T, Ct> {
498 type Modulus = ModMathValue<T>;
499 type MontgomeryForm = ModMathForm<T, Ct>;
500
501 fn modulus(&self) -> &Odd<Self::Modulus> {
502 &self.modulus_odd
503 }
504
505 fn bits_precision(&self) -> u32 {
506 FixedWidthUnsignedInt::bits_precision(self.field.modulus())
507 }
508}
509
510#[cfg(test)]
511#[cfg(all(feature = "alloc", feature = "private-key"))]
512mod tests {
513 use fixed_bigint::{Ct, FixedUInt};
514 use rand::rngs::ChaCha8Rng;
515 use rand_core::SeedableRng;
516 use sha1::Sha1;
517 use signature::hazmat::PrehashVerifier;
518
519 use super::{
520 public_key_ct_from_be_bytes, public_key_from_be_bytes, ModMathParams, ModMathValue,
521 };
522 use crate::key::GenericRsaPublicKey;
523 use crate::pkcs1v15::{GenericEncryptingKey, GenericSignature, GenericVerifyingKey};
524 use crate::{traits::RandomizedEncryptor, BoxedUint, Pkcs1v15Encrypt, RsaPublicKey};
525
526 type SmallU = FixedUInt<u8, 64>;
527 type SmallUCt = FixedUInt<u8, 64, Ct>;
528
529 #[test]
530 fn brand_round_trip() {
531 let params = ModMathParams::<SmallU>::new(SmallU::from(13u8)).unwrap();
532 let f = params.field();
533 let r = f.reduce(&SmallU::from(7u8));
534 assert_eq!(f.into_raw(&r), SmallU::from(7u8));
535 }
536
537 #[test]
538 fn brand_mul_exp() {
539 let params = ModMathParams::<SmallU>::new(SmallU::from(13u8)).unwrap();
540 let f = params.field();
541 let a = f.reduce(&SmallU::from(7u8));
543 let b = f.reduce(&SmallU::from(11u8));
544 assert_eq!(f.into_raw(&f.mul(&a, &b)), SmallU::from(12u8));
545 let base = f.reduce(&SmallU::from(2u8));
547 assert_eq!(
548 f.into_raw(&f.exp(&base, &SmallU::from(10u8))),
549 SmallU::from(10u8)
550 );
551 }
552
553 #[test]
554 fn brand_ct_matches_nct() {
555 let p_nct = ModMathParams::<SmallU>::new(SmallU::from(13u8)).unwrap();
556 let p_ct = ModMathParams::<SmallUCt, Ct>::new(SmallUCt::from(13u8)).unwrap();
557 let f_nct = p_nct.field();
558 let f_ct = p_ct.field();
559 let nct = f_nct.into_raw(&f_nct.mul(
560 &f_nct.reduce(&SmallU::from(7u8)),
561 &f_nct.reduce(&SmallU::from(11u8)),
562 ));
563 let ct = f_ct.into_raw(&f_ct.mul(
564 &f_ct.reduce(&SmallUCt::from(7u8)),
565 &f_ct.reduce(&SmallUCt::from(11u8)),
566 ));
567 let mut nct_bytes = [0u8; 64];
569 let mut ct_bytes = [0u8; 64];
570 let _ = nct.to_be_bytes(&mut nct_bytes);
571 let _ = ct.to_be_bytes(&mut ct_bytes);
572 assert_eq!(nct_bytes, ct_bytes);
573 }
574
575 #[test]
576 fn verify_pkcs1v15_signature_with_modmath_fixed_uint() {
577 type U512 = FixedUInt<u8, 64>;
578
579 let digest: [u8; 20] = [
580 0x43, 0x0c, 0xe3, 0x4d, 0x02, 0x07, 0x24, 0xed, 0x75, 0xa1, 0x96, 0xdf, 0xc2, 0xad,
581 0x67, 0xc7, 0x77, 0x72, 0xd1, 0x69,
582 ];
583 let modulus: [u8; 64] = [
584 0x96, 0x9D, 0x03, 0xFF, 0xA9, 0x8D, 0x88, 0x8F, 0x3A, 0xA4, 0xF2, 0xFE, 0xD2, 0x32,
585 0xE6, 0x1C, 0x4A, 0xCF, 0x06, 0x63, 0xA9, 0x2F, 0x99, 0x03, 0x4C, 0xF7, 0xB7, 0x24,
586 0x5A, 0x1A, 0x1E, 0x5E, 0xAF, 0xA5, 0x65, 0xAF, 0xB9, 0x0B, 0xAB, 0x22, 0x85, 0x71,
587 0x2F, 0xAA, 0x50, 0x39, 0x39, 0xA0, 0x65, 0xFB, 0x60, 0xDD, 0x08, 0x28, 0xA3, 0x84,
588 0xF2, 0x6D, 0x8A, 0xFC, 0x28, 0x6D, 0xF6, 0xCF,
589 ];
590 let signature: [u8; 64] = [
591 0x45, 0x53, 0xF3, 0xAF, 0x16, 0xAF, 0x63, 0x97, 0xB0, 0xD3, 0x2F, 0x8A, 0xEC, 0xD5,
592 0x4C, 0xF1, 0xF3, 0xD0, 0x0C, 0x9F, 0x42, 0xDC, 0x68, 0xCB, 0xD7, 0x05, 0xCE, 0xA5,
593 0xA9, 0x70, 0x95, 0x3E, 0xC0, 0xBC, 0x4A, 0x18, 0xED, 0x91, 0xA3, 0x5D, 0x66, 0xEC,
594 0xDA, 0x4A, 0x83, 0x32, 0xCF, 0xC3, 0xA3, 0xAB, 0x21, 0xAD, 0x59, 0xB2, 0x2E, 0x87,
595 0xC2, 0x73, 0xFF, 0x08, 0x88, 0xDD, 0x4D, 0xE0,
596 ];
597
598 let key = public_key_from_be_bytes::<U512>(&modulus, 3).unwrap();
599 let verifying_key = GenericVerifyingKey::<Sha1, _, _>::new(key);
600 let signature =
601 GenericSignature::from(ModMathValue::from_inner(U512::from_be_bytes(&signature)));
602 verifying_key.verify_prehash(&digest, &signature).unwrap();
603 }
604
605 #[test]
606 fn verify_pkcs1v15_signature_with_modmath_fixed_uint32() {
607 type U512 = FixedUInt<u32, 16>;
608
609 let digest: [u8; 20] = [
610 0x43, 0x0c, 0xe3, 0x4d, 0x02, 0x07, 0x24, 0xed, 0x75, 0xa1, 0x96, 0xdf, 0xc2, 0xad,
611 0x67, 0xc7, 0x77, 0x72, 0xd1, 0x69,
612 ];
613 let modulus: [u8; 64] = [
614 0x96, 0x9D, 0x03, 0xFF, 0xA9, 0x8D, 0x88, 0x8F, 0x3A, 0xA4, 0xF2, 0xFE, 0xD2, 0x32,
615 0xE6, 0x1C, 0x4A, 0xCF, 0x06, 0x63, 0xA9, 0x2F, 0x99, 0x03, 0x4C, 0xF7, 0xB7, 0x24,
616 0x5A, 0x1A, 0x1E, 0x5E, 0xAF, 0xA5, 0x65, 0xAF, 0xB9, 0x0B, 0xAB, 0x22, 0x85, 0x71,
617 0x2F, 0xAA, 0x50, 0x39, 0x39, 0xA0, 0x65, 0xFB, 0x60, 0xDD, 0x08, 0x28, 0xA3, 0x84,
618 0xF2, 0x6D, 0x8A, 0xFC, 0x28, 0x6D, 0xF6, 0xCF,
619 ];
620 let signature: [u8; 64] = [
621 0x45, 0x53, 0xF3, 0xAF, 0x16, 0xAF, 0x63, 0x97, 0xB0, 0xD3, 0x2F, 0x8A, 0xEC, 0xD5,
622 0x4C, 0xF1, 0xF3, 0xD0, 0x0C, 0x9F, 0x42, 0xDC, 0x68, 0xCB, 0xD7, 0x05, 0xCE, 0xA5,
623 0xA9, 0x70, 0x95, 0x3E, 0xC0, 0xBC, 0x4A, 0x18, 0xED, 0x91, 0xA3, 0x5D, 0x66, 0xEC,
624 0xDA, 0x4A, 0x83, 0x32, 0xCF, 0xC3, 0xA3, 0xAB, 0x21, 0xAD, 0x59, 0xB2, 0x2E, 0x87,
625 0xC2, 0x73, 0xFF, 0x08, 0x88, 0xDD, 0x4D, 0xE0,
626 ];
627
628 let n = U512::from_be_bytes(&modulus);
629 let e = U512::from(3u8);
630 let key = GenericRsaPublicKey::from_components(
634 ModMathValue::from_inner(n),
635 ModMathValue::from_inner(e),
636 ModMathParams::<U512, fixed_bigint::Nct>::new(n).unwrap(),
637 )
638 .unwrap();
639 let verifying_key = GenericVerifyingKey::<Sha1, _, _>::new(key);
640 let signature =
641 GenericSignature::from(ModMathValue::from_inner(U512::from_be_bytes(&signature)));
642 verifying_key.verify_prehash(&digest, &signature).unwrap();
643 }
644
645 #[test]
646 fn encrypt_pkcs1v15_with_modmath_fixed_uint_matches_boxeduint() {
647 type U512 = FixedUInt<u8, 64, Ct>;
651
652 let modulus: [u8; 64] = [
653 0x96, 0x9D, 0x03, 0xFF, 0xA9, 0x8D, 0x88, 0x8F, 0x3A, 0xA4, 0xF2, 0xFE, 0xD2, 0x32,
654 0xE6, 0x1C, 0x4A, 0xCF, 0x06, 0x63, 0xA9, 0x2F, 0x99, 0x03, 0x4C, 0xF7, 0xB7, 0x24,
655 0x5A, 0x1A, 0x1E, 0x5E, 0xAF, 0xA5, 0x65, 0xAF, 0xB9, 0x0B, 0xAB, 0x22, 0x85, 0x71,
656 0x2F, 0xAA, 0x50, 0x39, 0x39, 0xA0, 0x65, 0xFB, 0x60, 0xDD, 0x08, 0x28, 0xA3, 0x84,
657 0xF2, 0x6D, 0x8A, 0xFC, 0x28, 0x6D, 0xF6, 0xCF,
658 ];
659 let msg = b"hello world!";
660
661 let modmath_key = public_key_ct_from_be_bytes::<U512>(&modulus, 3).unwrap();
662 let boxed_key = RsaPublicKey::new(
663 BoxedUint::from_be_slice(&modulus, 512).unwrap(),
664 3u64.into(),
665 )
666 .unwrap();
667
668 let mut modmath_rng = ChaCha8Rng::from_seed([42; 32]);
669 let mut boxed_rng = ChaCha8Rng::from_seed([42; 32]);
670 let mut storage = [0u8; 64];
671
672 let modmath_ciphertext = GenericEncryptingKey::new(modmath_key)
673 .encrypt_with_rng_into(&mut modmath_rng, msg, &mut storage)
674 .unwrap();
675 let boxed_ciphertext = boxed_key
676 .encrypt(&mut boxed_rng, Pkcs1v15Encrypt, msg)
677 .unwrap();
678
679 assert_eq!(modmath_ciphertext, boxed_ciphertext.as_slice());
680 }
681}