cmail_rpgp/composed/key/
builder.rs

1use std::time::Duration;
2
3use chrono::SubsecRound;
4use derive_builder::Builder;
5use rand::{CryptoRng, Rng};
6use smallvec::SmallVec;
7
8use crate::composed::{KeyDetails, SecretKey, SecretSubkey};
9use crate::crypto::aead::AeadAlgorithm;
10use crate::crypto::ecc_curve::ECCCurve;
11use crate::crypto::hash::HashAlgorithm;
12use crate::crypto::public_key::PublicKeyAlgorithm;
13use crate::crypto::sym::SymmetricKeyAlgorithm;
14use crate::crypto::{dsa, ecdh, ecdsa, eddsa, rsa, x25519};
15use crate::errors::Result;
16use crate::packet::{self, KeyFlags, UserAttribute, UserId};
17use crate::types::{self, CompressionAlgorithm, PublicParams, RevocationKey, S2kParams};
18
19#[derive(Debug, PartialEq, Eq, Builder)]
20#[builder(build_fn(validate = "Self::validate"))]
21pub struct SecretKeyParams {
22    key_type: KeyType,
23
24    // -- Keyflags
25    #[builder(default)]
26    can_sign: bool,
27    #[builder(default)]
28    can_certify: bool,
29    #[builder(default)]
30    can_encrypt: bool,
31
32    // -- Preferences
33    /// List of symmetric algorithms that indicate which algorithms the key holder prefers to use.
34    #[builder(default)]
35    preferred_symmetric_algorithms: SmallVec<[SymmetricKeyAlgorithm; 8]>,
36    /// List of hash algorithms that indicate which algorithms the key holder prefers to use.
37    #[builder(default)]
38    preferred_hash_algorithms: SmallVec<[HashAlgorithm; 8]>,
39    /// List of compression algorithms that indicate which algorithms the key holder prefers to use.
40    #[builder(default)]
41    preferred_compression_algorithms: SmallVec<[CompressionAlgorithm; 8]>,
42    #[builder(default)]
43    preferred_aead_algorithms: SmallVec<[(SymmetricKeyAlgorithm, AeadAlgorithm); 4]>,
44    #[builder(default)]
45    revocation_key: Option<RevocationKey>,
46
47    #[builder]
48    primary_user_id: String,
49
50    #[builder(default)]
51    user_ids: Vec<String>,
52    #[builder(default)]
53    user_attributes: Vec<UserAttribute>,
54    #[builder(default)]
55    passphrase: Option<String>,
56    #[builder(default)]
57    s2k: Option<S2kParams>,
58    #[builder(default = "chrono::Utc::now().trunc_subsecs(0)")]
59    created_at: chrono::DateTime<chrono::Utc>,
60    #[builder(default)]
61    packet_version: types::Version,
62    #[builder(default)]
63    version: types::KeyVersion,
64    #[builder(default)]
65    expiration: Option<Duration>,
66
67    #[builder(default)]
68    subkeys: Vec<SubkeyParams>,
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, Builder)]
72pub struct SubkeyParams {
73    key_type: KeyType,
74
75    #[builder(default)]
76    can_sign: bool,
77    #[builder(default)]
78    can_certify: bool,
79    #[builder(default)]
80    can_encrypt: bool,
81    #[builder(default)]
82    can_authenticate: bool,
83
84    #[builder(default)]
85    user_ids: Vec<UserId>,
86    #[builder(default)]
87    user_attributes: Vec<UserAttribute>,
88    #[builder(default)]
89    passphrase: Option<String>,
90    #[builder(default)]
91    s2k: Option<S2kParams>,
92    #[builder(default = "chrono::Utc::now().trunc_subsecs(0)")]
93    created_at: chrono::DateTime<chrono::Utc>,
94    #[builder(default)]
95    packet_version: types::Version,
96    #[builder(default)]
97    version: types::KeyVersion,
98    #[builder(default)]
99    expiration: Option<Duration>,
100}
101
102impl SecretKeyParamsBuilder {
103    fn validate(&self) -> std::result::Result<(), String> {
104        match &self.key_type {
105            Some(KeyType::Rsa(size)) => {
106                if *size < 2048 {
107                    return Err("Keys with less than 2048bits are considered insecure".into());
108                }
109            }
110            Some(KeyType::EdDSALegacy) => {
111                if let Some(can_encrypt) = self.can_encrypt {
112                    if can_encrypt {
113                        return Err("EdDSA can only be used for signing keys".into());
114                    }
115                }
116            }
117            Some(KeyType::ECDSA(curve)) => {
118                if let Some(can_encrypt) = self.can_encrypt {
119                    if can_encrypt {
120                        return Err("ECDSA can only be used for signing keys".into());
121                    }
122                };
123                match curve {
124                    ECCCurve::P256 | ECCCurve::P384 | ECCCurve::P521 | ECCCurve::Secp256k1 => {}
125                    _ => return Err(format!("Curve {} is not supported for ECDSA", curve.name())),
126                }
127            }
128            Some(KeyType::ECDH(_)) => {
129                if let Some(can_sign) = self.can_sign {
130                    if can_sign {
131                        return Err("ECDH can only be used for encryption keys".into());
132                    }
133                }
134            }
135            Some(KeyType::Dsa(_)) => {
136                if let Some(can_encrypt) = self.can_encrypt {
137                    if can_encrypt {
138                        return Err("DSA can only be used for signing keys".into());
139                    }
140                }
141            }
142            _ => {}
143        }
144
145        Ok(())
146    }
147
148    pub fn user_id<VALUE: Into<String>>(&mut self, value: VALUE) -> &mut Self {
149        if let Some(ref mut user_ids) = self.user_ids {
150            user_ids.push(value.into());
151        } else {
152            self.user_ids = Some(vec![value.into()]);
153        }
154        self
155    }
156
157    pub fn subkey<VALUE: Into<SubkeyParams>>(&mut self, value: VALUE) -> &mut Self {
158        if let Some(ref mut subkeys) = self.subkeys {
159            subkeys.push(value.into());
160        } else {
161            self.subkeys = Some(vec![value.into()]);
162        }
163        self
164    }
165}
166
167impl SecretKeyParams {
168    pub fn generate<R: Rng + CryptoRng>(self, mut rng: R) -> Result<SecretKey> {
169        let passphrase = self.passphrase;
170        let s2k = self
171            .s2k
172            .unwrap_or_else(|| S2kParams::new_default(&mut rng, self.version));
173        let (public_params, secret_params) = self.key_type.generate(&mut rng)?;
174        let mut primary_key = packet::SecretKey::new(
175            packet::PublicKey::new(
176                self.packet_version,
177                self.version,
178                self.key_type.to_alg(),
179                self.created_at,
180                self.expiration.map(|v| v.as_secs() as u16),
181                public_params,
182            )?,
183            secret_params,
184        );
185        if let Some(passphrase) = passphrase {
186            primary_key.set_password_with_s2k(|| passphrase, s2k)?;
187        }
188
189        let mut keyflags = KeyFlags::default();
190        keyflags.set_certify(self.can_certify);
191        keyflags.set_encrypt_comms(self.can_encrypt);
192        keyflags.set_encrypt_storage(self.can_encrypt);
193        keyflags.set_sign(self.can_sign);
194
195        Ok(SecretKey::new(
196            primary_key,
197            KeyDetails::new(
198                UserId::from_str(Default::default(), &self.primary_user_id),
199                self.user_ids
200                    .iter()
201                    .map(|m| UserId::from_str(Default::default(), m))
202                    .collect(),
203                self.user_attributes,
204                keyflags,
205                self.preferred_symmetric_algorithms,
206                self.preferred_hash_algorithms,
207                self.preferred_compression_algorithms,
208                self.preferred_aead_algorithms,
209                self.revocation_key,
210            ),
211            Default::default(),
212            self.subkeys
213                .into_iter()
214                .map(|subkey| {
215                    let passphrase = subkey.passphrase;
216                    let s2k = subkey
217                        .s2k
218                        .unwrap_or_else(|| S2kParams::new_default(&mut rng, subkey.version));
219                    let (public_params, secret_params) = subkey.key_type.generate(&mut rng)?;
220                    let mut keyflags = KeyFlags::default();
221                    keyflags.set_certify(subkey.can_certify);
222                    keyflags.set_encrypt_comms(subkey.can_encrypt);
223                    keyflags.set_encrypt_storage(subkey.can_encrypt);
224                    keyflags.set_sign(subkey.can_sign);
225                    keyflags.set_authentication(subkey.can_authenticate);
226
227                    let mut sub = packet::SecretSubkey::new(
228                        packet::PublicSubkey::new(
229                            subkey.packet_version,
230                            subkey.version,
231                            subkey.key_type.to_alg(),
232                            subkey.created_at,
233                            subkey.expiration.map(|v| v.as_secs() as u16),
234                            public_params,
235                        )?,
236                        secret_params,
237                    );
238
239                    if let Some(passphrase) = passphrase {
240                        sub.set_password_with_s2k(|| passphrase, s2k)?;
241                    }
242
243                    Ok(SecretSubkey::new(sub, keyflags))
244                })
245                .collect::<Result<Vec<_>>>()?,
246        ))
247    }
248}
249
250#[derive(Clone, Debug, PartialEq, Eq)]
251pub enum KeyType {
252    /// Encryption & Signing with RSA and the given bitsize.
253    Rsa(u32),
254    /// Encrypting with ECDH
255    ECDH(ECCCurve),
256    /// Signing with Curve25519, legacy format (deprecated in RFC 9580)
257    EdDSALegacy,
258    /// Signing with ECDSA
259    ECDSA(ECCCurve),
260    /// Signing with DSA for the given bitsize.
261    Dsa(DsaKeySize),
262    /// Signing with Ed25519
263    Ed25519,
264    /// Encrypting with X25519
265    X25519,
266    /// Encrypting with X448
267    X448,
268}
269
270#[derive(Clone, Debug, Copy, PartialEq, Eq)]
271#[repr(u32)]
272pub enum DsaKeySize {
273    /// DSA parameter size constant: L = 1024, N = 160
274    B1024 = 1024,
275    /// DSA parameter size constant: L = 2048, N = 256
276    B2048 = 2048,
277    /// DSA parameter size constant: L = 3072, N = 256
278    B3072 = 3072,
279}
280
281impl From<DsaKeySize> for dsa::KeySize {
282    fn from(value: DsaKeySize) -> Self {
283        match value {
284            #[allow(deprecated)]
285            DsaKeySize::B1024 => dsa::KeySize::DSA_1024_160,
286            DsaKeySize::B2048 => dsa::KeySize::DSA_2048_256,
287            DsaKeySize::B3072 => dsa::KeySize::DSA_3072_256,
288        }
289    }
290}
291
292impl KeyType {
293    pub fn to_alg(&self) -> PublicKeyAlgorithm {
294        match self {
295            KeyType::Rsa(_) => PublicKeyAlgorithm::RSA,
296            KeyType::ECDH(_) => PublicKeyAlgorithm::ECDH,
297            KeyType::EdDSALegacy => PublicKeyAlgorithm::EdDSALegacy,
298            KeyType::ECDSA(_) => PublicKeyAlgorithm::ECDSA,
299            KeyType::Dsa(_) => PublicKeyAlgorithm::DSA,
300            KeyType::Ed25519 => PublicKeyAlgorithm::Ed25519,
301            KeyType::X25519 => PublicKeyAlgorithm::X25519,
302            KeyType::X448 => PublicKeyAlgorithm::X448,
303        }
304    }
305
306    pub fn generate<R: Rng + CryptoRng>(
307        &self,
308        rng: R,
309    ) -> Result<(PublicParams, types::SecretParams)> {
310        let (pub_params, plain) = match self {
311            KeyType::Rsa(bit_size) => rsa::generate_key(rng, *bit_size as usize)?,
312            KeyType::ECDH(curve) => ecdh::generate_key(rng, curve)?,
313            KeyType::EdDSALegacy => eddsa::generate_key(rng, eddsa::Mode::EdDSALegacy),
314            KeyType::ECDSA(curve) => ecdsa::generate_key(rng, curve)?,
315            KeyType::Dsa(key_size) => dsa::generate_key(rng, (*key_size).into())?,
316            KeyType::Ed25519 => eddsa::generate_key(rng, eddsa::Mode::Ed25519),
317            KeyType::X25519 => x25519::generate_key(rng),
318            KeyType::X448 => crate::crypto::x448::generate_key(rng),
319        };
320
321        Ok((pub_params, types::SecretParams::Plain(plain)))
322    }
323}
324
325#[cfg(test)]
326mod tests {
327    #![allow(clippy::unwrap_used)]
328
329    use rand::SeedableRng;
330    use rand_chacha::ChaCha8Rng;
331    use smallvec::smallvec;
332
333    use super::*;
334    use crate::composed::{Deserializable, SignedPublicKey, SignedSecretKey};
335    use crate::types::{KeyVersion, SecretKeyTrait};
336
337    #[test]
338    #[ignore] // slow in debug mode
339    fn test_key_gen_rsa_2048() {
340        let _ = pretty_env_logger::try_init();
341        let mut rng = ChaCha8Rng::seed_from_u64(0);
342
343        for key_version in [KeyVersion::V4, KeyVersion::V6] {
344            println!("key version {:?}", key_version);
345
346            for i in 0..50 {
347                println!("round {i}");
348                gen_rsa_2048(&mut rng, key_version);
349            }
350        }
351    }
352
353    fn gen_rsa_2048<R: Rng + CryptoRng>(mut rng: R, version: KeyVersion) {
354        let mut key_params = SecretKeyParamsBuilder::default();
355        key_params
356            .version(version)
357            .key_type(KeyType::Rsa(2048))
358            .can_certify(true)
359            .can_sign(true)
360            .primary_user_id("Me <me@mail.com>".into())
361            .preferred_symmetric_algorithms(smallvec![
362                SymmetricKeyAlgorithm::MKV128256,
363                SymmetricKeyAlgorithm::MKV128192,
364                SymmetricKeyAlgorithm::MKV128128,
365                SymmetricKeyAlgorithm::AES256,
366                SymmetricKeyAlgorithm::AES192,
367                SymmetricKeyAlgorithm::AES128,
368            ])
369            .preferred_hash_algorithms(smallvec![
370                HashAlgorithm::SHA2_256,
371                HashAlgorithm::SHA2_384,
372                HashAlgorithm::SHA2_512,
373                HashAlgorithm::SHA2_224,
374                HashAlgorithm::SHA1,
375            ])
376            .preferred_compression_algorithms(smallvec![
377                CompressionAlgorithm::ZLIB,
378                CompressionAlgorithm::ZIP,
379            ]);
380
381        let key_params_enc = key_params
382            .clone()
383            .passphrase(Some("hello".into()))
384            .subkey(
385                SubkeyParamsBuilder::default()
386                    .version(version)
387                    .key_type(KeyType::Rsa(2048))
388                    .passphrase(Some("hello".into()))
389                    .can_encrypt(true)
390                    .build()
391                    .unwrap(),
392            )
393            .build()
394            .unwrap();
395        let key_enc = key_params_enc
396            .generate(&mut rng)
397            .expect("failed to generate secret key, encrypted");
398
399        let key_params_plain = key_params
400            .passphrase(None)
401            .subkey(
402                SubkeyParamsBuilder::default()
403                    .version(version)
404                    .key_type(KeyType::Rsa(2048))
405                    .can_encrypt(true)
406                    .build()
407                    .unwrap(),
408            )
409            .build()
410            .unwrap();
411        let key_plain = key_params_plain
412            .generate(&mut rng)
413            .expect("failed to generate secret key");
414
415        let signed_key_enc = key_enc
416            .sign(&mut rng, || "hello".into())
417            .expect("failed to sign key");
418        let signed_key_plain = key_plain
419            .sign(&mut rng, || "".into())
420            .expect("failed to sign key");
421
422        let armor_enc = signed_key_enc
423            .to_armored_string(None.into())
424            .expect("failed to serialize key");
425        let armor_plain = signed_key_plain
426            .to_armored_string(None.into())
427            .expect("failed to serialize key");
428
429        // std::fs::write("sample-rsa-enc.sec.asc", &armor_enc).unwrap();
430        // std::fs::write("sample-rsa.sec.asc", &armor_plain).unwrap();
431
432        let (signed_key2_enc, _headers) =
433            SignedSecretKey::from_string(&armor_enc).expect("failed to parse key (enc)");
434        signed_key2_enc.verify().expect("invalid key (enc)");
435
436        let (signed_key2_plain, _headers) =
437            SignedSecretKey::from_string(&armor_plain).expect("failed to parse key (plain)");
438        signed_key2_plain.verify().expect("invalid key (plain)");
439
440        signed_key2_enc
441            .unlock(|| "hello".into(), |_| Ok(()))
442            .expect("failed to unlock parsed key (enc)");
443        signed_key2_plain
444            .unlock(|| "".into(), |_| Ok(()))
445            .expect("failed to unlock parsed key (plain)");
446
447        assert_eq!(signed_key_plain, signed_key2_plain);
448
449        let public_key = signed_key_plain.public_key();
450
451        let public_signed_key = public_key
452            .sign(&mut rng, &signed_key_plain, || "".into())
453            .expect("failed to sign public key");
454
455        public_signed_key.verify().expect("invalid public key");
456
457        let armor = public_signed_key
458            .to_armored_string(None.into())
459            .expect("failed to serialize public key");
460
461        // std::fs::write("sample-rsa.pub.asc", &armor).unwrap();
462
463        let (signed_key2, _headers) =
464            SignedPublicKey::from_string(&armor).expect("failed to parse public key");
465        signed_key2.verify().expect("invalid public key");
466    }
467
468    #[ignore]
469    #[test]
470    fn key_gen_25519_legacy_long() {
471        let mut rng = ChaCha8Rng::seed_from_u64(0);
472        for i in 0..10_000 {
473            println!("round {i}");
474            gen_25519_legacy(&mut rng);
475        }
476    }
477
478    #[test]
479    fn key_gen_25519_legacy_short() {
480        let mut rng = ChaCha8Rng::seed_from_u64(0);
481        for _ in 0..100 {
482            gen_25519_legacy(&mut rng);
483        }
484    }
485
486    fn gen_25519_legacy<R: Rng + CryptoRng>(mut rng: R) {
487        // The v4-only key format variants based on Curve 25519 (EdDSALegacy/ECDH over 25519)
488
489        let _ = pretty_env_logger::try_init();
490
491        let key_params = SecretKeyParamsBuilder::default()
492            .key_type(KeyType::EdDSALegacy)
493            .can_certify(true)
494            .can_sign(true)
495            .primary_user_id("Me-X <me-25519-legacy@mail.com>".into())
496            .passphrase(None)
497            .preferred_symmetric_algorithms(smallvec![
498                SymmetricKeyAlgorithm::MKV128256,
499                SymmetricKeyAlgorithm::MKV128192,
500                SymmetricKeyAlgorithm::MKV128128,
501                SymmetricKeyAlgorithm::AES256,
502                SymmetricKeyAlgorithm::AES192,
503                SymmetricKeyAlgorithm::AES128,
504            ])
505            .preferred_hash_algorithms(smallvec![
506                HashAlgorithm::SHA2_256,
507                HashAlgorithm::SHA2_384,
508                HashAlgorithm::SHA2_512,
509                HashAlgorithm::SHA2_224,
510                HashAlgorithm::SHA1,
511            ])
512            .preferred_compression_algorithms(smallvec![
513                CompressionAlgorithm::ZLIB,
514                CompressionAlgorithm::ZIP,
515            ])
516            .subkey(
517                SubkeyParamsBuilder::default()
518                    .key_type(KeyType::ECDH(ECCCurve::Curve25519))
519                    .can_encrypt(true)
520                    .passphrase(None)
521                    .build()
522                    .unwrap(),
523            )
524            .build()
525            .unwrap();
526
527        let key = key_params
528            .generate(&mut rng)
529            .expect("failed to generate secret key");
530
531        let signed_key = key
532            .sign(&mut rng, || "".into())
533            .expect("failed to sign key");
534
535        let armor = signed_key
536            .to_armored_string(None.into())
537            .expect("failed to serialize key");
538
539        // std::fs::write("sample-25519-legacy.sec.asc", &armor).unwrap();
540
541        let (signed_key2, _headers) =
542            SignedSecretKey::from_string(&armor).expect("failed to parse key");
543        signed_key2.verify().expect("invalid key");
544
545        assert_eq!(signed_key, signed_key2);
546
547        let public_key = signed_key.public_key();
548
549        let public_signed_key = public_key
550            .sign(&mut rng, &signed_key, || "".into())
551            .expect("failed to sign public key");
552
553        public_signed_key.verify().expect("invalid public key");
554
555        let armor = public_signed_key
556            .to_armored_string(None.into())
557            .expect("failed to serialize public key");
558
559        // std::fs::write("sample-25519-legacy.pub.asc", &armor).unwrap();
560
561        let (signed_key2, _headers) =
562            SignedPublicKey::from_string(&armor).expect("failed to parse public key");
563        signed_key2.verify().expect("invalid public key");
564    }
565
566    #[ignore]
567    #[test]
568    fn key_gen_25519_rfc9580_long() {
569        let mut rng = ChaCha8Rng::seed_from_u64(0);
570
571        for key_version in [KeyVersion::V4, KeyVersion::V6] {
572            println!("key version {:?}", key_version);
573
574            for i in 0..10_000 {
575                println!("round {i}");
576                gen_25519_rfc9580(&mut rng, key_version);
577            }
578        }
579    }
580
581    #[test]
582    fn key_gen_25519_rfc9580_short() {
583        let mut rng = ChaCha8Rng::seed_from_u64(0);
584
585        for key_version in [KeyVersion::V4, KeyVersion::V6] {
586            println!("key version {:?}", key_version);
587
588            for _ in 0..100 {
589                gen_25519_rfc9580(&mut rng, key_version);
590            }
591        }
592    }
593
594    fn gen_25519_rfc9580<R: Rng + CryptoRng>(mut rng: R, version: KeyVersion) {
595        // The RFC 9580 key format variants based on Curve 25519 (X25519/Ed25519)
596
597        let _ = pretty_env_logger::try_init();
598
599        let key_params = SecretKeyParamsBuilder::default()
600            .version(version)
601            .key_type(KeyType::Ed25519)
602            .can_certify(true)
603            .can_sign(true)
604            .primary_user_id("Me-X <me-25519-rfc9580@mail.com>".into())
605            .passphrase(None)
606            .preferred_symmetric_algorithms(smallvec![
607                SymmetricKeyAlgorithm::MKV128256,
608                SymmetricKeyAlgorithm::MKV128192,
609                SymmetricKeyAlgorithm::MKV128128,
610                SymmetricKeyAlgorithm::AES256,
611                SymmetricKeyAlgorithm::AES192,
612                SymmetricKeyAlgorithm::AES128,
613            ])
614            .preferred_hash_algorithms(smallvec![
615                HashAlgorithm::SHA2_256,
616                HashAlgorithm::SHA2_384,
617                HashAlgorithm::SHA2_512,
618                HashAlgorithm::SHA2_224,
619                HashAlgorithm::SHA1,
620            ])
621            .preferred_compression_algorithms(smallvec![
622                CompressionAlgorithm::ZLIB,
623                CompressionAlgorithm::ZIP,
624            ])
625            .subkey(
626                SubkeyParamsBuilder::default()
627                    .version(version)
628                    .key_type(KeyType::X25519)
629                    .can_encrypt(true)
630                    .passphrase(None)
631                    .build()
632                    .unwrap(),
633            )
634            .build()
635            .unwrap();
636
637        let key = key_params
638            .generate(&mut rng)
639            .expect("failed to generate secret key");
640
641        let signed_key = key
642            .sign(&mut rng, || "".into())
643            .expect("failed to sign key");
644
645        let armor = signed_key
646            .to_armored_string(None.into())
647            .expect("failed to serialize key");
648
649        // std::fs::write("sample-25519-rfc9580.sec.asc", &armor).unwrap();
650
651        let (signed_key2, _headers) =
652            SignedSecretKey::from_string(&armor).expect("failed to parse key");
653        signed_key2.verify().expect("invalid key");
654
655        assert_eq!(signed_key, signed_key2);
656
657        let public_key = signed_key.public_key();
658
659        let public_signed_key = public_key
660            .sign(&mut rng, &signed_key, || "".into())
661            .expect("failed to sign public key");
662
663        public_signed_key.verify().expect("invalid public key");
664
665        let armor = public_signed_key
666            .to_armored_string(None.into())
667            .expect("failed to serialize public key");
668
669        // std::fs::write("sample-25519-rfc9580.pub.asc", &armor).unwrap();
670
671        let (signed_key2, _headers) =
672            SignedPublicKey::from_string(&armor).expect("failed to parse public key");
673        signed_key2.verify().expect("invalid public key");
674    }
675
676    fn gen_ecdsa_ecdh<R: Rng + CryptoRng>(
677        mut rng: R,
678        ecdsa: ECCCurve,
679        ecdh: ECCCurve,
680        version: KeyVersion,
681    ) {
682        let _ = pretty_env_logger::try_init();
683
684        let key_params = SecretKeyParamsBuilder::default()
685            .version(version)
686            .key_type(KeyType::ECDSA(ecdsa.clone()))
687            .can_certify(true)
688            .can_sign(true)
689            .primary_user_id("Me-X <me-ecdsa@mail.com>".into())
690            .passphrase(None)
691            .preferred_symmetric_algorithms(smallvec![
692                SymmetricKeyAlgorithm::MKV128256,
693                SymmetricKeyAlgorithm::MKV128192,
694                SymmetricKeyAlgorithm::MKV128128,
695                SymmetricKeyAlgorithm::AES256,
696                SymmetricKeyAlgorithm::AES192,
697                SymmetricKeyAlgorithm::AES128,
698            ])
699            .preferred_hash_algorithms(smallvec![
700                HashAlgorithm::SHA2_256,
701                HashAlgorithm::SHA2_384,
702                HashAlgorithm::SHA2_512,
703                HashAlgorithm::SHA2_224,
704                HashAlgorithm::SHA1,
705            ])
706            .preferred_compression_algorithms(smallvec![
707                CompressionAlgorithm::ZLIB,
708                CompressionAlgorithm::ZIP,
709            ])
710            .subkey(
711                SubkeyParamsBuilder::default()
712                    .version(version)
713                    .key_type(KeyType::ECDH(ecdh.clone()))
714                    .can_encrypt(true)
715                    .passphrase(None)
716                    .build()
717                    .unwrap(),
718            )
719            .build()
720            .unwrap();
721
722        let key = key_params
723            .generate(&mut rng)
724            .expect("failed to generate secret key");
725
726        let signed_key = key
727            .sign(&mut rng, || "".into())
728            .expect("failed to sign key");
729
730        let armor = signed_key
731            .to_armored_string(None.into())
732            .expect("failed to serialize key");
733
734        // std::fs::write(
735        //     format!("sample-ecdsa-{ecdsa:?}-ecdh-{ecdh:?}.pub.asc"),
736        //     &armor,
737        // )
738        // .unwrap();
739
740        let (signed_key2, _headers) =
741            SignedSecretKey::from_string(&armor).expect("failed to parse key");
742        signed_key2.verify().expect("invalid key");
743
744        assert_eq!(signed_key, signed_key2);
745
746        let public_key = signed_key.public_key();
747
748        let public_signed_key = public_key
749            .sign(&mut rng, &signed_key, || "".into())
750            .expect("failed to sign public key");
751
752        public_signed_key.verify().expect("invalid public key");
753
754        let armor = public_signed_key
755            .to_armored_string(None.into())
756            .expect("failed to serialize public key");
757
758        // std::fs::write(
759        //     format!("sample-ecdsa-{ecdsa:?}-ecdh-{ecdh:?}.pub.asc"),
760        //     &armor,
761        // )
762        // .unwrap();
763
764        let (signed_key2, _headers) =
765            SignedPublicKey::from_string(&armor).expect("failed to parse public key");
766        signed_key2.verify().expect("invalid public key");
767    }
768
769    #[test]
770    fn key_gen_ecdsa_p256() {
771        let mut rng = &mut ChaCha8Rng::seed_from_u64(0);
772
773        for key_version in [KeyVersion::V4, KeyVersion::V6] {
774            println!("key version {:?}", key_version);
775
776            for _ in 0..=175 {
777                gen_ecdsa_ecdh(&mut rng, ECCCurve::P256, ECCCurve::P256, key_version);
778            }
779        }
780    }
781
782    #[test]
783    fn key_gen_ecdsa_p384() {
784        let mut rng = &mut ChaCha8Rng::seed_from_u64(0);
785
786        for key_version in [KeyVersion::V4, KeyVersion::V6] {
787            println!("key version {:?}", key_version);
788
789            for _ in 0..100 {
790                gen_ecdsa_ecdh(&mut rng, ECCCurve::P384, ECCCurve::P384, key_version);
791            }
792        }
793    }
794
795    #[test]
796    fn key_gen_ecdsa_p521() {
797        let mut rng = &mut ChaCha8Rng::seed_from_u64(0);
798
799        for key_version in [KeyVersion::V4, KeyVersion::V6] {
800            println!("key version {:?}", key_version);
801
802            for _ in 0..100 {
803                gen_ecdsa_ecdh(&mut rng, ECCCurve::P521, ECCCurve::P521, key_version);
804            }
805        }
806    }
807
808    #[test]
809    fn key_gen_ecdsa_secp256k1() {
810        let mut rng = &mut ChaCha8Rng::seed_from_u64(0);
811
812        for _ in 0..100 {
813            gen_ecdsa_ecdh(
814                &mut rng,
815                ECCCurve::Secp256k1,
816                ECCCurve::Curve25519, // we don't currently support ECDH over Secp256k1
817                KeyVersion::V4,       // use of secp256k1 isn't specified in RFC 9580
818            );
819        }
820    }
821
822    fn gen_dsa<R: Rng + CryptoRng>(mut rng: R, key_size: DsaKeySize) {
823        let _ = pretty_env_logger::try_init();
824
825        let key_params = SecretKeyParamsBuilder::default()
826            .key_type(KeyType::Dsa(key_size))
827            .can_certify(true)
828            .can_sign(true)
829            .primary_user_id("Me-X <me-dsa@mail.com>".into())
830            .passphrase(None)
831            .preferred_symmetric_algorithms(smallvec![
832                SymmetricKeyAlgorithm::MKV128256,
833                SymmetricKeyAlgorithm::MKV128192,
834                SymmetricKeyAlgorithm::MKV128128,
835                SymmetricKeyAlgorithm::AES256,
836                SymmetricKeyAlgorithm::AES192,
837                SymmetricKeyAlgorithm::AES128,
838            ])
839            .preferred_hash_algorithms(smallvec![
840                HashAlgorithm::SHA2_256,
841                HashAlgorithm::SHA2_384,
842                HashAlgorithm::SHA2_512,
843                HashAlgorithm::SHA2_224,
844                HashAlgorithm::SHA1,
845            ])
846            .preferred_compression_algorithms(smallvec![
847                CompressionAlgorithm::ZLIB,
848                CompressionAlgorithm::ZIP,
849            ])
850            .subkey(
851                SubkeyParamsBuilder::default()
852                    .key_type(KeyType::ECDH(ECCCurve::Curve25519))
853                    .can_encrypt(true)
854                    .passphrase(None)
855                    .build()
856                    .unwrap(),
857            )
858            .build()
859            .unwrap();
860
861        let key = key_params
862            .generate(&mut rng)
863            .expect("failed to generate secret key");
864
865        let signed_key = key
866            .sign(&mut rng, || "".into())
867            .expect("failed to sign key");
868
869        let armor = signed_key
870            .to_armored_string(None.into())
871            .expect("failed to serialize key");
872
873        // std::fs::write("sample-dsa.sec.asc", &armor).unwrap();
874
875        let (signed_key2, _headers) =
876            SignedSecretKey::from_string(&armor).expect("failed to parse key");
877        signed_key2.verify().expect("invalid key");
878
879        assert_eq!(signed_key, signed_key2);
880
881        let public_key = signed_key.public_key();
882
883        let public_signed_key = public_key
884            .sign(&mut rng, &signed_key, || "".into())
885            .expect("failed to sign public key");
886
887        public_signed_key.verify().expect("invalid public key");
888
889        let armor = public_signed_key
890            .to_armored_string(None.into())
891            .expect("failed to serialize public key");
892
893        // std::fs::write(format!("sample-dsa-{key_size:?}.pub.asc"), &armor).unwrap();
894
895        let (signed_key2, _headers) =
896            SignedPublicKey::from_string(&armor).expect("failed to parse public key");
897        signed_key2.verify().expect("invalid public key");
898    }
899
900    // Test is slow in debug mode
901    #[test]
902    #[ignore]
903    fn key_gen_dsa() {
904        let mut rng = &mut ChaCha8Rng::seed_from_u64(0);
905        for _ in 0..10 {
906            gen_dsa(&mut rng, DsaKeySize::B1024);
907            gen_dsa(&mut rng, DsaKeySize::B2048);
908            gen_dsa(&mut rng, DsaKeySize::B3072);
909        }
910    }
911}