rsa/
pss.rs

1//! Support for the [Probabilistic Signature Scheme] (PSS) a.k.a. RSASSA-PSS.
2//!
3//! Designed by Mihir Bellare and Phillip Rogaway. Specified in [RFC8017 § 8.1].
4//!
5//! # Usage
6//!
7//! See [code example in the toplevel rustdoc](../index.html#pss-signatures).
8//!
9//! [Probabilistic Signature Scheme]: https://en.wikipedia.org/wiki/Probabilistic_signature_scheme
10//! [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
11
12mod blinded_signing_key;
13mod signature;
14mod signing_key;
15mod verifying_key;
16
17pub use self::{
18    blinded_signing_key::BlindedSigningKey, signature::Signature, signing_key::SigningKey,
19    verifying_key::VerifyingKey,
20};
21
22use alloc::{boxed::Box, vec::Vec};
23use core::fmt::{self, Debug};
24
25use const_oid::{AssociatedOid, ObjectIdentifier};
26use digest::{Digest, DynDigest, FixedOutputReset};
27use num_bigint::BigUint;
28use pkcs1::RsaPssParams;
29use pkcs8::spki::{der::Any, AlgorithmIdentifierOwned};
30use rand_core::CryptoRngCore;
31
32use crate::algorithms::pad::{uint_to_be_pad, uint_to_zeroizing_be_pad};
33use crate::algorithms::pss::*;
34use crate::algorithms::rsa::{rsa_decrypt_and_check, rsa_encrypt};
35use crate::errors::{Error, Result};
36use crate::traits::PublicKeyParts;
37use crate::traits::SignatureScheme;
38use crate::{RsaPrivateKey, RsaPublicKey};
39
40/// Digital signatures using PSS padding.
41pub struct Pss {
42    /// Create blinded signatures.
43    pub blinded: bool,
44
45    /// Digest type to use.
46    pub digest: Box<dyn DynDigest + Send + Sync>,
47
48    /// Salt length.
49    pub salt_len: usize,
50}
51
52impl Pss {
53    /// New PSS padding for the given digest.
54    /// Digest output size is used as a salt length.
55    pub fn new<T: 'static + Digest + DynDigest + Send + Sync>() -> Self {
56        Self::new_with_salt::<T>(<T as Digest>::output_size())
57    }
58
59    /// New PSS padding for the given digest with a salt value of the given length.
60    pub fn new_with_salt<T: 'static + Digest + DynDigest + Send + Sync>(len: usize) -> Self {
61        Self {
62            blinded: false,
63            digest: Box::new(T::new()),
64            salt_len: len,
65        }
66    }
67
68    /// New PSS padding for blinded signatures (RSA-BSSA) for the given digest.
69    /// Digest output size is used as a salt length.
70    pub fn new_blinded<T: 'static + Digest + DynDigest + Send + Sync>() -> Self {
71        Self::new_blinded_with_salt::<T>(<T as Digest>::output_size())
72    }
73
74    /// New PSS padding for blinded signatures (RSA-BSSA) for the given digest
75    /// with a salt value of the given length.
76    pub fn new_blinded_with_salt<T: 'static + Digest + DynDigest + Send + Sync>(
77        len: usize,
78    ) -> Self {
79        Self {
80            blinded: true,
81            digest: Box::new(T::new()),
82            salt_len: len,
83        }
84    }
85}
86
87impl SignatureScheme for Pss {
88    fn sign<Rng: CryptoRngCore>(
89        mut self,
90        rng: Option<&mut Rng>,
91        priv_key: &RsaPrivateKey,
92        hashed: &[u8],
93    ) -> Result<Vec<u8>> {
94        sign(
95            rng.ok_or(Error::InvalidPaddingScheme)?,
96            self.blinded,
97            priv_key,
98            hashed,
99            self.salt_len,
100            &mut *self.digest,
101        )
102    }
103
104    fn verify(mut self, pub_key: &RsaPublicKey, hashed: &[u8], sig: &[u8]) -> Result<()> {
105        verify(
106            pub_key,
107            hashed,
108            &BigUint::from_bytes_be(sig),
109            sig.len(),
110            &mut *self.digest,
111            self.salt_len,
112        )
113    }
114}
115
116impl Debug for Pss {
117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118        f.debug_struct("PSS")
119            .field("blinded", &self.blinded)
120            .field("digest", &"...")
121            .field("salt_len", &self.salt_len)
122            .finish()
123    }
124}
125
126pub(crate) fn verify(
127    pub_key: &RsaPublicKey,
128    hashed: &[u8],
129    sig: &BigUint,
130    sig_len: usize,
131    digest: &mut dyn DynDigest,
132    salt_len: usize,
133) -> Result<()> {
134    if sig_len != pub_key.size() {
135        return Err(Error::Verification);
136    }
137
138    let mut em = uint_to_be_pad(rsa_encrypt(pub_key, sig)?, pub_key.size())?;
139
140    emsa_pss_verify(hashed, &mut em, salt_len, digest, pub_key.n().bits())
141}
142
143pub(crate) fn verify_digest<D>(
144    pub_key: &RsaPublicKey,
145    hashed: &[u8],
146    sig: &BigUint,
147    sig_len: usize,
148    salt_len: usize,
149) -> Result<()>
150where
151    D: Digest + FixedOutputReset,
152{
153    if sig >= pub_key.n() || sig_len != pub_key.size() {
154        return Err(Error::Verification);
155    }
156
157    let mut em = uint_to_be_pad(rsa_encrypt(pub_key, sig)?, pub_key.size())?;
158
159    emsa_pss_verify_digest::<D>(hashed, &mut em, salt_len, pub_key.n().bits())
160}
161
162/// SignPSS calculates the signature of hashed using RSASSA-PSS.
163///
164/// Note that hashed must be the result of hashing the input message using the
165/// given hash function. The opts argument may be nil, in which case sensible
166/// defaults are used.
167pub(crate) fn sign<T: CryptoRngCore>(
168    rng: &mut T,
169    blind: bool,
170    priv_key: &RsaPrivateKey,
171    hashed: &[u8],
172    salt_len: usize,
173    digest: &mut dyn DynDigest,
174) -> Result<Vec<u8>> {
175    let mut salt = vec![0; salt_len];
176    rng.fill_bytes(&mut salt[..]);
177
178    sign_pss_with_salt(blind.then_some(rng), priv_key, hashed, &salt, digest)
179}
180
181pub(crate) fn sign_digest<T: CryptoRngCore + ?Sized, D: Digest + FixedOutputReset>(
182    rng: &mut T,
183    blind: bool,
184    priv_key: &RsaPrivateKey,
185    hashed: &[u8],
186    salt_len: usize,
187) -> Result<Vec<u8>> {
188    let mut salt = vec![0; salt_len];
189    rng.fill_bytes(&mut salt[..]);
190
191    sign_pss_with_salt_digest::<_, D>(blind.then_some(rng), priv_key, hashed, &salt)
192}
193
194/// signPSSWithSalt calculates the signature of hashed using PSS with specified salt.
195///
196/// Note that hashed must be the result of hashing the input message using the
197/// given hash function. salt is a random sequence of bytes whose length will be
198/// later used to verify the signature.
199fn sign_pss_with_salt<T: CryptoRngCore>(
200    blind_rng: Option<&mut T>,
201    priv_key: &RsaPrivateKey,
202    hashed: &[u8],
203    salt: &[u8],
204    digest: &mut dyn DynDigest,
205) -> Result<Vec<u8>> {
206    let em_bits = priv_key.n().bits() - 1;
207    let em = emsa_pss_encode(hashed, em_bits, salt, digest)?;
208
209    uint_to_zeroizing_be_pad(
210        rsa_decrypt_and_check(priv_key, blind_rng, &BigUint::from_bytes_be(&em))?,
211        priv_key.size(),
212    )
213}
214
215fn sign_pss_with_salt_digest<T: CryptoRngCore + ?Sized, D: Digest + FixedOutputReset>(
216    blind_rng: Option<&mut T>,
217    priv_key: &RsaPrivateKey,
218    hashed: &[u8],
219    salt: &[u8],
220) -> Result<Vec<u8>> {
221    let em_bits = priv_key.n().bits() - 1;
222    let em = emsa_pss_encode_digest::<D>(hashed, em_bits, salt)?;
223
224    uint_to_zeroizing_be_pad(
225        rsa_decrypt_and_check(priv_key, blind_rng, &BigUint::from_bytes_be(&em))?,
226        priv_key.size(),
227    )
228}
229
230/// Returns the [`AlgorithmIdentifierOwned`] associated with PSS signature using a given digest.
231pub fn get_default_pss_signature_algo_id<D>() -> pkcs8::spki::Result<AlgorithmIdentifierOwned>
232where
233    D: Digest + AssociatedOid,
234{
235    let salt_len: u8 = <D as Digest>::output_size() as u8;
236    get_pss_signature_algo_id::<D>(salt_len)
237}
238
239fn get_pss_signature_algo_id<D>(salt_len: u8) -> pkcs8::spki::Result<AlgorithmIdentifierOwned>
240where
241    D: Digest + AssociatedOid,
242{
243    const ID_RSASSA_PSS: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10");
244
245    let pss_params = RsaPssParams::new::<D>(salt_len);
246
247    Ok(AlgorithmIdentifierOwned {
248        oid: ID_RSASSA_PSS,
249        parameters: Some(Any::encode_from(&pss_params)?),
250    })
251}
252
253#[cfg(test)]
254mod test {
255    use crate::pss::{BlindedSigningKey, Pss, Signature, SigningKey, VerifyingKey};
256    use crate::{RsaPrivateKey, RsaPublicKey};
257
258    use hex_literal::hex;
259    use num_bigint::BigUint;
260    use num_traits::{FromPrimitive, Num};
261    use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng};
262    use sha1::{Digest, Sha1};
263    use signature::hazmat::{PrehashVerifier, RandomizedPrehashSigner};
264    use signature::{DigestVerifier, Keypair, RandomizedDigestSigner, RandomizedSigner, Verifier};
265
266    fn get_private_key() -> RsaPrivateKey {
267        // In order to generate new test vectors you'll need the PEM form of this key:
268        // -----BEGIN RSA PRIVATE KEY-----
269        // MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
270        // fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu
271        // /ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu
272        // RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/
273        // EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A
274        // IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS
275        // tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V
276        // -----END RSA PRIVATE KEY-----
277
278        RsaPrivateKey::from_components(
279            BigUint::from_str_radix("9353930466774385905609975137998169297361893554149986716853295022578535724979677252958524466350471210367835187480748268864277464700638583474144061408845077", 10).unwrap(),
280            BigUint::from_u64(65537).unwrap(),
281            BigUint::from_str_radix("7266398431328116344057699379749222532279343923819063639497049039389899328538543087657733766554155839834519529439851673014800261285757759040931985506583861", 10).unwrap(),
282            vec![
283                BigUint::from_str_radix("98920366548084643601728869055592650835572950932266967461790948584315647051443",10).unwrap(),
284                BigUint::from_str_radix("94560208308847015747498523884063394671606671904944666360068158221458669711639", 10).unwrap()
285            ],
286        ).unwrap()
287    }
288
289    #[test]
290    fn test_verify_pss() {
291        let priv_key = get_private_key();
292
293        let tests = [
294            (
295                "test\n",
296                hex!(
297                    "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae"
298                    "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962f"
299                ),
300                true,
301            ),
302            (
303                "test\n",
304                hex!(
305                    "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae"
306                    "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962e"
307                ),
308                false,
309            ),
310        ];
311        let pub_key: RsaPublicKey = priv_key.into();
312
313        for (text, sig, expected) in &tests {
314            let digest = Sha1::digest(text.as_bytes()).to_vec();
315            let result = pub_key.verify(Pss::new::<Sha1>(), &digest, sig);
316            match expected {
317                true => result.expect("failed to verify"),
318                false => {
319                    result.expect_err("expected verifying error");
320                }
321            }
322        }
323    }
324
325    #[test]
326    fn test_verify_pss_signer() {
327        let priv_key = get_private_key();
328
329        let tests = [
330            (
331                "test\n",
332                hex!(
333                    "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae"
334                    "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962f"
335                ),
336                true,
337            ),
338            (
339                "test\n",
340                hex!(
341                    "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae"
342                    "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962e"
343                ),
344                false,
345            ),
346        ];
347        let pub_key: RsaPublicKey = priv_key.into();
348        let verifying_key: VerifyingKey<Sha1> = VerifyingKey::new(pub_key);
349
350        for (text, sig, expected) in &tests {
351            let result = verifying_key.verify(
352                text.as_bytes(),
353                &Signature::try_from(sig.as_slice()).unwrap(),
354            );
355            match expected {
356                true => result.expect("failed to verify"),
357                false => {
358                    result.expect_err("expected verifying error");
359                }
360            }
361        }
362    }
363
364    #[test]
365    fn test_verify_pss_digest_signer() {
366        let priv_key = get_private_key();
367
368        let tests = [
369            (
370                "test\n",
371                hex!(
372                    "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae"
373                    "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962f"
374                ),
375                true,
376            ),
377            (
378                "test\n",
379                hex!(
380                    "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae"
381                    "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962e"
382                ),
383                false,
384            ),
385        ];
386        let pub_key: RsaPublicKey = priv_key.into();
387        let verifying_key = VerifyingKey::new(pub_key);
388
389        for (text, sig, expected) in &tests {
390            let mut digest = Sha1::new();
391            digest.update(text.as_bytes());
392            let result =
393                verifying_key.verify_digest(digest, &Signature::try_from(sig.as_slice()).unwrap());
394            match expected {
395                true => result.expect("failed to verify"),
396                false => {
397                    result.expect_err("expected verifying error");
398                }
399            }
400        }
401    }
402
403    #[test]
404    fn test_sign_and_verify_roundtrip() {
405        let priv_key = get_private_key();
406
407        let tests = ["test\n"];
408        let rng = ChaCha8Rng::from_seed([42; 32]);
409
410        for test in &tests {
411            let digest = Sha1::digest(test.as_bytes()).to_vec();
412            let sig = priv_key
413                .sign_with_rng(&mut rng.clone(), Pss::new::<Sha1>(), &digest)
414                .expect("failed to sign");
415
416            priv_key
417                .to_public_key()
418                .verify(Pss::new::<Sha1>(), &digest, &sig)
419                .expect("failed to verify");
420        }
421    }
422
423    #[test]
424    fn test_sign_blinded_and_verify_roundtrip() {
425        let priv_key = get_private_key();
426
427        let tests = ["test\n"];
428        let rng = ChaCha8Rng::from_seed([42; 32]);
429
430        for test in &tests {
431            let digest = Sha1::digest(test.as_bytes()).to_vec();
432            let sig = priv_key
433                .sign_with_rng(&mut rng.clone(), Pss::new_blinded::<Sha1>(), &digest)
434                .expect("failed to sign");
435
436            priv_key
437                .to_public_key()
438                .verify(Pss::new::<Sha1>(), &digest, &sig)
439                .expect("failed to verify");
440        }
441    }
442
443    #[test]
444    fn test_sign_and_verify_roundtrip_signer() {
445        let priv_key = get_private_key();
446
447        let tests = ["test\n"];
448        let mut rng = ChaCha8Rng::from_seed([42; 32]);
449        let signing_key = SigningKey::<Sha1>::new(priv_key);
450        let verifying_key = signing_key.verifying_key();
451
452        for test in &tests {
453            let sig = signing_key.sign_with_rng(&mut rng, test.as_bytes());
454            verifying_key
455                .verify(test.as_bytes(), &sig)
456                .expect("failed to verify");
457        }
458    }
459
460    #[test]
461    fn test_sign_and_verify_roundtrip_blinded_signer() {
462        let priv_key = get_private_key();
463
464        let tests = ["test\n"];
465        let mut rng = ChaCha8Rng::from_seed([42; 32]);
466        let signing_key = BlindedSigningKey::<Sha1>::new(priv_key);
467        let verifying_key = signing_key.verifying_key();
468
469        for test in &tests {
470            let sig = signing_key.sign_with_rng(&mut rng, test.as_bytes());
471            verifying_key
472                .verify(test.as_bytes(), &sig)
473                .expect("failed to verify");
474        }
475    }
476
477    #[test]
478    fn test_sign_and_verify_roundtrip_digest_signer() {
479        let priv_key = get_private_key();
480
481        let tests = ["test\n"];
482        let mut rng = ChaCha8Rng::from_seed([42; 32]);
483        let signing_key = SigningKey::new(priv_key);
484        let verifying_key = signing_key.verifying_key();
485
486        for test in &tests {
487            let mut digest = Sha1::new();
488            digest.update(test.as_bytes());
489            let sig = signing_key.sign_digest_with_rng(&mut rng, digest);
490
491            let mut digest = Sha1::new();
492            digest.update(test.as_bytes());
493            verifying_key
494                .verify_digest(digest, &sig)
495                .expect("failed to verify");
496        }
497    }
498
499    #[test]
500    fn test_sign_and_verify_roundtrip_blinded_digest_signer() {
501        let priv_key = get_private_key();
502
503        let tests = ["test\n"];
504        let mut rng = ChaCha8Rng::from_seed([42; 32]);
505        let signing_key = BlindedSigningKey::<Sha1>::new(priv_key);
506        let verifying_key = signing_key.verifying_key();
507
508        for test in &tests {
509            let mut digest = Sha1::new();
510            digest.update(test.as_bytes());
511            let sig = signing_key.sign_digest_with_rng(&mut rng, digest);
512
513            let mut digest = Sha1::new();
514            digest.update(test.as_bytes());
515            verifying_key
516                .verify_digest(digest, &sig)
517                .expect("failed to verify");
518        }
519    }
520
521    #[test]
522    fn test_verify_pss_hazmat() {
523        let priv_key = get_private_key();
524
525        let tests = [
526            (
527                Sha1::digest("test\n"),
528                hex!(
529                    "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae"
530                    "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962f"
531                ),
532                true,
533            ),
534            (
535                Sha1::digest("test\n"),
536                hex!(
537                    "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae"
538                    "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962e"
539                ),
540                false,
541            ),
542        ];
543        let pub_key: RsaPublicKey = priv_key.into();
544        let verifying_key = VerifyingKey::<Sha1>::new(pub_key);
545
546        for (text, sig, expected) in &tests {
547            let result = verifying_key
548                .verify_prehash(text.as_ref(), &Signature::try_from(sig.as_slice()).unwrap());
549            match expected {
550                true => result.expect("failed to verify"),
551                false => {
552                    result.expect_err("expected verifying error");
553                }
554            }
555        }
556    }
557
558    #[test]
559    fn test_sign_and_verify_pss_hazmat() {
560        let priv_key = get_private_key();
561
562        let tests = [Sha1::digest("test\n")];
563        let mut rng = ChaCha8Rng::from_seed([42; 32]);
564        let signing_key = SigningKey::<Sha1>::new(priv_key);
565        let verifying_key = signing_key.verifying_key();
566
567        for test in &tests {
568            let sig = signing_key
569                .sign_prehash_with_rng(&mut rng, &test)
570                .expect("failed to sign");
571            verifying_key
572                .verify_prehash(&test, &sig)
573                .expect("failed to verify");
574        }
575    }
576
577    #[test]
578    fn test_sign_and_verify_pss_blinded_hazmat() {
579        let priv_key = get_private_key();
580
581        let tests = [Sha1::digest("test\n")];
582        let mut rng = ChaCha8Rng::from_seed([42; 32]);
583        let signing_key = BlindedSigningKey::<Sha1>::new(priv_key);
584        let verifying_key = signing_key.verifying_key();
585
586        for test in &tests {
587            let sig = signing_key
588                .sign_prehash_with_rng(&mut rng, &test)
589                .expect("failed to sign");
590            verifying_key
591                .verify_prehash(&test, &sig)
592                .expect("failed to verify");
593        }
594    }
595
596    #[test]
597    // Tests the corner case where the key is multiple of 8 + 1 bits long
598    fn test_sign_and_verify_2049bit_key() {
599        let plaintext = "Hello\n";
600        let rng = ChaCha8Rng::from_seed([42; 32]);
601        let priv_key = RsaPrivateKey::new(&mut rng.clone(), 2049).unwrap();
602
603        let digest = Sha1::digest(plaintext.as_bytes()).to_vec();
604        let sig = priv_key
605            .sign_with_rng(&mut rng.clone(), Pss::new::<Sha1>(), &digest)
606            .expect("failed to sign");
607
608        priv_key
609            .to_public_key()
610            .verify(Pss::new::<Sha1>(), &digest, &sig)
611            .expect("failed to verify");
612    }
613}