Skip to main content

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