1#[cfg(feature = "private-key")]
13mod blinded_signing_key;
14mod signature;
15#[cfg(feature = "private-key")]
16mod signing_key;
17mod verifying_key;
18
19#[cfg(feature = "private-key")]
20pub use self::{blinded_signing_key::BlindedSigningKey, signing_key::SigningKey};
21
22#[cfg(feature = "alloc")]
23pub use self::{signature::Signature, verifying_key::VerifyingKey};
24pub use self::{
25 signature::{GenericSignature, SignatureBytes},
26 verifying_key::GenericVerifyingKey,
27};
28
29#[cfg(feature = "alloc")]
30use alloc::{vec, vec::Vec};
31use core::fmt::{self, Debug};
32#[cfg(feature = "alloc")]
33use crypto_bigint::BoxedUint;
34
35use digest::{Digest, FixedOutputReset};
36use rand_core::TryCryptoRng;
37
38#[cfg(feature = "alloc")]
39use crate::algorithms::pad::{uint_to_be_pad, uint_to_be_pad_into, uint_to_zeroizing_be_pad};
40use crate::algorithms::pss::*;
41#[cfg(feature = "private-key")]
42use crate::algorithms::rsa::rsa_decrypt_and_check;
43#[cfg(feature = "alloc")]
44use crate::algorithms::rsa::rsa_encrypt;
45use crate::errors::{Error, Result};
46use crate::traits::{PublicKeyParts, SignatureScheme, UnsignedModularInt};
47#[cfg(feature = "private-key")]
48use crate::RsaPrivateKey;
49#[cfg(feature = "alloc")]
50use crate::RsaPublicKey;
51
52#[cfg(feature = "encoding")]
53use {
54 crate::encoding::ID_RSASSA_PSS,
55 const_oid::AssociatedOid,
56 pkcs1::RsaPssParams,
57 spki::{der::Any, AlgorithmIdentifierOwned},
58};
59
60pub struct Pss<D> {
62 pub blinded: bool,
64
65 pub digest: D,
67
68 pub salt_len: Option<usize>,
71}
72
73impl<D> Default for Pss<D>
74where
75 D: Digest,
76{
77 fn default() -> Self {
78 Self::new()
79 }
80}
81
82impl<D> Pss<D>
83where
84 D: Digest,
85{
86 pub fn new() -> Self {
89 Self::new_with_salt(<D as Digest>::output_size())
90 }
91
92 pub fn new_with_salt(len: usize) -> Self {
94 Self {
95 blinded: false,
96 digest: D::new(),
97 salt_len: Some(len),
98 }
99 }
100
101 pub fn new_blinded() -> Self {
104 Self::new_blinded_with_salt(<D as Digest>::output_size())
105 }
106
107 pub fn new_blinded_with_salt(len: usize) -> Self {
110 Self {
111 blinded: true,
112 digest: D::new(),
113 salt_len: Some(len),
114 }
115 }
116}
117
118#[cfg(feature = "alloc")]
119impl<D> SignatureScheme for Pss<D>
120where
121 D: Digest + FixedOutputReset,
122{
123 #[cfg(feature = "private-key")]
124 fn sign<Rng: TryCryptoRng + ?Sized>(
125 mut self,
126 rng: Option<&mut Rng>,
127 priv_key: &RsaPrivateKey,
128 hashed: &[u8],
129 ) -> Result<Vec<u8>> {
130 sign(
131 rng.ok_or(Error::InvalidPaddingScheme)?,
132 self.blinded,
133 priv_key,
134 hashed,
135 self.salt_len.expect("salt_len to be Some"),
136 &mut self.digest,
137 )
138 }
139
140 fn verify<K, T>(mut self, pub_key: &K, hashed: &[u8], sig: &[u8]) -> Result<()>
141 where
142 T: UnsignedModularInt,
143 K: PublicKeyParts<T>,
144 {
145 if sig.len() != pub_key.size() {
146 return Err(Error::Verification);
147 }
148 let sig = T::try_from_be_bytes_vartime(sig).map_err(|_| Error::Verification)?;
149 if sig >= *pub_key.n().as_ref() || sig.bits_precision() != pub_key.n_bits_precision() {
150 return Err(Error::Verification);
151 }
152
153 let mut em = vec![0u8; pub_key.size()];
154 let em_len =
155 uint_to_be_pad_into(rsa_encrypt(pub_key, &sig)?, pub_key.size(), &mut em)?.len();
156
157 emsa_pss_verify(
158 hashed,
159 &mut em[..em_len],
160 self.salt_len,
161 &mut self.digest,
162 pub_key.n().bits() as _,
163 )
164 }
165}
166
167impl<D> Debug for Pss<D> {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 f.debug_struct("PSS")
170 .field("blinded", &self.blinded)
171 .field("digest", &"...")
172 .field("salt_len", &self.salt_len)
173 .finish()
174 }
175}
176
177#[cfg(feature = "alloc")]
178#[allow(dead_code)]
179pub(crate) fn verify<D>(
180 pub_key: &RsaPublicKey,
181 hashed: &[u8],
182 sig: &BoxedUint,
183 sig_len: usize,
184 digest: &mut D,
185 salt_len: Option<usize>,
186) -> Result<()>
187where
188 D: Digest + FixedOutputReset,
189{
190 if sig_len != pub_key.size() {
191 return Err(Error::Verification);
192 }
193 let raw = rsa_encrypt(pub_key, sig)?;
194 let mut em = uint_to_be_pad(raw, pub_key.size())?;
195
196 emsa_pss_verify(hashed, &mut em, salt_len, digest, pub_key.n().bits() as _)
197}
198
199#[cfg(feature = "alloc")]
200#[allow(dead_code)]
201pub(crate) fn verify_digest<D>(
202 pub_key: &RsaPublicKey,
203 hashed: &[u8],
204 sig: &BoxedUint,
205 salt_len: Option<usize>,
206) -> Result<()>
207where
208 D: Digest + FixedOutputReset,
209{
210 let mut storage = vec![0u8; pub_key.size()];
211 verify_digest_into::<D, _, BoxedUint>(pub_key, hashed, sig, salt_len, &mut storage)
212}
213
214pub fn verify_digest_into<D, K, T>(
216 pub_key: &K,
217 hashed: &[u8],
218 sig: &T,
219 salt_len: Option<usize>,
220 storage: &mut [u8],
221) -> crate::Result<()>
222where
223 D: digest::Digest + digest::FixedOutputReset,
224 K: crate::traits::PublicKeyParts<T>,
225 T: crate::traits::UnsignedModularInt,
226{
227 if sig >= pub_key.n().as_ref() || sig.bits_precision() != pub_key.n_bits_precision() {
228 return Err(crate::Error::Verification);
229 }
230 let padded_len = pub_key.size();
231 let em = crate::algorithms::pad::uint_to_be_pad_into(
232 crate::algorithms::rsa::rsa_encrypt(pub_key, sig)?,
233 padded_len,
234 storage,
235 )?;
236 let em_len = em.len();
239 emsa_pss_verify_digest::<D>(
240 hashed,
241 &mut storage[..em_len],
242 salt_len,
243 pub_key.n().bits() as _,
244 )
245}
246
247#[cfg(feature = "private-key")]
253pub(crate) fn sign<T, D>(
254 rng: &mut T,
255 blind: bool,
256 priv_key: &RsaPrivateKey,
257 hashed: &[u8],
258 salt_len: usize,
259 digest: &mut D,
260) -> Result<Vec<u8>>
261where
262 T: TryCryptoRng + ?Sized,
263 D: Digest + FixedOutputReset,
264{
265 let mut salt = vec![0; salt_len];
266 rng.try_fill_bytes(&mut salt[..]).map_err(|_| Error::Rng)?;
267
268 sign_pss_with_salt(blind.then_some(rng), priv_key, hashed, &salt, digest)
269}
270
271#[cfg(feature = "private-key")]
272pub(crate) fn sign_digest<T, D>(
273 rng: &mut T,
274 blind: bool,
275 priv_key: &RsaPrivateKey,
276 hashed: &[u8],
277 salt_len: usize,
278) -> Result<Vec<u8>>
279where
280 T: TryCryptoRng + ?Sized,
281 D: Digest + FixedOutputReset,
282{
283 let mut salt = vec![0; salt_len];
284 rng.try_fill_bytes(&mut salt[..]).map_err(|_| Error::Rng)?;
285
286 sign_pss_with_salt_digest::<_, D>(blind.then_some(rng), priv_key, hashed, &salt)
287}
288
289#[cfg(feature = "private-key")]
295fn sign_pss_with_salt<T, D>(
296 blind_rng: Option<&mut T>,
297 priv_key: &RsaPrivateKey,
298 hashed: &[u8],
299 salt: &[u8],
300 digest: &mut D,
301) -> Result<Vec<u8>>
302where
303 T: TryCryptoRng + ?Sized,
304 D: Digest + FixedOutputReset,
305{
306 let em_bits = priv_key.n().bits() - 1;
307
308 let em = emsa_pss_encode(hashed, em_bits as _, salt, digest)?;
309
310 let em = BoxedUint::from_be_slice(&em, priv_key.n_bits_precision())?;
311 let raw = rsa_decrypt_and_check(priv_key, blind_rng, &em)?;
312 uint_to_zeroizing_be_pad(raw, priv_key.size())
313}
314
315#[cfg(feature = "private-key")]
316fn sign_pss_with_salt_digest<T, D>(
317 blind_rng: Option<&mut T>,
318 priv_key: &RsaPrivateKey,
319 hashed: &[u8],
320 salt: &[u8],
321) -> Result<Vec<u8>>
322where
323 T: TryCryptoRng + ?Sized,
324 D: Digest + FixedOutputReset,
325{
326 let em_bits = priv_key.n().bits() - 1;
327 let em = emsa_pss_encode_digest::<D>(hashed, em_bits as _, salt)?;
328
329 let em = BoxedUint::from_be_slice(&em, priv_key.n_bits_precision())?;
330 uint_to_zeroizing_be_pad(
331 rsa_decrypt_and_check(priv_key, blind_rng, &em)?,
332 priv_key.size(),
333 )
334}
335
336#[cfg(feature = "encoding")]
338pub fn get_default_pss_signature_algo_id<D>() -> spki::Result<AlgorithmIdentifierOwned>
339where
340 D: Digest + AssociatedOid,
341{
342 let salt_len: u8 = <D as Digest>::output_size() as u8;
343 get_pss_signature_algo_id::<D>(salt_len)
344}
345
346#[cfg(feature = "encoding")]
347fn get_pss_signature_algo_id<D>(salt_len: u8) -> spki::Result<AlgorithmIdentifierOwned>
348where
349 D: Digest + AssociatedOid,
350{
351 let pss_params = RsaPssParams::new::<D>(salt_len);
352
353 Ok(AlgorithmIdentifierOwned {
354 oid: ID_RSASSA_PSS,
355 parameters: Some(Any::encode_from(&pss_params)?),
356 })
357}
358
359#[cfg(all(test, feature = "encoding"))]
360mod test {
361 use crate::pss::{BlindedSigningKey, Pss, Signature, SigningKey, VerifyingKey};
362 use crate::{RsaPrivateKey, RsaPublicKey};
363
364 use crate::traits::PublicKeyParts;
365 use hex_literal::hex;
366 use pkcs1::DecodeRsaPrivateKey;
367 use rand::rngs::ChaCha8Rng;
368 use rand_core::SeedableRng;
369 use rstest::rstest;
370 use sha1::{Digest, Sha1};
371 use signature::hazmat::{PrehashVerifier, RandomizedPrehashSigner};
372 use signature::{DigestVerifier, Keypair, RandomizedDigestSigner, RandomizedSigner, Verifier};
373
374 fn get_private_key() -> RsaPrivateKey {
375 let pem = r#"
387-----BEGIN RSA PRIVATE KEY-----
388MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
389fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu
390/ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu
391RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/
392EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A
393IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS
394tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V
395-----END RSA PRIVATE KEY-----"#;
396
397 RsaPrivateKey::from_pkcs1_pem(pem).unwrap()
398 }
399
400 #[rstest]
401 #[case(
402 "test\n",
403 hex!(
404 "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae"
405 "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962f"
406 ),
407 true,
408 )]
409 #[case(
410 "test\n",
411 hex!(
412 "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae"
413 "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962e"
414 ),
415 false,
416 )]
417 fn test_verify_pss(#[case] text: &str, #[case] sig: [u8; 64], #[case] expected: bool) {
418 let priv_key = get_private_key();
419 let pub_key: RsaPublicKey = priv_key.into();
420
421 let digest = Sha1::digest(text.as_bytes()).to_vec();
422 let result = pub_key.verify(Pss::<Sha1>::new(), &digest, &sig);
423
424 match expected {
425 true => result.expect("failed to verify"),
426 false => {
427 result.expect_err("expected verifying error");
428 }
429 }
430 }
431
432 #[rstest]
433 #[case(
434 "test\n",
435 hex!(
436 "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae"
437 "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962f"
438 ),
439 true,
440 )]
441 #[case(
442 "test\n",
443 hex!(
444 "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae"
445 "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962e"
446 ),
447 false,
448 )]
449 fn test_verify_pss_signer(#[case] text: &str, #[case] sig: [u8; 64], #[case] expected: bool) {
450 let priv_key = get_private_key();
451 let pub_key: RsaPublicKey = priv_key.into();
452 let verifying_key: VerifyingKey<Sha1> = VerifyingKey::new(pub_key);
453
454 let result = verifying_key.verify(
455 text.as_bytes(),
456 &Signature::try_from(sig.as_slice()).unwrap(),
457 );
458 match expected {
459 true => result.expect("failed to verify"),
460 false => {
461 result.expect_err("expected verifying error");
462 }
463 }
464 }
465
466 #[rstest]
467 #[case(
468 "test\n",
469 hex!(
470 "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae"
471 "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962f"
472 ),
473 true,
474 )]
475 #[case(
476 "test\n",
477 hex!(
478 "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae"
479 "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962e"
480 ),
481 false,
482 )]
483 fn test_verify_pss_digest_signer(
484 #[case] text: &str,
485 #[case] sig: [u8; 64],
486 #[case] expected: bool,
487 ) {
488 let priv_key = get_private_key();
489 let pub_key: RsaPublicKey = priv_key.into();
490 let verifying_key = VerifyingKey::new(pub_key);
491
492 let result = verifying_key.verify_digest(
493 |digest: &mut Sha1| {
494 digest.update(text.as_bytes());
495 Ok(())
496 },
497 &Signature::try_from(sig.as_slice()).unwrap(),
498 );
499 match expected {
500 true => result.expect("failed to verify"),
501 false => {
502 result.expect_err("expected verifying error");
503 }
504 }
505 }
506
507 #[rstest]
508 #[case("test\n")]
509 fn test_sign_and_verify_roundtrip(#[case] test: &str) {
510 let priv_key = get_private_key();
511
512 let mut rng = ChaCha8Rng::from_seed([42; 32]);
513
514 let digest = Sha1::digest(test.as_bytes()).to_vec();
515 let sig = priv_key
516 .sign_with_rng(&mut rng, Pss::<Sha1>::new(), &digest)
517 .expect("failed to sign");
518
519 priv_key
520 .to_public_key()
521 .verify(Pss::<Sha1>::new(), &digest, &sig)
522 .expect("failed to verify");
523 }
524
525 #[rstest]
526 #[case("test\n")]
527 fn test_sign_blinded_and_verify_roundtrip(#[case] test: &str) {
528 let priv_key = get_private_key();
529
530 let mut rng = ChaCha8Rng::from_seed([42; 32]);
531
532 let digest = Sha1::digest(test.as_bytes()).to_vec();
533 let sig = priv_key
534 .sign_with_rng(&mut rng, Pss::<Sha1>::new_blinded(), &digest)
535 .expect("failed to sign");
536
537 priv_key
538 .to_public_key()
539 .verify(Pss::<Sha1>::new(), &digest, &sig)
540 .expect("failed to verify");
541 }
542
543 #[rstest]
544 #[case("test\n")]
545 fn test_sign_and_verify_roundtrip_signer(#[case] test: &str) {
546 let priv_key = get_private_key();
547
548 let mut rng = ChaCha8Rng::from_seed([42; 32]);
549 let signing_key = SigningKey::<Sha1>::new(priv_key);
550 let verifying_key = signing_key.verifying_key();
551
552 let sig = signing_key.sign_with_rng(&mut rng, test.as_bytes());
553 verifying_key
554 .verify(test.as_bytes(), &sig)
555 .expect("failed to verify");
556 }
557
558 #[rstest]
559 #[case("test\n")]
560 fn test_sign_and_verify_roundtrip_blinded_signer(#[case] test: &str) {
561 let priv_key = get_private_key();
562
563 let mut rng = ChaCha8Rng::from_seed([42; 32]);
564 let signing_key = BlindedSigningKey::<Sha1>::new(priv_key);
565 let verifying_key = signing_key.verifying_key();
566
567 let sig = signing_key.sign_with_rng(&mut rng, test.as_bytes());
568 verifying_key
569 .verify(test.as_bytes(), &sig)
570 .expect("failed to verify");
571 }
572
573 #[rstest]
574 #[case("test\n")]
575 fn test_sign_and_verify_roundtrip_digest_signer(#[case] test: &str) {
576 let priv_key = get_private_key();
577
578 let mut rng = ChaCha8Rng::from_seed([42; 32]);
579 let signing_key = SigningKey::new(priv_key);
580 let verifying_key = signing_key.verifying_key();
581
582 let sig = signing_key
583 .sign_digest_with_rng(&mut rng, |digest: &mut Sha1| digest.update(test.as_bytes()));
584
585 verifying_key
586 .verify_digest(
587 |digest: &mut Sha1| {
588 digest.update(test.as_bytes());
589 Ok(())
590 },
591 &sig,
592 )
593 .expect("failed to verify");
594 }
595
596 #[rstest]
597 #[case("test\n")]
598 fn test_sign_and_verify_roundtrip_blinded_digest_signer(#[case] test: &str) {
599 let priv_key = get_private_key();
600
601 let mut rng = ChaCha8Rng::from_seed([42; 32]);
602 let signing_key = BlindedSigningKey::<Sha1>::new(priv_key);
603 let verifying_key = signing_key.verifying_key();
604
605 let sig = signing_key
606 .sign_digest_with_rng(&mut rng, |digest: &mut Sha1| digest.update(test.as_bytes()));
607
608 verifying_key
609 .verify_digest(
610 |digest: &mut Sha1| {
611 digest.update(test.as_bytes());
612 Ok(())
613 },
614 &sig,
615 )
616 .expect("failed to verify");
617 }
618
619 #[rstest]
620 #[case(
621 "test\n",
622 hex!(
623 "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae"
624 "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962f"
625 ),
626 true
627 )]
628 #[case(
629 "test\n",
630 hex!(
631 "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae"
632 "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962e"
633 ),
634 false
635 )]
636 fn test_verify_pss_hazmat(#[case] text: &str, #[case] sig: [u8; 64], #[case] expected: bool) {
637 let text = Sha1::digest(text);
638 let priv_key = get_private_key();
639
640 let pub_key: RsaPublicKey = priv_key.into();
641 let verifying_key = VerifyingKey::<Sha1>::new(pub_key);
642
643 let result = verifying_key
644 .verify_prehash(text.as_ref(), &Signature::try_from(sig.as_slice()).unwrap());
645 match expected {
646 true => result.expect("failed to verify"),
647 false => {
648 result.expect_err("expected verifying error");
649 }
650 }
651 }
652
653 #[rstest]
654 #[case("test\n")]
655 fn test_sign_and_verify_pss_hazmat(#[case] test: &str) {
656 let test = &Sha1::digest(test);
657 let priv_key = get_private_key();
658
659 let mut rng = ChaCha8Rng::from_seed([42; 32]);
660 let signing_key = SigningKey::<Sha1>::new(priv_key);
661 let verifying_key = signing_key.verifying_key();
662
663 let sig = signing_key
664 .sign_prehash_with_rng(&mut rng, test)
665 .expect("failed to sign");
666 verifying_key
667 .verify_prehash(test, &sig)
668 .expect("failed to verify");
669 }
670
671 #[rstest]
672 #[case("test\n")]
673 fn test_sign_and_verify_pss_blinded_hazmat(#[case] test: &str) {
674 let priv_key = get_private_key();
675
676 let test = &Sha1::digest(test);
677 let mut rng = ChaCha8Rng::from_seed([42; 32]);
678 let signing_key = BlindedSigningKey::<Sha1>::new(priv_key);
679 let verifying_key = signing_key.verifying_key();
680
681 let sig = signing_key
682 .sign_prehash_with_rng(&mut rng, test)
683 .expect("failed to sign");
684 verifying_key
685 .verify_prehash(test, &sig)
686 .expect("failed to verify");
687 }
688
689 #[test]
690 fn test_sign_and_verify_2049bit_key() {
692 let plaintext = "Hello\n";
693 let mut rng = ChaCha8Rng::from_seed([42; 32]);
694 for i in 0..10 {
695 println!("round {i}");
696 let priv_key = RsaPrivateKey::new(&mut rng, 2049).unwrap();
697
698 let digest = Sha1::digest(plaintext.as_bytes()).to_vec();
699 let sig = priv_key
700 .sign_with_rng(&mut rng, Pss::<Sha1>::new(), &digest)
701 .expect("failed to sign");
702
703 priv_key
704 .to_public_key()
705 .verify(Pss::<Sha1>::new(), &digest, &sig)
706 .expect("failed to verify");
707 }
708 }
709
710 #[rstest]
713 #[case("test\n")]
714 fn test_sign_and_verify_pss_differing_salt_len(#[case] test: &str) {
715 let priv_key = get_private_key();
716
717 let mut rng = ChaCha8Rng::from_seed([42; 32]);
718
719 let signing_keys = [
721 SigningKey::<Sha1>::new(priv_key.clone()),
723 SigningKey::<Sha1>::new_with_salt_len(
725 priv_key.clone(),
726 priv_key.size() - Sha1::output_size() - 2,
727 ),
728 SigningKey::<Sha1>::new_with_salt_len(priv_key.clone(), 0),
730 ];
731
732 let verifying_key = VerifyingKey::<Sha1>::new_with_auto_salt_len(priv_key.to_public_key());
734
735 for signing_key in &signing_keys {
736 let sig = signing_key.sign_with_rng(&mut rng, test.as_bytes());
737 verifying_key
738 .verify(test.as_bytes(), &sig)
739 .expect("verification to succeed");
740 }
741 }
742}