Skip to main content

sequoia_openpgp/packet/key/
v6.rs

1//! OpenPGP v6 key packet.
2
3use std::fmt;
4use std::cmp::Ordering;
5use std::hash::Hasher;
6use std::time;
7
8#[cfg(test)]
9use quickcheck::{Arbitrary, Gen};
10
11use crate::Error;
12use crate::crypto::{mpi, hash::Hash, mem::Protected, KeyPair};
13use crate::packet::key::{
14    KeyParts,
15    KeyRole,
16    KeyRoleRT,
17    PublicParts,
18    SecretParts,
19    UnspecifiedParts,
20};
21use crate::packet::prelude::*;
22use crate::PublicKeyAlgorithm;
23use crate::HashAlgorithm;
24use crate::types::Timestamp;
25use crate::Result;
26use crate::crypto::Password;
27use crate::KeyID;
28use crate::Fingerprint;
29use crate::KeyHandle;
30use crate::policy::HashAlgoSecurity;
31
32/// Holds a public key, public subkey, private key or private subkey
33/// packet.
34///
35/// Use [`Key6::generate_rsa`] or [`Key6::generate_ecc`] to create a
36/// new key.
37///
38/// Existing key material can be turned into an OpenPGP key using
39/// [`Key6::new`], [`Key6::with_secret`], [`Key6::import_public_x25519`],
40/// [`Key6::import_public_ed25519`], [`Key6::import_public_rsa`],
41/// [`Key6::import_secret_x25519`], [`Key6::import_secret_ed25519`],
42/// and [`Key6::import_secret_rsa`].
43///
44/// Whether you create a new key or import existing key material, you
45/// still need to create a binding signature, and, for signing keys, a
46/// back signature before integrating the key into a certificate.
47///
48/// Normally, you won't directly use `Key6`, but [`Key`], which is a
49/// relatively thin wrapper around `Key6`.
50///
51/// See [Section 5.5 of RFC 9580] and [the documentation for `Key`]
52/// for more details.
53///
54/// [Section 5.5 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.5
55/// [the documentation for `Key`]: super::Key
56/// [`Key`]: super::Key
57#[derive(PartialEq, Eq, Hash)]
58pub struct Key6<P: KeyParts, R: KeyRole> {
59    pub(crate) common: Key4<P, R>,
60}
61
62// derive(Clone) doesn't work as expected with generic type parameters
63// that don't implement clone: it adds a trait bound on Clone to P and
64// R in the Clone implementation.  Happily, we don't need P or R to
65// implement Clone: they are just marker traits, which we can clone
66// manually.
67//
68// See: https://github.com/rust-lang/rust/issues/26925
69impl<P, R> Clone for Key6<P, R>
70    where P: KeyParts, R: KeyRole
71{
72    fn clone(&self) -> Self {
73        Key6 {
74            common: self.common.clone(),
75        }
76    }
77}
78
79impl<P, R> fmt::Debug for Key6<P, R>
80where P: KeyParts,
81      R: KeyRole,
82{
83    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84        f.debug_struct("Key6")
85            .field("fingerprint", &self.fingerprint())
86            .field("creation_time", &self.creation_time())
87            .field("pk_algo", &self.pk_algo())
88            .field("mpis", &self.mpis())
89            .field("secret", &self.optional_secret())
90            .finish()
91    }
92}
93
94impl<P, R> fmt::Display for Key6<P, R>
95where P: KeyParts,
96      R: KeyRole,
97{
98    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99        write!(f, "{}", self.fingerprint())
100    }
101}
102
103impl<P, R> Key6<P, R>
104where P: KeyParts,
105      R: KeyRole,
106{
107    /// The security requirements of the hash algorithm for
108    /// self-signatures.
109    ///
110    /// A cryptographic hash algorithm usually has [three security
111    /// properties]: pre-image resistance, second pre-image
112    /// resistance, and collision resistance.  If an attacker can
113    /// influence the signed data, then the hash algorithm needs to
114    /// have both second pre-image resistance, and collision
115    /// resistance.  If not, second pre-image resistance is
116    /// sufficient.
117    ///
118    ///   [three security properties]: https://en.wikipedia.org/wiki/Cryptographic_hash_function#Properties
119    ///
120    /// In general, an attacker may be able to influence third-party
121    /// signatures.  But direct key signatures, and binding signatures
122    /// are only over data fully determined by signer.  And, an
123    /// attacker's control over self signatures over User IDs is
124    /// limited due to their structure.
125    ///
126    /// These observations can be used to extend the life of a hash
127    /// algorithm after its collision resistance has been partially
128    /// compromised, but not completely broken.  For more details,
129    /// please refer to the documentation for [HashAlgoSecurity].
130    ///
131    ///   [HashAlgoSecurity]: crate::policy::HashAlgoSecurity
132    pub fn hash_algo_security(&self) -> HashAlgoSecurity {
133        HashAlgoSecurity::SecondPreImageResistance
134    }
135
136    /// Compares the public bits of two keys.
137    ///
138    /// This returns `Ordering::Equal` if the public MPIs, creation
139    /// time, and algorithm of the two `Key6`s match.  This does not
140    /// consider the packets' encodings, packets' tags or their secret
141    /// key material.
142    pub fn public_cmp<PB, RB>(&self, b: &Key6<PB, RB>) -> Ordering
143    where PB: KeyParts,
144          RB: KeyRole,
145    {
146        self.mpis().cmp(b.mpis())
147            .then_with(|| self.creation_time().cmp(&b.creation_time()))
148            .then_with(|| self.pk_algo().cmp(&b.pk_algo()))
149    }
150
151    /// Tests whether two keys are equal modulo their secret key
152    /// material.
153    ///
154    /// This returns true if the public MPIs, creation time and
155    /// algorithm of the two `Key6`s match.  This does not consider
156    /// the packets' encodings, packets' tags or their secret key
157    /// material.
158    pub fn public_eq<PB, RB>(&self, b: &Key6<PB, RB>) -> bool
159    where PB: KeyParts,
160          RB: KeyRole,
161    {
162        self.public_cmp(b) == Ordering::Equal
163    }
164
165    /// Hashes everything but any secret key material into state.
166    ///
167    /// This is an alternate implementation of [`Hash`], which never
168    /// hashes the secret key material.
169    ///
170    ///   [`Hash`]: std::hash::Hash
171    pub fn public_hash<H>(&self, state: &mut H)
172    where H: Hasher
173    {
174        self.common.public_hash(state);
175    }
176}
177
178impl<P, R> Key6<P, R>
179where
180    P: KeyParts,
181    R: KeyRole,
182{
183    /// Gets the `Key`'s creation time.
184    pub fn creation_time(&self) -> time::SystemTime {
185        self.common.creation_time()
186    }
187
188    /// Gets the `Key`'s creation time without converting it to a
189    /// system time.
190    ///
191    /// This conversion may truncate the time to signed 32-bit time_t.
192    pub(crate) fn creation_time_raw(&self) -> Timestamp {
193        self.common.creation_time_raw()
194    }
195
196    /// Sets the `Key`'s creation time.
197    ///
198    /// `timestamp` is converted to OpenPGP's internal format,
199    /// [`Timestamp`]: a 32-bit quantity containing the number of
200    /// seconds since the Unix epoch.
201    ///
202    /// `timestamp` is silently rounded to match the internal
203    /// resolution.  An error is returned if `timestamp` is out of
204    /// range.
205    ///
206    /// [`Timestamp`]: crate::types::Timestamp
207    pub fn set_creation_time<T>(&mut self, timestamp: T)
208                                -> Result<time::SystemTime>
209    where T: Into<time::SystemTime>
210    {
211        self.common.set_creation_time(timestamp)
212    }
213
214    /// Gets the public key algorithm.
215    pub fn pk_algo(&self) -> PublicKeyAlgorithm {
216        self.common.pk_algo()
217    }
218
219    /// Sets the public key algorithm.
220    ///
221    /// Returns the old public key algorithm.
222    pub fn set_pk_algo(&mut self, pk_algo: PublicKeyAlgorithm)
223                       -> PublicKeyAlgorithm
224    {
225        self.common.set_pk_algo(pk_algo)
226    }
227
228    /// Returns a reference to the `Key`'s MPIs.
229    pub fn mpis(&self) -> &mpi::PublicKey {
230        self.common.mpis()
231    }
232
233    /// Returns a mutable reference to the `Key`'s MPIs.
234    pub fn mpis_mut(&mut self) -> &mut mpi::PublicKey {
235        self.common.mpis_mut()
236    }
237
238    /// Sets the `Key`'s MPIs.
239    ///
240    /// This function returns the old MPIs, if any.
241    pub fn set_mpis(&mut self, mpis: mpi::PublicKey) -> mpi::PublicKey {
242        self.common.set_mpis(mpis)
243    }
244
245    /// Returns whether the `Key` contains secret key material.
246    pub fn has_secret(&self) -> bool {
247        self.common.has_secret()
248    }
249
250    /// Returns whether the `Key` contains unencrypted secret key
251    /// material.
252    ///
253    /// This returns false if the `Key` doesn't contain any secret key
254    /// material.
255    pub fn has_unencrypted_secret(&self) -> bool {
256        self.common.has_unencrypted_secret()
257    }
258
259    /// Returns `Key`'s secret key material, if any.
260    pub fn optional_secret(&self) -> Option<&SecretKeyMaterial> {
261        self.common.optional_secret()
262    }
263
264    /// Computes and returns the `Key`'s `Fingerprint` and returns it as
265    /// a `KeyHandle`.
266    ///
267    /// See [Section 5.5.4 of RFC 9580].
268    ///
269    /// [Section 5.5.4 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.5.4
270    pub fn key_handle(&self) -> KeyHandle {
271        self.fingerprint().into()
272    }
273
274    /// Computes and returns the `Key`'s `Fingerprint`.
275    ///
276    /// See [Key IDs and Fingerprints].
277    ///
278    /// [Key IDs and Fingerprints]: https://www.rfc-editor.org/rfc/rfc9580.html#key-ids-fingerprints
279    pub fn fingerprint(&self) -> Fingerprint {
280        let fp = self.common.fingerprint.get_or_init(|| {
281            let mut h = HashAlgorithm::SHA256.context()
282                .expect("SHA256 is MTI for RFC9580")
283            // v6 fingerprints are computed the same way a key is
284            // hashed for v6 signatures.
285                .for_signature(6);
286
287            self.hash(&mut h).expect("v6 key hashing is infallible");
288
289            let mut digest = [0u8; 32];
290            let _ = h.digest(&mut digest);
291            Fingerprint::V6(digest)
292        });
293
294        // Currently, it could happen that a Key4 has its fingerprint
295        // computed, and is then converted to a Key6.  That is only
296        // possible within this crate, and should not happen.  Assert
297        // that.  The better way to handle this is to have a CommonKey
298        // struct which both Key4 and Key6 use, so that a Key6 does
299        // not start out as a Key4, preventing this issue.
300        debug_assert!(matches!(fp, Fingerprint::V6(_)));
301
302        fp.clone()
303    }
304
305    /// Computes and returns the `Key`'s `Key ID`.
306    ///
307    /// See [Section 5.5.4 of RFC 9580].
308    ///
309    /// [Section 5.5.4 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.5.4
310    pub fn keyid(&self) -> KeyID {
311        self.fingerprint().into()
312    }
313
314    /// Creates a v6 key from a v4 key.  Used internally in
315    /// constructors.
316    pub(crate) fn from_common(common: Key4<P, R>) -> Self {
317        Key6 { common }
318    }
319
320    /// Creates an OpenPGP public key from the specified key material.
321    ///
322    /// This is an internal version for parse.rs that avoids going
323    /// through SystemTime.
324    pub(crate) fn make(creation_time: Timestamp,
325                       pk_algo: PublicKeyAlgorithm,
326                       mpis: mpi::PublicKey,
327                       secret: Option<SecretKeyMaterial>)
328                       -> Result<Self>
329    where
330    {
331        Ok(Key6 {
332            common: Key4::make(creation_time, pk_algo, mpis, secret)?,
333        })
334    }
335
336    pub(crate) fn role(&self) -> KeyRoleRT {
337        self.common.role()
338    }
339
340    pub(crate) fn set_role(&mut self, role: KeyRoleRT) {
341        self.common.set_role(role);
342    }
343}
344
345impl<R> Key6<key::PublicParts, R>
346where R: KeyRole,
347{
348    /// Creates an OpenPGP public key from the specified key material.
349    pub fn new<T>(creation_time: T, pk_algo: PublicKeyAlgorithm,
350                  mpis: mpi::PublicKey)
351                  -> Result<Self>
352    where T: Into<time::SystemTime>
353    {
354        Ok(Key6 {
355            common: Key4::new(creation_time, pk_algo, mpis)?,
356        })
357    }
358
359    /// Creates an OpenPGP public key packet from existing X25519 key
360    /// material.
361    ///
362    /// The key will have its creation date set to `ctime` or the
363    /// current time if `None` is given.
364    pub fn import_public_x25519<T>(public_key: &[u8], ctime: T)
365                                   -> Result<Self>
366    where
367        T: Into<Option<time::SystemTime>>,
368    {
369        Ok(Key6 {
370            common: Key4::new(ctime.into().unwrap_or_else(crate::now),
371                              PublicKeyAlgorithm::X25519,
372                              mpi::PublicKey::X25519 {
373                                  u: public_key.try_into()?,
374                              })?,
375        })
376    }
377
378    /// Creates an OpenPGP public key packet from existing X448 key
379    /// material.
380    ///
381    /// The key will have its creation date set to `ctime` or the
382    /// current time if `None` is given.
383    pub fn import_public_x448<T>(public_key: &[u8], ctime: T)
384                                 -> Result<Self>
385    where
386        T: Into<Option<time::SystemTime>>,
387    {
388        Ok(Key6 {
389            common: Key4::new(ctime.into().unwrap_or_else(crate::now),
390                              PublicKeyAlgorithm::X448,
391                              mpi::PublicKey::X448 {
392                                  u: Box::new(public_key.try_into()?),
393                              })?,
394        })
395    }
396
397    /// Creates an OpenPGP public key packet from existing Ed25519 key
398    /// material.
399    ///
400    /// The key will have its creation date set to `ctime` or the
401    /// current time if `None` is given.
402    pub fn import_public_ed25519<T>(public_key: &[u8], ctime: T) -> Result<Self>
403    where
404        T: Into<Option<time::SystemTime>>,
405    {
406        Ok(Key6 {
407            common: Key4::new(ctime.into().unwrap_or_else(crate::now),
408                              PublicKeyAlgorithm::Ed25519,
409                              mpi::PublicKey::Ed25519 {
410                                  a: public_key.try_into()?,
411                              })?,
412        })
413    }
414
415    /// Creates an OpenPGP public key packet from existing Ed448 key
416    /// material.
417    ///
418    /// The key will have its creation date set to `ctime` or the
419    /// current time if `None` is given.
420    pub fn import_public_ed448<T>(public_key: &[u8], ctime: T) -> Result<Self>
421    where
422        T: Into<Option<time::SystemTime>>,
423    {
424        Ok(Key6 {
425            common: Key4::new(ctime.into().unwrap_or_else(crate::now),
426                              PublicKeyAlgorithm::Ed448,
427                              mpi::PublicKey::Ed448 {
428                                  a: Box::new(public_key.try_into()?),
429                              })?,
430        })
431    }
432
433    /// Creates an OpenPGP public key packet from existing RSA key
434    /// material.
435    ///
436    /// The RSA key will use the public exponent `e` and the modulo
437    /// `n`. The key will have its creation date set to `ctime` or the
438    /// current time if `None` is given.
439    pub fn import_public_rsa<T>(e: &[u8], n: &[u8], ctime: T)
440                                -> Result<Self> where T: Into<Option<time::SystemTime>>
441    {
442        Ok(Key6 {
443            common: Key4::import_public_rsa(e, n, ctime)?,
444        })
445    }
446
447    /// Creates an OpenPGP public key packet from existing ML-DSA-65
448    /// and Ed25519 key material.
449    ///
450    /// Note: in OpenPGP, ML-DSA keys are composite keys and include
451    /// an EdDSA key to provide a pre-quantum security fallback.
452    ///
453    /// The ML-DSA-65 public key must be exactly 1952 bytes.  The
454    /// Ed25519 public key must be exactly 32 bytes.
455    ///
456    /// The key will have its creation date set to `ctime` or the
457    /// current time if `None` is given.
458    pub fn import_public_mldsa65_ed25519<T>(
459        mldsa: &[u8], eddsa: &[u8], ctime: T)
460        -> Result<Self>
461    where
462        T: Into<Option<time::SystemTime>>
463    {
464        Ok(Key6 {
465            common: Key4::new(ctime.into().unwrap_or_else(crate::now),
466                              PublicKeyAlgorithm::MLDSA65_Ed25519,
467                              mpi::PublicKey::MLDSA65_Ed25519 {
468                                  eddsa: Box::new(eddsa.try_into()?),
469                                  mldsa: Box::new(mldsa.try_into()?),
470                              })?,
471        })
472    }
473
474    /// Creates an OpenPGP public key packet from existing ML-DSA-87
475    /// and Ed448 key material.
476    ///
477    /// Note: in OpenPGP, ML-DSA keys are composite keys and include
478    /// an EdDSA key to provide a pre-quantum security fallback.
479    ///
480    /// The ML-DSA-87 public key must be exactly 2592 bytes.  The
481    /// Ed448 public key must be exactly 57 bytes.
482    ///
483    /// The key will have its creation date set to `ctime` or the
484    /// current time if `None` is given.
485    pub fn import_public_mldsa87_ed448<T>(
486        mldsa: &[u8], eddsa: &[u8], ctime: T)
487        -> Result<Self>
488    where
489        T: Into<Option<time::SystemTime>>
490    {
491        Ok(Key6 {
492            common: Key4::new(ctime.into().unwrap_or_else(crate::now),
493                              PublicKeyAlgorithm::MLDSA87_Ed448,
494                              mpi::PublicKey::MLDSA87_Ed448 {
495                                  eddsa: Box::new(eddsa.try_into()?),
496                                  mldsa: Box::new(mldsa.try_into()?),
497                              })?,
498        })
499    }
500
501    /// Creates an OpenPGP public key packet from existing
502    /// SLH-DSA-128s key material.
503    ///
504    /// SLH-DSA-128s keys must be exactly 32 bytes.
505    ///
506    /// The key will have its creation date set to `ctime` or the
507    /// current time if `None` is given.
508    pub fn import_public_slhdsa128s<T>(public: &[u8], ctime: T)
509        -> Result<Self>
510    where
511        T: Into<Option<time::SystemTime>>
512    {
513        Ok(Key6 {
514            common: Key4::new(ctime.into().unwrap_or_else(crate::now),
515                              PublicKeyAlgorithm::SLHDSA128s,
516                              mpi::PublicKey::SLHDSA128s {
517                                  public: public.try_into()?,
518                              })?,
519        })
520    }
521
522    /// Creates an OpenPGP public key packet from existing
523    /// SLH-DSA-128f key material.
524    ///
525    /// SLH-DSA-128f keys must be exactly 32 bytes.
526    ///
527    /// The key will have its creation date set to `ctime` or the
528    /// current time if `None` is given.
529    pub fn import_public_slhdsa128f<T>(public: &[u8], ctime: T)
530        -> Result<Self>
531    where
532        T: Into<Option<time::SystemTime>>
533    {
534        Ok(Key6 {
535            common: Key4::new(ctime.into().unwrap_or_else(crate::now),
536                              PublicKeyAlgorithm::SLHDSA128f,
537                              mpi::PublicKey::SLHDSA128f {
538                                  public: public.try_into()?,
539                              })?,
540        })
541    }
542
543    /// Creates an OpenPGP public key packet from existing
544    /// SLH-DSA-256s key material.
545    ///
546    /// SLH-DSA-256s keys must be exactly 64 bytes.
547    ///
548    /// The key will have its creation date set to `ctime` or the
549    /// current time if `None` is given.
550    pub fn import_public_slhdsa256s<T>(public: &[u8], ctime: T)
551        -> Result<Self>
552    where
553        T: Into<Option<time::SystemTime>>
554    {
555        Ok(Key6 {
556            common: Key4::new(ctime.into().unwrap_or_else(crate::now),
557                              PublicKeyAlgorithm::SLHDSA256s,
558                              mpi::PublicKey::SLHDSA256s {
559                                  public: Box::new(public.try_into()?),
560                              })?,
561        })
562    }
563
564    /// Creates an OpenPGP public key packet from existing
565    /// ML-KEM768+X25519 key material.
566    ///
567    /// Note: in OpenPGP, ML-KEM keys are composite keys and include
568    /// an X25519 key to provide a pre-quantum security fallback.
569    ///
570    /// ML-KEM768 keys must be exactly 1184 bytes, and X25519 keys
571    /// must be exactly 32 bytes.
572    ///
573    /// The key will have its creation date set to `ctime` or the
574    /// current time if `None` is given.
575    pub fn import_public_mlkem768_x25519<T>(mlkem: &[u8], ecdh: &[u8], ctime: T)
576        -> Result<Self>
577    where
578        T: Into<Option<time::SystemTime>>
579    {
580        Ok(Key6 {
581            common: Key4::new(ctime.into().unwrap_or_else(crate::now),
582                              PublicKeyAlgorithm::MLKEM768_X25519,
583                              mpi::PublicKey::MLKEM768_X25519 {
584                                  ecdh: Box::new(ecdh.try_into()?),
585                                  mlkem: Box::new(mlkem.try_into()?),
586                              })?,
587        })
588    }
589
590    /// Creates an OpenPGP public key packet from existing
591    /// ML-KEM1024+X448 key material.
592    ///
593    /// Note: in OpenPGP, ML-KEM keys are composite keys and include
594    /// an X448 key to provide a pre-quantum security fallback.
595    ///
596    /// ML-KEM1024 keys must be exactly 1568 bytes, and X448 keys must
597    /// be exactly 56 bytes.
598    ///
599    /// The key will have its creation date set to `ctime` or the
600    /// current time if `None` is given.
601    pub fn import_public_mlkem1024_x448<T>(mlkem: &[u8], ecdh: &[u8], ctime: T)
602        -> Result<Self>
603    where
604        T: Into<Option<time::SystemTime>>
605    {
606        Ok(Key6 {
607            common: Key4::new(ctime.into().unwrap_or_else(crate::now),
608                              PublicKeyAlgorithm::MLKEM1024_X448,
609                              mpi::PublicKey::MLKEM1024_X448 {
610                                  ecdh: Box::new(ecdh.try_into()?),
611                                  mlkem: Box::new(mlkem.try_into()?),
612                              })?,
613        })
614    }
615}
616
617impl<R> Key6<SecretParts, R>
618where R: KeyRole,
619{
620    /// Creates an OpenPGP key packet from the specified secret key
621    /// material.
622    pub fn with_secret<T>(creation_time: T, pk_algo: PublicKeyAlgorithm,
623                          mpis: mpi::PublicKey,
624                          secret: SecretKeyMaterial)
625                          -> Result<Self>
626    where T: Into<time::SystemTime>
627    {
628        Ok(Key6 {
629            common: Key4::with_secret(creation_time, pk_algo, mpis, secret)?,
630        })
631    }
632
633    /// Creates a new OpenPGP secret key packet for an existing X25519
634    /// key.
635    ///
636    /// The given `private_key` is expected to be in the native X25519
637    /// representation, i.e. as opaque byte string of length 32.
638    ///
639    /// The key will have its creation date set to `ctime` or the
640    /// current time if `None` is given.
641    pub fn import_secret_x25519<T>(private_key: &[u8],
642                                   ctime: T)
643                                   -> Result<Self>
644    where
645        T: Into<Option<std::time::SystemTime>>,
646    {
647        use crate::crypto::backend::{Backend, interface::Asymmetric};
648
649        let private_key = Protected::from(private_key);
650        let public_key = Backend::x25519_derive_public(&private_key)?;
651
652        Self::with_secret(
653            ctime.into().unwrap_or_else(crate::now),
654            PublicKeyAlgorithm::X25519,
655            mpi::PublicKey::X25519 {
656                u: public_key,
657            },
658            mpi::SecretKeyMaterial::X25519 {
659                x: private_key.into(),
660            }.into())
661    }
662
663    /// Creates a new OpenPGP secret key packet for an existing X448
664    /// key.
665    ///
666    /// The given `private_key` is expected to be in the native X448
667    /// representation, i.e. as opaque byte string of length 32.
668    ///
669    /// The key will have its creation date set to `ctime` or the
670    /// current time if `None` is given.
671    pub fn import_secret_x448<T>(private_key: &[u8],
672                                 ctime: T)
673                                 -> Result<Self>
674    where
675        T: Into<Option<std::time::SystemTime>>,
676    {
677        use crate::crypto::backend::{Backend, interface::Asymmetric};
678
679        let private_key = Protected::from(private_key);
680        let public_key = Backend::x448_derive_public(&private_key)?;
681
682        Self::with_secret(
683            ctime.into().unwrap_or_else(crate::now),
684            PublicKeyAlgorithm::X448,
685            mpi::PublicKey::X448 {
686                u: Box::new(public_key),
687            },
688            mpi::SecretKeyMaterial::X448 {
689                x: private_key.into(),
690            }.into())
691    }
692
693    /// Creates a new OpenPGP secret key packet for an existing
694    /// Ed25519 key.
695    ///
696    /// The key will have its creation date set to `ctime` or the
697    /// current time if `None` is given.
698    pub fn import_secret_ed25519<T>(private_key: &[u8], ctime: T)
699                                    -> Result<Self>
700    where
701        T: Into<Option<time::SystemTime>>,
702    {
703        use crate::crypto::backend::{Backend, interface::Asymmetric};
704
705        let private_key = Protected::from(private_key);
706        let public_key = Backend::ed25519_derive_public(&private_key)?;
707
708        Self::with_secret(
709            ctime.into().unwrap_or_else(crate::now),
710            PublicKeyAlgorithm::Ed25519,
711            mpi::PublicKey::Ed25519 {
712                a: public_key,
713            },
714            mpi::SecretKeyMaterial::Ed25519 {
715                x: private_key.into(),
716            }.into())
717    }
718
719    /// Creates a new OpenPGP secret key packet for an existing
720    /// Ed448 key.
721    ///
722    /// The key will have its creation date set to `ctime` or the
723    /// current time if `None` is given.
724    pub fn import_secret_ed448<T>(private_key: &[u8], ctime: T)
725                                  -> Result<Self>
726    where
727        T: Into<Option<time::SystemTime>>,
728    {
729        use crate::crypto::backend::{Backend, interface::Asymmetric};
730
731        let private_key = Protected::from(private_key);
732        let public_key = Backend::ed448_derive_public(&private_key)?;
733
734        Self::with_secret(
735            ctime.into().unwrap_or_else(crate::now),
736            PublicKeyAlgorithm::Ed448,
737            mpi::PublicKey::Ed448 {
738                a: Box::new(public_key),
739            },
740            mpi::SecretKeyMaterial::Ed448 {
741                x: private_key.into(),
742            }.into())
743    }
744
745    /// Creates a new key pair from a secret `Key` with an unencrypted
746    /// secret key.
747    ///
748    /// # Errors
749    ///
750    /// Fails if the secret key is encrypted.  You can use
751    /// [`Key::decrypt_secret`] to decrypt a key.
752    pub fn into_keypair(self) -> Result<KeyPair> {
753        let (key, secret) = self.take_secret();
754        let secret = match secret {
755            SecretKeyMaterial::Unencrypted(secret) => secret,
756            SecretKeyMaterial::Encrypted(_) =>
757                return Err(Error::InvalidArgument(
758                    "secret key material is encrypted".into()).into()),
759        };
760
761        KeyPair::new(key.role_into_unspecified().into(), secret)
762    }
763}
764
765macro_rules! impl_common_secret_functions_v6 {
766    ($t: ident) => {
767        /// Secret key material handling.
768        impl<R> Key6<$t, R>
769        where R: KeyRole,
770        {
771            /// Takes the `Key`'s `SecretKeyMaterial`, if any.
772            pub fn take_secret(mut self)
773                               -> (Key6<PublicParts, R>, Option<SecretKeyMaterial>)
774            {
775                let old = std::mem::replace(&mut self.common.secret, None);
776                (self.parts_into_public(), old)
777            }
778
779            /// Adds the secret key material to the `Key`, returning
780            /// the old secret key material, if any.
781            pub fn add_secret(mut self, secret: SecretKeyMaterial)
782                              -> (Key6<SecretParts, R>, Option<SecretKeyMaterial>)
783            {
784                let old = std::mem::replace(&mut self.common.secret, Some(secret));
785                (self.parts_into_secret().expect("secret just set"), old)
786            }
787
788            /// Takes the `Key`'s `SecretKeyMaterial`, if any.
789            pub fn steal_secret(&mut self) -> Option<SecretKeyMaterial>
790            {
791                std::mem::replace(&mut self.common.secret, None)
792            }
793        }
794    }
795}
796impl_common_secret_functions_v6!(PublicParts);
797impl_common_secret_functions_v6!(UnspecifiedParts);
798
799/// Secret key handling.
800impl<R> Key6<SecretParts, R>
801where R: KeyRole,
802{
803    /// Gets the `Key`'s `SecretKeyMaterial`.
804    pub fn secret(&self) -> &SecretKeyMaterial {
805        self.common.secret()
806    }
807
808    /// Gets a mutable reference to the `Key`'s `SecretKeyMaterial`.
809    pub fn secret_mut(&mut self) -> &mut SecretKeyMaterial {
810        self.common.secret_mut()
811    }
812
813    /// Takes the `Key`'s `SecretKeyMaterial`.
814    pub fn take_secret(mut self)
815                       -> (Key6<PublicParts, R>, SecretKeyMaterial)
816    {
817        let old = std::mem::replace(&mut self.common.secret, None);
818        (self.parts_into_public(),
819         old.expect("Key<SecretParts, _> has a secret key material"))
820    }
821
822    /// Adds `SecretKeyMaterial` to the `Key`.
823    ///
824    /// This function returns the old secret key material, if any.
825    pub fn add_secret(mut self, secret: SecretKeyMaterial)
826                      -> (Key6<SecretParts, R>, SecretKeyMaterial)
827    {
828        let old = std::mem::replace(&mut self.common.secret, Some(secret));
829        (self.parts_into_secret().expect("secret just set"),
830         old.expect("Key<SecretParts, _> has a secret key material"))
831    }
832
833    /// Decrypts the secret key material using `password`.
834    ///
835    /// In OpenPGP, secret key material can be [protected with a
836    /// password].  The password is usually hardened using a [KDF].
837    ///
838    /// Refer to the documentation of [`Key::decrypt_secret`] for
839    /// details.
840    ///
841    /// This function returns an error if the secret key material is
842    /// not encrypted or the password is incorrect.
843    ///
844    /// [protected with a password]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.5.3
845    /// [KDF]: https://www.rfc-editor.org/rfc/rfc9580.html#section-3.7
846    /// [`Key::decrypt_secret`]: super::Key::decrypt_secret()
847    pub fn decrypt_secret(self, password: &Password) -> Result<Self> {
848        let (key, mut secret) = self.take_secret();
849        // Note: Key version is authenticated.
850        let key = Key::V6(key);
851        secret.decrypt_in_place(&key, password)?;
852        let key = if let Key::V6(k) = key { k } else { unreachable!() };
853        Ok(key.add_secret(secret).0)
854    }
855
856    /// Encrypts the secret key material using `password`.
857    ///
858    /// In OpenPGP, secret key material can be [protected with a
859    /// password].  The password is usually hardened using a [KDF].
860    ///
861    /// Refer to the documentation of [`Key::encrypt_secret`] for
862    /// details.
863    ///
864    /// This returns an error if the secret key material is already
865    /// encrypted.
866    ///
867    /// [protected with a password]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.5.3
868    /// [KDF]: https://www.rfc-editor.org/rfc/rfc9580.html#section-3.7
869    /// [`Key::encrypt_secret`]: super::Key::encrypt_secret()
870    pub fn encrypt_secret(self, password: &Password)
871                          -> Result<Key6<SecretParts, R>>
872    {
873        let (key, mut secret) = self.take_secret();
874        // Note: Key version is authenticated.
875        let key = Key::V6(key);
876        secret.encrypt_in_place(&key, password)?;
877        let key = if let Key::V6(k) = key { k } else { unreachable!() };
878        Ok(key.add_secret(secret).0)
879    }
880}
881
882impl<P, R> From<Key6<P, R>> for super::Key<P, R>
883where P: KeyParts,
884      R: KeyRole,
885{
886    fn from(p: Key6<P, R>) -> Self {
887        super::Key::V6(p)
888    }
889}
890
891#[cfg(test)]
892use crate::packet::key::{
893    PrimaryRole,
894    SubordinateRole,
895    UnspecifiedRole,
896};
897
898#[cfg(test)]
899impl Arbitrary for Key6<PublicParts, PrimaryRole> {
900    fn arbitrary(g: &mut Gen) -> Self {
901        Key6::from_common(Key4::arbitrary(g))
902    }
903}
904
905#[cfg(test)]
906impl Arbitrary for Key6<PublicParts, SubordinateRole> {
907    fn arbitrary(g: &mut Gen) -> Self {
908        Key6::from_common(Key4::arbitrary(g))
909    }
910}
911
912#[cfg(test)]
913impl Arbitrary for Key6<PublicParts, UnspecifiedRole> {
914    fn arbitrary(g: &mut Gen) -> Self {
915        Key6::from_common(Key4::arbitrary(g))
916    }
917}
918
919#[cfg(test)]
920impl Arbitrary for Key6<SecretParts, PrimaryRole> {
921    fn arbitrary(g: &mut Gen) -> Self {
922        Key6::from_common(Key4::arbitrary(g))
923    }
924}
925
926#[cfg(test)]
927impl Arbitrary for Key6<SecretParts, SubordinateRole> {
928    fn arbitrary(g: &mut Gen) -> Self {
929        Key6::from_common(Key4::arbitrary(g))
930    }
931}
932
933
934#[cfg(test)]
935mod tests {
936    use std::time::Duration;
937    use std::time::UNIX_EPOCH;
938
939    use crate::crypto::S2K;
940    use crate::packet::Key;
941    use crate::packet::key;
942    use crate::packet::Packet;
943    use super::*;
944    use crate::PacketPile;
945    use crate::serialize::Serialize;
946    use crate::types::*;
947    use crate::parse::Parse;
948
949    #[test]
950    fn primary_key_encrypt_decrypt() -> Result<()> {
951        key_encrypt_decrypt::<PrimaryRole>()
952    }
953
954    #[test]
955    fn subkey_encrypt_decrypt() -> Result<()> {
956        key_encrypt_decrypt::<SubordinateRole>()
957    }
958
959    fn key_encrypt_decrypt<R>() -> Result<()>
960    where
961        R: KeyRole + PartialEq,
962    {
963        let mut g = quickcheck::Gen::new(256);
964        let p: Password = Vec::<u8>::arbitrary(&mut g).into();
965
966        let check = |key: Key6<SecretParts, R>| -> Result<()> {
967            let key: Key<_, _> = key.into();
968            let encrypted = key.clone().encrypt_secret(&p)?;
969            let decrypted = encrypted.decrypt_secret(&p)?;
970            assert_eq!(key, decrypted);
971            Ok(())
972        };
973
974        use crate::types::Curve::*;
975        for curve in vec![NistP256, NistP384, NistP521, Ed25519] {
976            if ! curve.is_supported() {
977                eprintln!("Skipping unsupported {}", curve);
978                continue;
979            }
980
981            let key: Key6<_, R>
982                = Key6::generate_ecc(true, curve.clone())?;
983            check(key)?;
984        }
985
986        for bits in vec![2048, 3072] {
987            if ! PublicKeyAlgorithm::RSAEncryptSign.is_supported() {
988                eprintln!("Skipping unsupported RSA");
989                continue;
990            }
991
992            let key: Key6<_, R>
993                = Key6::generate_rsa(bits)?;
994            check(key)?;
995        }
996
997        Ok(())
998    }
999
1000    #[test]
1001    fn eq() {
1002        use crate::types::Curve::*;
1003
1004        for curve in vec![NistP256, NistP384, NistP521] {
1005            if ! curve.is_supported() {
1006                eprintln!("Skipping unsupported {}", curve);
1007                continue;
1008            }
1009
1010            let sign_key : Key6<_, key::UnspecifiedRole>
1011                = Key6::generate_ecc(true, curve.clone()).unwrap();
1012            let enc_key : Key6<_, key::UnspecifiedRole>
1013                = Key6::generate_ecc(false, curve).unwrap();
1014            let sign_clone = sign_key.clone();
1015            let enc_clone = enc_key.clone();
1016
1017            assert_eq!(sign_key, sign_clone);
1018            assert_eq!(enc_key, enc_clone);
1019        }
1020
1021        for bits in vec![1024, 2048, 3072, 4096] {
1022            if ! PublicKeyAlgorithm::RSAEncryptSign.is_supported() {
1023                eprintln!("Skipping unsupported RSA");
1024                continue;
1025            }
1026
1027            let key : Key6<_, key::UnspecifiedRole>
1028                = Key6::generate_rsa(bits).unwrap();
1029            let clone = key.clone();
1030            assert_eq!(key, clone);
1031        }
1032    }
1033
1034    #[test]
1035    fn generate_roundtrip() {
1036        use crate::types::Curve::*;
1037
1038        let keys = vec![NistP256, NistP384, NistP521].into_iter().flat_map(|cv|
1039        {
1040            if ! cv.is_supported() {
1041                eprintln!("Skipping unsupported {}", cv);
1042                return Vec::new();
1043            }
1044
1045            let sign_key : Key6<key::SecretParts, key::PrimaryRole>
1046                = Key6::generate_ecc(true, cv.clone()).unwrap();
1047            let enc_key = Key6::generate_ecc(false, cv).unwrap();
1048
1049            vec![sign_key, enc_key]
1050        }).chain(vec![1024, 2048, 3072, 4096].into_iter().filter_map(|b| {
1051            Key6::generate_rsa(b).ok()
1052        }));
1053
1054        for key in keys {
1055            let mut b = Vec::new();
1056            Packet::SecretKey(key.clone().into()).serialize(&mut b).unwrap();
1057
1058            let pp = PacketPile::from_bytes(&b).unwrap();
1059            if let Some(Packet::SecretKey(Key::V6(ref parsed_key))) =
1060                pp.path_ref(&[0])
1061            {
1062                assert_eq!(key.creation_time(), parsed_key.creation_time());
1063                assert_eq!(key.pk_algo(), parsed_key.pk_algo());
1064                assert_eq!(key.mpis(), parsed_key.mpis());
1065                assert_eq!(key.secret(), parsed_key.secret());
1066
1067                assert_eq!(&key, parsed_key);
1068            } else {
1069                panic!("bad packet: {:?}", pp.path_ref(&[0]));
1070            }
1071
1072            let mut b = Vec::new();
1073            let pk4 : Key6<PublicParts, PrimaryRole> = key.clone().into();
1074            Packet::PublicKey(pk4.into()).serialize(&mut b).unwrap();
1075
1076            let pp = PacketPile::from_bytes(&b).unwrap();
1077            if let Some(Packet::PublicKey(Key::V6(ref parsed_key))) =
1078                pp.path_ref(&[0])
1079            {
1080                assert!(! parsed_key.has_secret());
1081
1082                let key = key.take_secret().0;
1083                assert_eq!(&key, parsed_key);
1084            } else {
1085                panic!("bad packet: {:?}", pp.path_ref(&[0]));
1086            }
1087        }
1088    }
1089
1090    #[test]
1091    fn encryption_roundtrip() {
1092        use crate::crypto::SessionKey;
1093        use crate::types::Curve::*;
1094
1095        let keys = vec![NistP256, NistP384, NistP521].into_iter()
1096            .filter_map(|cv| {
1097                Key6::generate_ecc(false, cv).ok()
1098            }).chain(vec![1024, 2048, 3072, 4096].into_iter().filter_map(|b| {
1099                Key6::generate_rsa(b).ok()
1100            })).chain([
1101                (PublicKeyAlgorithm::MLKEM768_X25519, Key6::generate_mlkem768_x25519 as fn() -> Result<_>),
1102                (PublicKeyAlgorithm::MLKEM1024_X448, Key6::generate_mlkem1024_x448),
1103            ].into_iter().filter_map(|(algo, gen)| {
1104                if algo.is_supported() {
1105                    Some(gen().expect(&format!("{} is supported", algo)))
1106                } else {
1107                    None
1108                }
1109            }));
1110
1111        for key in keys.into_iter() {
1112            let key: Key<key::SecretParts, key::UnspecifiedRole> = key.into();
1113            let mut keypair = key.clone().into_keypair().unwrap();
1114            let cipher = SymmetricAlgorithm::AES256;
1115            let sk = SessionKey::new(cipher.key_size().unwrap()).unwrap();
1116
1117            let pkesk = PKESK6::for_recipient(&sk, &key).unwrap();
1118            let sk_ = pkesk.decrypt(&mut keypair, None)
1119                .expect("keypair should be able to decrypt PKESK");
1120            assert_eq!(sk, sk_);
1121
1122            let sk_ =
1123                pkesk.decrypt(&mut keypair, Some(cipher)).unwrap();
1124            assert_eq!(sk, sk_);
1125        }
1126    }
1127
1128    #[test]
1129    fn signature_roundtrip() {
1130        use crate::types::{Curve::*, SignatureType};
1131
1132        let keys = vec![NistP256, NistP384, NistP521].into_iter()
1133            .filter_map(|cv| {
1134                Key6::generate_ecc(true, cv).ok()
1135            }).chain(vec![1024, 2048, 3072, 4096].into_iter().filter_map(|b| {
1136                Key6::generate_rsa(b).ok()
1137            })).chain([
1138                (PublicKeyAlgorithm::MLDSA65_Ed25519, Key6::generate_mldsa65_ed25519 as fn() -> Result<_>),
1139                (PublicKeyAlgorithm::MLDSA87_Ed448, Key6::generate_mldsa87_ed448),
1140                (PublicKeyAlgorithm::SLHDSA128s, Key6::generate_slhdsa128s),
1141                (PublicKeyAlgorithm::SLHDSA128f, Key6::generate_slhdsa128f),
1142                (PublicKeyAlgorithm::SLHDSA256s, Key6::generate_slhdsa256s),
1143            ].into_iter().filter_map(|(algo, gen)| {
1144                if algo.is_supported() {
1145                    Some(gen().expect(&format!("{} is supported", algo)))
1146                } else {
1147                    None
1148                }
1149            }));
1150
1151        for key in keys.into_iter() {
1152            let key: Key<key::SecretParts, key::UnspecifiedRole> = key.into();
1153            let mut keypair = key.clone().into_keypair().unwrap();
1154            let hash = HashAlgorithm::default();
1155
1156            // Sign.
1157            let ctx = hash.context().unwrap().for_signature(key.version());
1158            let sig = SignatureBuilder::new(SignatureType::Binary)
1159                .sign_hash(&mut keypair, ctx).unwrap();
1160
1161            // Verify.
1162            let ctx = hash.context().unwrap().for_signature(key.version());
1163            sig.verify_hash(&key, ctx).unwrap();
1164        }
1165    }
1166
1167    #[test]
1168    fn secret_encryption_roundtrip() {
1169        use crate::types::Curve::*;
1170        use crate::types::SymmetricAlgorithm::*;
1171        use crate::types::AEADAlgorithm::*;
1172
1173        let keys = vec![NistP256, NistP384, NistP521].into_iter()
1174            .filter_map(|cv| -> Option<Key<key::SecretParts, key::PrimaryRole>> {
1175                Key6::generate_ecc(false, cv).map(Into::into).ok()
1176            }).chain(vec![1024, 2048, 3072, 4096].into_iter().filter_map(|b| {
1177                Key6::generate_rsa(b).map(Into::into).ok()
1178            }));
1179
1180        for key in keys {
1181          for (symm, aead) in [(AES128, None),
1182                               (AES128, Some(OCB)),
1183                               (AES256, Some(EAX))] {
1184            if ! aead.map(|a| a.is_supported()).unwrap_or(true) {
1185                continue;
1186            }
1187            assert!(! key.secret().is_encrypted());
1188
1189            let password = Password::from("foobarbaz");
1190            let mut encrypted_key = key.clone();
1191
1192            encrypted_key.secret_mut()
1193                .encrypt_in_place_with(&key, S2K::default(), symm, aead,
1194                                       &password).unwrap();
1195            assert!(encrypted_key.secret().is_encrypted());
1196
1197            encrypted_key.secret_mut()
1198                .decrypt_in_place(&key, &password).unwrap();
1199            assert!(! key.secret().is_encrypted());
1200            assert_eq!(key, encrypted_key);
1201            assert_eq!(key.secret(), encrypted_key.secret());
1202          }
1203        }
1204    }
1205
1206    #[test]
1207    fn encrypt_huge_plaintext() -> Result<()> {
1208        let sk = crate::crypto::SessionKey::new(256).unwrap();
1209
1210        if PublicKeyAlgorithm::RSAEncryptSign.is_supported() {
1211            let rsa2k: Key<SecretParts, UnspecifiedRole> =
1212                Key6::generate_rsa(2048)?.into();
1213            assert!(matches!(
1214                rsa2k.encrypt(&sk).unwrap_err().downcast().unwrap(),
1215                crate::Error::InvalidArgument(_)
1216            ));
1217        }
1218
1219        Ok(())
1220    }
1221
1222    #[test]
1223    fn issue_1016() {
1224        // The fingerprint is a function of the creation time,
1225        // algorithm, and public MPIs.  When we change them make sure
1226        // the fingerprint also changes.
1227
1228        let mut g = quickcheck::Gen::new(256);
1229
1230        let mut key = Key6::<PublicParts, UnspecifiedRole>::arbitrary(&mut g);
1231        let fpr1 = key.fingerprint();
1232        if key.creation_time() == UNIX_EPOCH {
1233            key.set_creation_time(UNIX_EPOCH + Duration::new(1, 0)).expect("ok");
1234        } else {
1235            key.set_creation_time(UNIX_EPOCH).expect("ok");
1236        }
1237        assert_ne!(fpr1, key.fingerprint());
1238
1239        let mut key = Key6::<PublicParts, UnspecifiedRole>::arbitrary(&mut g);
1240        let fpr1 = key.fingerprint();
1241        key.set_pk_algo(PublicKeyAlgorithm::from(u8::from(key.pk_algo()) + 1));
1242        assert_ne!(fpr1, key.fingerprint());
1243
1244        let mut key = Key6::<PublicParts, UnspecifiedRole>::arbitrary(&mut g);
1245        let fpr1 = key.fingerprint();
1246        loop {
1247            let mpis2 = mpi::PublicKey::arbitrary(&mut g);
1248            if key.mpis() != &mpis2 {
1249                *key.mpis_mut() = mpis2;
1250                break;
1251            }
1252        }
1253        assert_ne!(fpr1, key.fingerprint());
1254
1255        let mut key = Key6::<PublicParts, UnspecifiedRole>::arbitrary(&mut g);
1256        let fpr1 = key.fingerprint();
1257        loop {
1258            let mpis2 = mpi::PublicKey::arbitrary(&mut g);
1259            if key.mpis() != &mpis2 {
1260                key.set_mpis(mpis2);
1261                break;
1262            }
1263        }
1264        assert_ne!(fpr1, key.fingerprint());
1265    }
1266
1267    /// Smoke test for ECC key creation, signing and verification, and
1268    /// encryption and decryption.
1269    #[test]
1270    fn ecc_support() -> Result<()> {
1271        for for_signing in [true, false] {
1272            for curve in Curve::variants()
1273                .filter(Curve::is_supported)
1274            {
1275                match curve {
1276                    Curve::Cv25519 if for_signing => continue,
1277                    Curve::Ed25519 if ! for_signing => continue,
1278                    _ => (),
1279                }
1280
1281                eprintln!("curve {}, for signing {:?}", curve, for_signing);
1282                let key: Key<SecretParts, UnspecifiedRole> =
1283                    Key6::generate_ecc(for_signing, curve.clone())?.into();
1284                let mut pair = key.into_keypair()?;
1285
1286                if for_signing {
1287                    use crate::crypto::Signer;
1288                    let hash = HashAlgorithm::default();
1289                    let digest = hash.context()?
1290                        .for_signature(pair.public().version())
1291                        .into_digest()?;
1292                    let sig = pair.sign(hash, &digest)?;
1293                    pair.public().verify(&sig, hash, &digest)?;
1294                } else {
1295                    use crate::crypto::{SessionKey, Decryptor};
1296                    let sk = SessionKey::new(32).unwrap();
1297                    let ciphertext = pair.public().encrypt(&sk)?;
1298                    assert_eq!(pair.decrypt(&ciphertext, Some(sk.len()))?, sk);
1299                }
1300            }
1301        }
1302        Ok(())
1303    }
1304
1305    #[test]
1306    fn ecc_encoding() -> Result<()> {
1307        for for_signing in [true, false] {
1308            for curve in Curve::variants()
1309                .filter(Curve::is_supported)
1310            {
1311                match curve {
1312                    Curve::Cv25519 if for_signing => continue,
1313                    Curve::Ed25519 if ! for_signing => continue,
1314                    _ => (),
1315                }
1316
1317                use crate::crypto::mpi::{Ciphertext, MPI, PublicKey};
1318                eprintln!("curve {}, for signing {:?}", curve, for_signing);
1319
1320                let key: Key<SecretParts, UnspecifiedRole> =
1321                    Key6::generate_ecc(for_signing, curve.clone())?.into();
1322
1323                let uncompressed = |mpi: &MPI| mpi.value()[0] == 0x04;
1324
1325                match key.mpis() {
1326                    PublicKey::X25519 { .. } if ! for_signing => (),
1327                    PublicKey::X448 { .. } if ! for_signing => (),
1328                    PublicKey::Ed25519 { .. } if for_signing => (),
1329                    PublicKey::Ed448 { .. } if for_signing => (),
1330                    PublicKey::ECDSA { curve: c, q } if for_signing => {
1331                        assert!(c == &curve);
1332                        assert!(c != &Curve::Ed25519);
1333                        assert!(uncompressed(q));
1334                    },
1335                    PublicKey::ECDH { curve: c, q, .. } if ! for_signing => {
1336                        assert!(c == &curve);
1337                        assert!(c != &Curve::Cv25519);
1338                        assert!(uncompressed(q));
1339
1340                        use crate::crypto::SessionKey;
1341                        let sk = SessionKey::new(32).unwrap();
1342                        let ciphertext = key.encrypt(&sk)?;
1343                        if let Ciphertext::ECDH { e, .. } = &ciphertext {
1344                            assert!(uncompressed(e));
1345                        } else {
1346                            panic!("unexpected ciphertext: {:?}", ciphertext);
1347                        }
1348                    },
1349                    mpi => unreachable!(
1350                        "curve {}, mpi {:?}, for signing {:?}",
1351                        curve, mpi, for_signing),
1352                }
1353            }
1354        }
1355        Ok(())
1356    }
1357
1358
1359    #[test]
1360    fn v6_key_fingerprint() -> Result<()> {
1361        let p = Packet::from_bytes("-----BEGIN PGP ARMORED FILE-----
1362
1363xjcGY4d/4xYAAAAtCSsGAQQB2kcPAQEHQPlNp7tI1gph5WdwamWH0DMZmbudiRoI
1364JC6thFQ9+JWj
1365=SgmS
1366-----END PGP ARMORED FILE-----")?;
1367        let k: &Key<PublicParts, PrimaryRole> = p.downcast_ref().unwrap();
1368        assert_eq!(k.fingerprint().to_string(),
1369                   "4EADF309C6BC874AE04702451548F93F\
1370                    96FA7A01D0A33B5AF7D4E379E0F9F8EE".to_string());
1371        Ok(())
1372    }
1373
1374    #[test]
1375    fn import_public_mldsa() -> Result<()> {
1376        if PublicKeyAlgorithm::MLDSA65_Ed25519.is_supported() {
1377            let key: Key6<SecretParts, UnspecifiedRole>
1378                = Key6::generate_mldsa65_ed25519()
1379                .expect("failed to generate MLDSA65 key, but it is supported.");
1380
1381            assert_eq!(key.pk_algo(), PublicKeyAlgorithm::MLDSA65_Ed25519);
1382            let creation_time = key.creation_time();
1383            let mpis = key.mpis();
1384            let crate::crypto::mpi::PublicKey::MLDSA65_Ed25519 {
1385                eddsa,
1386                mldsa,
1387            } = &mpis else {
1388                panic!("Key generate generated the wrong key");
1389            };
1390
1391            let imported_key = Key6::import_public_mldsa65_ed25519(
1392                &mldsa[..], &eddsa[..], creation_time)
1393                .expect("Can import key");
1394
1395            assert_eq!(key.parts_into_public(), imported_key);
1396        }
1397
1398        if PublicKeyAlgorithm::MLDSA87_Ed448.is_supported() {
1399            let key: Key6<SecretParts, UnspecifiedRole>
1400                = Key6::generate_mldsa87_ed448()
1401                .expect("failed to generate MLDSA87 key, but it is supported.");
1402
1403            assert_eq!(key.pk_algo(), PublicKeyAlgorithm::MLDSA87_Ed448);
1404            let creation_time = key.creation_time();
1405            let mpis = key.mpis();
1406            let crate::crypto::mpi::PublicKey::MLDSA87_Ed448 {
1407                eddsa,
1408                mldsa,
1409            } = &mpis else {
1410                panic!("Key generate generated the wrong key");
1411            };
1412
1413            let imported_key = Key6::import_public_mldsa87_ed448(
1414                &mldsa[..], &eddsa[..], creation_time)
1415                .expect("Can import key");
1416
1417            assert_eq!(key.parts_into_public(), imported_key);
1418        }
1419
1420        Ok(())
1421    }
1422
1423
1424    #[test]
1425    fn import_public_slhdsa() -> Result<()> {
1426        if PublicKeyAlgorithm::SLHDSA128s.is_supported() {
1427            let key: Key6<SecretParts, UnspecifiedRole>
1428                = Key6::generate_slhdsa128s()
1429                .expect("failed to generate SLHDSA128s key, but it is supported.");
1430
1431            assert_eq!(key.pk_algo(), PublicKeyAlgorithm::SLHDSA128s);
1432            let creation_time = key.creation_time();
1433            let mpis = key.mpis();
1434            let crate::crypto::mpi::PublicKey::SLHDSA128s {
1435                public,
1436            } = &mpis else {
1437                panic!("Key generate generated the wrong key");
1438            };
1439
1440            let imported_key = Key6::import_public_slhdsa128s(
1441                &public[..], creation_time)
1442                .expect("Can import key");
1443
1444            assert_eq!(key.parts_into_public(), imported_key);
1445        }
1446
1447        if PublicKeyAlgorithm::SLHDSA128f.is_supported() {
1448            let key: Key6<SecretParts, UnspecifiedRole>
1449                = Key6::generate_slhdsa128f()
1450                .expect("failed to generate SLHDSA128f key, but it is supported.");
1451
1452            assert_eq!(key.pk_algo(), PublicKeyAlgorithm::SLHDSA128f);
1453            let creation_time = key.creation_time();
1454            let mpis = key.mpis();
1455            let crate::crypto::mpi::PublicKey::SLHDSA128f {
1456                public,
1457            } = &mpis else {
1458                panic!("Key generate generated the wrong key");
1459            };
1460
1461            let imported_key = Key6::import_public_slhdsa128f(
1462                &public[..], creation_time)
1463                .expect("Can import key");
1464
1465            assert_eq!(key.parts_into_public(), imported_key);
1466        }
1467
1468        if PublicKeyAlgorithm::SLHDSA256s.is_supported() {
1469            let key: Key6<SecretParts, UnspecifiedRole>
1470                = Key6::generate_slhdsa256s()
1471                .expect("failed to generate SLHDSA256s key, but it is supported.");
1472
1473            assert_eq!(key.pk_algo(), PublicKeyAlgorithm::SLHDSA256s);
1474            let creation_time = key.creation_time();
1475            let mpis = key.mpis();
1476            let crate::crypto::mpi::PublicKey::SLHDSA256s {
1477                public,
1478            } = &mpis else {
1479                panic!("Key generate generated the wrong key");
1480            };
1481
1482            let imported_key = Key6::import_public_slhdsa256s(
1483                &public[..], creation_time)
1484                .expect("Can import key");
1485
1486            assert_eq!(key.parts_into_public(), imported_key);
1487        }
1488
1489        Ok(())
1490    }
1491
1492    #[test]
1493    fn import_public_mlkem() -> Result<()> {
1494        if PublicKeyAlgorithm::MLKEM768_X25519.is_supported() {
1495            let key: Key6<SecretParts, UnspecifiedRole>
1496                = Key6::generate_mlkem768_x25519()
1497                .expect("failed to generate ML-KEM768 key, but it is supported.");
1498
1499            assert_eq!(key.pk_algo(), PublicKeyAlgorithm::MLKEM768_X25519);
1500            let creation_time = key.creation_time();
1501            let mpis = key.mpis();
1502            let crate::crypto::mpi::PublicKey::MLKEM768_X25519 {
1503                ecdh,
1504                mlkem,
1505            } = &mpis else {
1506                panic!("Key generate generated the wrong key");
1507            };
1508
1509            let imported_key = Key6::import_public_mlkem768_x25519(
1510                &mlkem[..], &ecdh[..], creation_time)
1511                .expect("Can import key");
1512
1513            assert_eq!(key.parts_into_public(), imported_key);
1514        }
1515
1516        if PublicKeyAlgorithm::MLKEM1024_X448.is_supported() {
1517            let key: Key6<SecretParts, UnspecifiedRole>
1518                = Key6::generate_mlkem1024_x448()
1519                .expect("failed to generate ML-KEM1024 key, but it is supported.");
1520
1521            assert_eq!(key.pk_algo(), PublicKeyAlgorithm::MLKEM1024_X448);
1522            let creation_time = key.creation_time();
1523            let mpis = key.mpis();
1524            let crate::crypto::mpi::PublicKey::MLKEM1024_X448 {
1525                ecdh,
1526                mlkem,
1527            } = &mpis else {
1528                panic!("Key generate generated the wrong key");
1529            };
1530
1531            let imported_key = Key6::import_public_mlkem1024_x448(
1532                &mlkem[..], &ecdh[..], creation_time)
1533                .expect("Can import key");
1534
1535            assert_eq!(key.parts_into_public(), imported_key);
1536        }
1537
1538        Ok(())
1539    }
1540}