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-SHAKE-128s key material.
503    ///
504    /// SLH-DSA-SHAKE-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    ///
509    /// Note: [RFC 9980 only specifies the SHAKE variant of the
510    /// SLH-DSA
511    /// algorithm](https://www.rfc-editor.org/rfc/rfc9980.html#name-supported-public-key-algori).
512    pub fn import_public_slhdsa128s<T>(public: &[u8], ctime: T)
513        -> Result<Self>
514    where
515        T: Into<Option<time::SystemTime>>
516    {
517        Ok(Key6 {
518            common: Key4::new(ctime.into().unwrap_or_else(crate::now),
519                              PublicKeyAlgorithm::SLHDSA128s,
520                              mpi::PublicKey::SLHDSA128s {
521                                  public: public.try_into()?,
522                              })?,
523        })
524    }
525
526    /// Creates an OpenPGP public key packet from existing
527    /// SLH-DSA-SHAKE-128f key material.
528    ///
529    /// SLH-DSA-SHAKE-128f keys must be exactly 32 bytes.
530    ///
531    /// The key will have its creation date set to `ctime` or the
532    /// current time if `None` is given.
533    ///
534    /// Note: [RFC 9980 only specifies the SHAKE variant of the
535    /// SLH-DSA
536    /// algorithm](https://www.rfc-editor.org/rfc/rfc9980.html#name-supported-public-key-algori).
537    pub fn import_public_slhdsa128f<T>(public: &[u8], ctime: T)
538        -> Result<Self>
539    where
540        T: Into<Option<time::SystemTime>>
541    {
542        Ok(Key6 {
543            common: Key4::new(ctime.into().unwrap_or_else(crate::now),
544                              PublicKeyAlgorithm::SLHDSA128f,
545                              mpi::PublicKey::SLHDSA128f {
546                                  public: public.try_into()?,
547                              })?,
548        })
549    }
550
551    /// Creates an OpenPGP public key packet from existing
552    /// SLH-DSA-SHAKE-256s key material.
553    ///
554    /// SLH-DSA-SHAKE-256s keys must be exactly 64 bytes.
555    ///
556    /// The key will have its creation date set to `ctime` or the
557    /// current time if `None` is given.
558    ///
559    /// Note: [RFC 9980 only specifies the SHAKE variant of the
560    /// SLH-DSA
561    /// algorithm](https://www.rfc-editor.org/rfc/rfc9980.html#name-supported-public-key-algori).
562    pub fn import_public_slhdsa256s<T>(public: &[u8], ctime: T)
563        -> Result<Self>
564    where
565        T: Into<Option<time::SystemTime>>
566    {
567        Ok(Key6 {
568            common: Key4::new(ctime.into().unwrap_or_else(crate::now),
569                              PublicKeyAlgorithm::SLHDSA256s,
570                              mpi::PublicKey::SLHDSA256s {
571                                  public: Box::new(public.try_into()?),
572                              })?,
573        })
574    }
575
576    /// Creates an OpenPGP public key packet from existing
577    /// ML-KEM768+X25519 key material.
578    ///
579    /// Note: in OpenPGP, ML-KEM keys are composite keys and include
580    /// an X25519 key to provide a pre-quantum security fallback.
581    ///
582    /// ML-KEM768 keys must be exactly 1184 bytes, and X25519 keys
583    /// must be exactly 32 bytes.
584    ///
585    /// The key will have its creation date set to `ctime` or the
586    /// current time if `None` is given.
587    pub fn import_public_mlkem768_x25519<T>(mlkem: &[u8], ecdh: &[u8], ctime: T)
588        -> Result<Self>
589    where
590        T: Into<Option<time::SystemTime>>
591    {
592        Ok(Key6 {
593            common: Key4::new(ctime.into().unwrap_or_else(crate::now),
594                              PublicKeyAlgorithm::MLKEM768_X25519,
595                              mpi::PublicKey::MLKEM768_X25519 {
596                                  ecdh: Box::new(ecdh.try_into()?),
597                                  mlkem: Box::new(mlkem.try_into()?),
598                              })?,
599        })
600    }
601
602    /// Creates an OpenPGP public key packet from existing
603    /// ML-KEM1024+X448 key material.
604    ///
605    /// Note: in OpenPGP, ML-KEM keys are composite keys and include
606    /// an X448 key to provide a pre-quantum security fallback.
607    ///
608    /// ML-KEM1024 keys must be exactly 1568 bytes, and X448 keys must
609    /// be exactly 56 bytes.
610    ///
611    /// The key will have its creation date set to `ctime` or the
612    /// current time if `None` is given.
613    pub fn import_public_mlkem1024_x448<T>(mlkem: &[u8], ecdh: &[u8], ctime: T)
614        -> Result<Self>
615    where
616        T: Into<Option<time::SystemTime>>
617    {
618        Ok(Key6 {
619            common: Key4::new(ctime.into().unwrap_or_else(crate::now),
620                              PublicKeyAlgorithm::MLKEM1024_X448,
621                              mpi::PublicKey::MLKEM1024_X448 {
622                                  ecdh: Box::new(ecdh.try_into()?),
623                                  mlkem: Box::new(mlkem.try_into()?),
624                              })?,
625        })
626    }
627}
628
629impl<R> Key6<SecretParts, R>
630where R: KeyRole,
631{
632    /// Creates an OpenPGP key packet from the specified secret key
633    /// material.
634    pub fn with_secret<T>(creation_time: T, pk_algo: PublicKeyAlgorithm,
635                          mpis: mpi::PublicKey,
636                          secret: SecretKeyMaterial)
637                          -> Result<Self>
638    where T: Into<time::SystemTime>
639    {
640        Ok(Key6 {
641            common: Key4::with_secret(creation_time, pk_algo, mpis, secret)?,
642        })
643    }
644
645    /// Creates a new OpenPGP secret key packet for an existing X25519
646    /// key.
647    ///
648    /// The given `private_key` is expected to be in the native X25519
649    /// representation, i.e. as opaque byte string of length 32.
650    ///
651    /// The key will have its creation date set to `ctime` or the
652    /// current time if `None` is given.
653    pub fn import_secret_x25519<T>(private_key: &[u8],
654                                   ctime: T)
655                                   -> Result<Self>
656    where
657        T: Into<Option<std::time::SystemTime>>,
658    {
659        use crate::crypto::backend::{Backend, interface::Asymmetric};
660
661        let private_key = Protected::from(private_key);
662        let public_key = Backend::x25519_derive_public(&private_key)?;
663
664        Self::with_secret(
665            ctime.into().unwrap_or_else(crate::now),
666            PublicKeyAlgorithm::X25519,
667            mpi::PublicKey::X25519 {
668                u: public_key,
669            },
670            mpi::SecretKeyMaterial::X25519 {
671                x: private_key.into(),
672            }.into())
673    }
674
675    /// Creates a new OpenPGP secret key packet for an existing X448
676    /// key.
677    ///
678    /// The given `private_key` is expected to be in the native X448
679    /// representation, i.e. as opaque byte string of length 32.
680    ///
681    /// The key will have its creation date set to `ctime` or the
682    /// current time if `None` is given.
683    pub fn import_secret_x448<T>(private_key: &[u8],
684                                 ctime: T)
685                                 -> Result<Self>
686    where
687        T: Into<Option<std::time::SystemTime>>,
688    {
689        use crate::crypto::backend::{Backend, interface::Asymmetric};
690
691        let private_key = Protected::from(private_key);
692        let public_key = Backend::x448_derive_public(&private_key)?;
693
694        Self::with_secret(
695            ctime.into().unwrap_or_else(crate::now),
696            PublicKeyAlgorithm::X448,
697            mpi::PublicKey::X448 {
698                u: Box::new(public_key),
699            },
700            mpi::SecretKeyMaterial::X448 {
701                x: private_key.into(),
702            }.into())
703    }
704
705    /// Creates a new OpenPGP secret key packet for an existing
706    /// Ed25519 key.
707    ///
708    /// The key will have its creation date set to `ctime` or the
709    /// current time if `None` is given.
710    pub fn import_secret_ed25519<T>(private_key: &[u8], ctime: T)
711                                    -> Result<Self>
712    where
713        T: Into<Option<time::SystemTime>>,
714    {
715        use crate::crypto::backend::{Backend, interface::Asymmetric};
716
717        let private_key = Protected::from(private_key);
718        let public_key = Backend::ed25519_derive_public(&private_key)?;
719
720        Self::with_secret(
721            ctime.into().unwrap_or_else(crate::now),
722            PublicKeyAlgorithm::Ed25519,
723            mpi::PublicKey::Ed25519 {
724                a: public_key,
725            },
726            mpi::SecretKeyMaterial::Ed25519 {
727                x: private_key.into(),
728            }.into())
729    }
730
731    /// Creates a new OpenPGP secret key packet for an existing
732    /// Ed448 key.
733    ///
734    /// The key will have its creation date set to `ctime` or the
735    /// current time if `None` is given.
736    pub fn import_secret_ed448<T>(private_key: &[u8], ctime: T)
737                                  -> Result<Self>
738    where
739        T: Into<Option<time::SystemTime>>,
740    {
741        use crate::crypto::backend::{Backend, interface::Asymmetric};
742
743        let private_key = Protected::from(private_key);
744        let public_key = Backend::ed448_derive_public(&private_key)?;
745
746        Self::with_secret(
747            ctime.into().unwrap_or_else(crate::now),
748            PublicKeyAlgorithm::Ed448,
749            mpi::PublicKey::Ed448 {
750                a: Box::new(public_key),
751            },
752            mpi::SecretKeyMaterial::Ed448 {
753                x: private_key.into(),
754            }.into())
755    }
756
757    /// Creates a new key pair from a secret `Key` with an unencrypted
758    /// secret key.
759    ///
760    /// # Errors
761    ///
762    /// Fails if the secret key is encrypted.  You can use
763    /// [`Key::decrypt_secret`] to decrypt a key.
764    pub fn into_keypair(self) -> Result<KeyPair> {
765        let (key, secret) = self.take_secret();
766        let secret = match secret {
767            SecretKeyMaterial::Unencrypted(secret) => secret,
768            SecretKeyMaterial::Encrypted(_) =>
769                return Err(Error::InvalidArgument(
770                    "secret key material is encrypted".into()).into()),
771        };
772
773        KeyPair::new(key.role_into_unspecified().into(), secret)
774    }
775}
776
777macro_rules! impl_common_secret_functions_v6 {
778    ($t: ident) => {
779        /// Secret key material handling.
780        impl<R> Key6<$t, R>
781        where R: KeyRole,
782        {
783            /// Takes the `Key`'s `SecretKeyMaterial`, if any.
784            pub fn take_secret(mut self)
785                               -> (Key6<PublicParts, R>, Option<SecretKeyMaterial>)
786            {
787                let old = std::mem::replace(&mut self.common.secret, None);
788                (self.parts_into_public(), old)
789            }
790
791            /// Adds the secret key material to the `Key`, returning
792            /// the old secret key material, if any.
793            pub fn add_secret(mut self, secret: SecretKeyMaterial)
794                              -> (Key6<SecretParts, R>, Option<SecretKeyMaterial>)
795            {
796                let old = std::mem::replace(&mut self.common.secret, Some(secret));
797                (self.parts_into_secret().expect("secret just set"), old)
798            }
799
800            /// Takes the `Key`'s `SecretKeyMaterial`, if any.
801            pub fn steal_secret(&mut self) -> Option<SecretKeyMaterial>
802            {
803                std::mem::replace(&mut self.common.secret, None)
804            }
805        }
806    }
807}
808impl_common_secret_functions_v6!(PublicParts);
809impl_common_secret_functions_v6!(UnspecifiedParts);
810
811/// Secret key handling.
812impl<R> Key6<SecretParts, R>
813where R: KeyRole,
814{
815    /// Gets the `Key`'s `SecretKeyMaterial`.
816    pub fn secret(&self) -> &SecretKeyMaterial {
817        self.common.secret()
818    }
819
820    /// Gets a mutable reference to the `Key`'s `SecretKeyMaterial`.
821    pub fn secret_mut(&mut self) -> &mut SecretKeyMaterial {
822        self.common.secret_mut()
823    }
824
825    /// Takes the `Key`'s `SecretKeyMaterial`.
826    pub fn take_secret(mut self)
827                       -> (Key6<PublicParts, R>, SecretKeyMaterial)
828    {
829        let old = std::mem::replace(&mut self.common.secret, None);
830        (self.parts_into_public(),
831         old.expect("Key<SecretParts, _> has a secret key material"))
832    }
833
834    /// Adds `SecretKeyMaterial` to the `Key`.
835    ///
836    /// This function returns the old secret key material, if any.
837    pub fn add_secret(mut self, secret: SecretKeyMaterial)
838                      -> (Key6<SecretParts, R>, SecretKeyMaterial)
839    {
840        let old = std::mem::replace(&mut self.common.secret, Some(secret));
841        (self.parts_into_secret().expect("secret just set"),
842         old.expect("Key<SecretParts, _> has a secret key material"))
843    }
844
845    /// Decrypts the secret key material using `password`.
846    ///
847    /// In OpenPGP, secret key material can be [protected with a
848    /// password].  The password is usually hardened using a [KDF].
849    ///
850    /// Refer to the documentation of [`Key::decrypt_secret`] for
851    /// details.
852    ///
853    /// This function returns an error if the secret key material is
854    /// not encrypted or the password is incorrect.
855    ///
856    /// [protected with a password]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.5.3
857    /// [KDF]: https://www.rfc-editor.org/rfc/rfc9580.html#section-3.7
858    /// [`Key::decrypt_secret`]: super::Key::decrypt_secret()
859    pub fn decrypt_secret(self, password: &Password) -> Result<Self> {
860        let (key, mut secret) = self.take_secret();
861        // Note: Key version is authenticated.
862        let key = Key::V6(key);
863        secret.decrypt_in_place(&key, password)?;
864        let key = if let Key::V6(k) = key { k } else { unreachable!() };
865        Ok(key.add_secret(secret).0)
866    }
867
868    /// Encrypts the secret key material using `password`.
869    ///
870    /// In OpenPGP, secret key material can be [protected with a
871    /// password].  The password is usually hardened using a [KDF].
872    ///
873    /// Refer to the documentation of [`Key::encrypt_secret`] for
874    /// details.
875    ///
876    /// This returns an error if the secret key material is already
877    /// encrypted.
878    ///
879    /// [protected with a password]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.5.3
880    /// [KDF]: https://www.rfc-editor.org/rfc/rfc9580.html#section-3.7
881    /// [`Key::encrypt_secret`]: super::Key::encrypt_secret()
882    pub fn encrypt_secret(self, password: &Password)
883                          -> Result<Key6<SecretParts, R>>
884    {
885        let (key, mut secret) = self.take_secret();
886        // Note: Key version is authenticated.
887        let key = Key::V6(key);
888        secret.encrypt_in_place(&key, password)?;
889        let key = if let Key::V6(k) = key { k } else { unreachable!() };
890        Ok(key.add_secret(secret).0)
891    }
892}
893
894impl<P, R> From<Key6<P, R>> for super::Key<P, R>
895where P: KeyParts,
896      R: KeyRole,
897{
898    fn from(p: Key6<P, R>) -> Self {
899        super::Key::V6(p)
900    }
901}
902
903#[cfg(test)]
904use crate::packet::key::{
905    PrimaryRole,
906    SubordinateRole,
907    UnspecifiedRole,
908};
909
910#[cfg(test)]
911impl Arbitrary for Key6<PublicParts, PrimaryRole> {
912    fn arbitrary(g: &mut Gen) -> Self {
913        Key6::from_common(Key4::arbitrary(g))
914    }
915}
916
917#[cfg(test)]
918impl Arbitrary for Key6<PublicParts, SubordinateRole> {
919    fn arbitrary(g: &mut Gen) -> Self {
920        Key6::from_common(Key4::arbitrary(g))
921    }
922}
923
924#[cfg(test)]
925impl Arbitrary for Key6<PublicParts, UnspecifiedRole> {
926    fn arbitrary(g: &mut Gen) -> Self {
927        Key6::from_common(Key4::arbitrary(g))
928    }
929}
930
931#[cfg(test)]
932impl Arbitrary for Key6<SecretParts, PrimaryRole> {
933    fn arbitrary(g: &mut Gen) -> Self {
934        Key6::from_common(Key4::arbitrary(g))
935    }
936}
937
938#[cfg(test)]
939impl Arbitrary for Key6<SecretParts, SubordinateRole> {
940    fn arbitrary(g: &mut Gen) -> Self {
941        Key6::from_common(Key4::arbitrary(g))
942    }
943}
944
945
946#[cfg(test)]
947mod tests {
948    use std::time::Duration;
949    use std::time::UNIX_EPOCH;
950
951    use crate::crypto::S2K;
952    use crate::packet::Key;
953    use crate::packet::key;
954    use crate::packet::Packet;
955    use super::*;
956    use crate::PacketPile;
957    use crate::serialize::Serialize;
958    use crate::types::*;
959    use crate::parse::Parse;
960
961    #[test]
962    fn primary_key_encrypt_decrypt() -> Result<()> {
963        key_encrypt_decrypt::<PrimaryRole>()
964    }
965
966    #[test]
967    fn subkey_encrypt_decrypt() -> Result<()> {
968        key_encrypt_decrypt::<SubordinateRole>()
969    }
970
971    fn key_encrypt_decrypt<R>() -> Result<()>
972    where
973        R: KeyRole + PartialEq,
974    {
975        let mut g = quickcheck::Gen::new(256);
976        let p: Password = Vec::<u8>::arbitrary(&mut g).into();
977
978        let check = |key: Key6<SecretParts, R>| -> Result<()> {
979            let key: Key<_, _> = key.into();
980            let encrypted = key.clone().encrypt_secret(&p)?;
981            let decrypted = encrypted.decrypt_secret(&p)?;
982            assert_eq!(key, decrypted);
983            Ok(())
984        };
985
986        use crate::types::Curve::*;
987        for curve in vec![NistP256, NistP384, NistP521, Ed25519] {
988            if ! curve.is_supported() {
989                eprintln!("Skipping unsupported {}", curve);
990                continue;
991            }
992
993            let key: Key6<_, R>
994                = Key6::generate_ecc(true, curve.clone())?;
995            check(key)?;
996        }
997
998        for bits in vec![2048, 3072] {
999            if ! PublicKeyAlgorithm::RSAEncryptSign.is_supported() {
1000                eprintln!("Skipping unsupported RSA");
1001                continue;
1002            }
1003
1004            let key: Key6<_, R>
1005                = Key6::generate_rsa(bits)?;
1006            check(key)?;
1007        }
1008
1009        Ok(())
1010    }
1011
1012    #[test]
1013    fn eq() {
1014        use crate::types::Curve::*;
1015
1016        for curve in vec![NistP256, NistP384, NistP521] {
1017            if ! curve.is_supported() {
1018                eprintln!("Skipping unsupported {}", curve);
1019                continue;
1020            }
1021
1022            let sign_key : Key6<_, key::UnspecifiedRole>
1023                = Key6::generate_ecc(true, curve.clone()).unwrap();
1024            let enc_key : Key6<_, key::UnspecifiedRole>
1025                = Key6::generate_ecc(false, curve).unwrap();
1026            let sign_clone = sign_key.clone();
1027            let enc_clone = enc_key.clone();
1028
1029            assert_eq!(sign_key, sign_clone);
1030            assert_eq!(enc_key, enc_clone);
1031        }
1032
1033        for bits in vec![1024, 2048, 3072, 4096] {
1034            if ! PublicKeyAlgorithm::RSAEncryptSign.is_supported() {
1035                eprintln!("Skipping unsupported RSA");
1036                continue;
1037            }
1038
1039            let key : Key6<_, key::UnspecifiedRole>
1040                = Key6::generate_rsa(bits).unwrap();
1041            let clone = key.clone();
1042            assert_eq!(key, clone);
1043        }
1044    }
1045
1046    #[test]
1047    fn generate_roundtrip() {
1048        use crate::types::Curve::*;
1049
1050        let keys = vec![NistP256, NistP384, NistP521].into_iter().flat_map(|cv|
1051        {
1052            if ! cv.is_supported() {
1053                eprintln!("Skipping unsupported {}", cv);
1054                return Vec::new();
1055            }
1056
1057            let sign_key : Key6<key::SecretParts, key::PrimaryRole>
1058                = Key6::generate_ecc(true, cv.clone()).unwrap();
1059            let enc_key = Key6::generate_ecc(false, cv).unwrap();
1060
1061            vec![sign_key, enc_key]
1062        }).chain(vec![1024, 2048, 3072, 4096].into_iter().filter_map(|b| {
1063            Key6::generate_rsa(b).ok()
1064        }));
1065
1066        for key in keys {
1067            let mut b = Vec::new();
1068            Packet::SecretKey(key.clone().into()).serialize(&mut b).unwrap();
1069
1070            let pp = PacketPile::from_bytes(&b).unwrap();
1071            if let Some(Packet::SecretKey(Key::V6(ref parsed_key))) =
1072                pp.path_ref(&[0])
1073            {
1074                assert_eq!(key.creation_time(), parsed_key.creation_time());
1075                assert_eq!(key.pk_algo(), parsed_key.pk_algo());
1076                assert_eq!(key.mpis(), parsed_key.mpis());
1077                assert_eq!(key.secret(), parsed_key.secret());
1078
1079                assert_eq!(&key, parsed_key);
1080            } else {
1081                panic!("bad packet: {:?}", pp.path_ref(&[0]));
1082            }
1083
1084            let mut b = Vec::new();
1085            let pk4 : Key6<PublicParts, PrimaryRole> = key.clone().into();
1086            Packet::PublicKey(pk4.into()).serialize(&mut b).unwrap();
1087
1088            let pp = PacketPile::from_bytes(&b).unwrap();
1089            if let Some(Packet::PublicKey(Key::V6(ref parsed_key))) =
1090                pp.path_ref(&[0])
1091            {
1092                assert!(! parsed_key.has_secret());
1093
1094                let key = key.take_secret().0;
1095                assert_eq!(&key, parsed_key);
1096            } else {
1097                panic!("bad packet: {:?}", pp.path_ref(&[0]));
1098            }
1099        }
1100    }
1101
1102    #[test]
1103    fn encryption_roundtrip() {
1104        use crate::crypto::SessionKey;
1105        use crate::types::Curve::*;
1106
1107        let keys = vec![NistP256, NistP384, NistP521].into_iter()
1108            .filter_map(|cv| {
1109                Key6::generate_ecc(false, cv).ok()
1110            }).chain(vec![1024, 2048, 3072, 4096].into_iter().filter_map(|b| {
1111                Key6::generate_rsa(b).ok()
1112            })).chain([
1113                (PublicKeyAlgorithm::MLKEM768_X25519, Key6::generate_mlkem768_x25519 as fn() -> Result<_>),
1114                (PublicKeyAlgorithm::MLKEM1024_X448, Key6::generate_mlkem1024_x448),
1115            ].into_iter().filter_map(|(algo, gen)| {
1116                if algo.is_supported() {
1117                    Some(gen().expect(&format!("{} is supported", algo)))
1118                } else {
1119                    None
1120                }
1121            }));
1122
1123        for key in keys.into_iter() {
1124            let key: Key<key::SecretParts, key::UnspecifiedRole> = key.into();
1125            let mut keypair = key.clone().into_keypair().unwrap();
1126            let cipher = SymmetricAlgorithm::AES256;
1127            let sk = SessionKey::new(cipher.key_size().unwrap()).unwrap();
1128
1129            let pkesk = PKESK6::for_recipient(&sk, &key).unwrap();
1130            let sk_ = pkesk.decrypt(&mut keypair, None)
1131                .expect("keypair should be able to decrypt PKESK");
1132            assert_eq!(sk, sk_);
1133
1134            let sk_ =
1135                pkesk.decrypt(&mut keypair, Some(cipher)).unwrap();
1136            assert_eq!(sk, sk_);
1137        }
1138    }
1139
1140    #[test]
1141    fn signature_roundtrip() {
1142        use crate::types::{Curve::*, SignatureType};
1143
1144        let keys = vec![NistP256, NistP384, NistP521].into_iter()
1145            .filter_map(|cv| {
1146                Key6::generate_ecc(true, cv).ok()
1147            }).chain(vec![1024, 2048, 3072, 4096].into_iter().filter_map(|b| {
1148                Key6::generate_rsa(b).ok()
1149            })).chain([
1150                (PublicKeyAlgorithm::MLDSA65_Ed25519, Key6::generate_mldsa65_ed25519 as fn() -> Result<_>),
1151                (PublicKeyAlgorithm::MLDSA87_Ed448, Key6::generate_mldsa87_ed448),
1152                (PublicKeyAlgorithm::SLHDSA128s, Key6::generate_slhdsa128s),
1153                (PublicKeyAlgorithm::SLHDSA128f, Key6::generate_slhdsa128f),
1154                (PublicKeyAlgorithm::SLHDSA256s, Key6::generate_slhdsa256s),
1155            ].into_iter().filter_map(|(algo, gen)| {
1156                if algo.is_supported() {
1157                    Some(gen().expect(&format!("{} is supported", algo)))
1158                } else {
1159                    None
1160                }
1161            }));
1162
1163        for key in keys.into_iter() {
1164            let key: Key<key::SecretParts, key::UnspecifiedRole> = key.into();
1165            let mut keypair = key.clone().into_keypair().unwrap();
1166            let hash = HashAlgorithm::default();
1167
1168            // Sign.
1169            let ctx = hash.context().unwrap().for_signature(key.version());
1170            let sig = SignatureBuilder::new(SignatureType::Binary)
1171                .sign_hash(&mut keypair, ctx).unwrap();
1172
1173            // Verify.
1174            let ctx = hash.context().unwrap().for_signature(key.version());
1175            sig.verify_hash(&key, ctx).unwrap();
1176        }
1177    }
1178
1179    #[test]
1180    fn secret_encryption_roundtrip() {
1181        use crate::types::Curve::*;
1182        use crate::types::SymmetricAlgorithm::*;
1183        use crate::types::AEADAlgorithm::*;
1184
1185        let keys = vec![NistP256, NistP384, NistP521].into_iter()
1186            .filter_map(|cv| -> Option<Key<key::SecretParts, key::PrimaryRole>> {
1187                Key6::generate_ecc(false, cv).map(Into::into).ok()
1188            }).chain(vec![1024, 2048, 3072, 4096].into_iter().filter_map(|b| {
1189                Key6::generate_rsa(b).map(Into::into).ok()
1190            }));
1191
1192        for key in keys {
1193          for (symm, aead) in [(AES128, None),
1194                               (AES128, Some(OCB)),
1195                               (AES256, Some(EAX))] {
1196            if ! aead.map(|a| a.is_supported()).unwrap_or(true) {
1197                continue;
1198            }
1199            assert!(! key.secret().is_encrypted());
1200
1201            let password = Password::from("foobarbaz");
1202            let mut encrypted_key = key.clone();
1203
1204            encrypted_key.secret_mut()
1205                .encrypt_in_place_with(&key, S2K::default(), symm, aead,
1206                                       &password).unwrap();
1207            assert!(encrypted_key.secret().is_encrypted());
1208
1209            encrypted_key.secret_mut()
1210                .decrypt_in_place(&key, &password).unwrap();
1211            assert!(! key.secret().is_encrypted());
1212            assert_eq!(key, encrypted_key);
1213            assert_eq!(key.secret(), encrypted_key.secret());
1214          }
1215        }
1216    }
1217
1218    #[test]
1219    fn encrypt_huge_plaintext() -> Result<()> {
1220        let sk = crate::crypto::SessionKey::new(256).unwrap();
1221
1222        if PublicKeyAlgorithm::RSAEncryptSign.is_supported() {
1223            let rsa2k: Key<SecretParts, UnspecifiedRole> =
1224                Key6::generate_rsa(2048)?.into();
1225            assert!(matches!(
1226                rsa2k.encrypt(&sk).unwrap_err().downcast().unwrap(),
1227                crate::Error::InvalidArgument(_)
1228            ));
1229        }
1230
1231        Ok(())
1232    }
1233
1234    #[test]
1235    fn issue_1016() {
1236        // The fingerprint is a function of the creation time,
1237        // algorithm, and public MPIs.  When we change them make sure
1238        // the fingerprint also changes.
1239
1240        let mut g = quickcheck::Gen::new(256);
1241
1242        let mut key = Key6::<PublicParts, UnspecifiedRole>::arbitrary(&mut g);
1243        let fpr1 = key.fingerprint();
1244        if key.creation_time() == UNIX_EPOCH {
1245            key.set_creation_time(UNIX_EPOCH + Duration::new(1, 0)).expect("ok");
1246        } else {
1247            key.set_creation_time(UNIX_EPOCH).expect("ok");
1248        }
1249        assert_ne!(fpr1, key.fingerprint());
1250
1251        let mut key = Key6::<PublicParts, UnspecifiedRole>::arbitrary(&mut g);
1252        let fpr1 = key.fingerprint();
1253        key.set_pk_algo(PublicKeyAlgorithm::from(u8::from(key.pk_algo()) + 1));
1254        assert_ne!(fpr1, key.fingerprint());
1255
1256        let mut key = Key6::<PublicParts, UnspecifiedRole>::arbitrary(&mut g);
1257        let fpr1 = key.fingerprint();
1258        loop {
1259            let mpis2 = mpi::PublicKey::arbitrary(&mut g);
1260            if key.mpis() != &mpis2 {
1261                *key.mpis_mut() = mpis2;
1262                break;
1263            }
1264        }
1265        assert_ne!(fpr1, key.fingerprint());
1266
1267        let mut key = Key6::<PublicParts, UnspecifiedRole>::arbitrary(&mut g);
1268        let fpr1 = key.fingerprint();
1269        loop {
1270            let mpis2 = mpi::PublicKey::arbitrary(&mut g);
1271            if key.mpis() != &mpis2 {
1272                key.set_mpis(mpis2);
1273                break;
1274            }
1275        }
1276        assert_ne!(fpr1, key.fingerprint());
1277    }
1278
1279    /// Smoke test for ECC key creation, signing and verification, and
1280    /// encryption and decryption.
1281    #[test]
1282    fn ecc_support() -> Result<()> {
1283        for for_signing in [true, false] {
1284            for curve in Curve::variants()
1285                .filter(Curve::is_supported)
1286            {
1287                match curve {
1288                    Curve::Cv25519 if for_signing => continue,
1289                    Curve::Ed25519 if ! for_signing => continue,
1290                    _ => (),
1291                }
1292
1293                eprintln!("curve {}, for signing {:?}", curve, for_signing);
1294                let key: Key<SecretParts, UnspecifiedRole> =
1295                    Key6::generate_ecc(for_signing, curve.clone())?.into();
1296                let mut pair = key.into_keypair()?;
1297
1298                if for_signing {
1299                    use crate::crypto::Signer;
1300                    let hash = HashAlgorithm::default();
1301                    let digest = hash.context()?
1302                        .for_signature(pair.public().version())
1303                        .into_digest()?;
1304                    let sig = pair.sign(hash, &digest)?;
1305                    pair.public().verify(&sig, hash, &digest)?;
1306                } else {
1307                    use crate::crypto::{SessionKey, Decryptor};
1308                    let sk = SessionKey::new(32).unwrap();
1309                    let ciphertext = pair.public().encrypt(&sk)?;
1310                    assert_eq!(pair.decrypt(&ciphertext, Some(sk.len()))?, sk);
1311                }
1312            }
1313        }
1314        Ok(())
1315    }
1316
1317    #[test]
1318    fn ecc_encoding() -> Result<()> {
1319        for for_signing in [true, false] {
1320            for curve in Curve::variants()
1321                .filter(Curve::is_supported)
1322            {
1323                match curve {
1324                    Curve::Cv25519 if for_signing => continue,
1325                    Curve::Ed25519 if ! for_signing => continue,
1326                    _ => (),
1327                }
1328
1329                use crate::crypto::mpi::{Ciphertext, MPI, PublicKey};
1330                eprintln!("curve {}, for signing {:?}", curve, for_signing);
1331
1332                let key: Key<SecretParts, UnspecifiedRole> =
1333                    Key6::generate_ecc(for_signing, curve.clone())?.into();
1334
1335                let uncompressed = |mpi: &MPI| mpi.value()[0] == 0x04;
1336
1337                match key.mpis() {
1338                    PublicKey::X25519 { .. } if ! for_signing => (),
1339                    PublicKey::X448 { .. } if ! for_signing => (),
1340                    PublicKey::Ed25519 { .. } if for_signing => (),
1341                    PublicKey::Ed448 { .. } if for_signing => (),
1342                    PublicKey::ECDSA { curve: c, q } if for_signing => {
1343                        assert!(c == &curve);
1344                        assert!(c != &Curve::Ed25519);
1345                        assert!(uncompressed(q));
1346                    },
1347                    PublicKey::ECDH { curve: c, q, .. } if ! for_signing => {
1348                        assert!(c == &curve);
1349                        assert!(c != &Curve::Cv25519);
1350                        assert!(uncompressed(q));
1351
1352                        use crate::crypto::SessionKey;
1353                        let sk = SessionKey::new(32).unwrap();
1354                        let ciphertext = key.encrypt(&sk)?;
1355                        if let Ciphertext::ECDH { e, .. } = &ciphertext {
1356                            assert!(uncompressed(e));
1357                        } else {
1358                            panic!("unexpected ciphertext: {:?}", ciphertext);
1359                        }
1360                    },
1361                    mpi => unreachable!(
1362                        "curve {}, mpi {:?}, for signing {:?}",
1363                        curve, mpi, for_signing),
1364                }
1365            }
1366        }
1367        Ok(())
1368    }
1369
1370
1371    #[test]
1372    fn v6_key_fingerprint() -> Result<()> {
1373        let p = Packet::from_bytes("-----BEGIN PGP ARMORED FILE-----
1374
1375xjcGY4d/4xYAAAAtCSsGAQQB2kcPAQEHQPlNp7tI1gph5WdwamWH0DMZmbudiRoI
1376JC6thFQ9+JWj
1377=SgmS
1378-----END PGP ARMORED FILE-----")?;
1379        let k: &Key<PublicParts, PrimaryRole> = p.downcast_ref().unwrap();
1380        assert_eq!(k.fingerprint().to_string(),
1381                   "4EADF309C6BC874AE04702451548F93F\
1382                    96FA7A01D0A33B5AF7D4E379E0F9F8EE".to_string());
1383        Ok(())
1384    }
1385
1386    #[test]
1387    fn import_public_mldsa() -> Result<()> {
1388        if PublicKeyAlgorithm::MLDSA65_Ed25519.is_supported() {
1389            let key: Key6<SecretParts, UnspecifiedRole>
1390                = Key6::generate_mldsa65_ed25519()
1391                .expect("failed to generate MLDSA65 key, but it is supported.");
1392
1393            assert_eq!(key.pk_algo(), PublicKeyAlgorithm::MLDSA65_Ed25519);
1394            let creation_time = key.creation_time();
1395            let mpis = key.mpis();
1396            let crate::crypto::mpi::PublicKey::MLDSA65_Ed25519 {
1397                eddsa,
1398                mldsa,
1399            } = &mpis else {
1400                panic!("Key generate generated the wrong key");
1401            };
1402
1403            let imported_key = Key6::import_public_mldsa65_ed25519(
1404                &mldsa[..], &eddsa[..], creation_time)
1405                .expect("Can import key");
1406
1407            assert_eq!(key.parts_into_public(), imported_key);
1408        }
1409
1410        if PublicKeyAlgorithm::MLDSA87_Ed448.is_supported() {
1411            let key: Key6<SecretParts, UnspecifiedRole>
1412                = Key6::generate_mldsa87_ed448()
1413                .expect("failed to generate MLDSA87 key, but it is supported.");
1414
1415            assert_eq!(key.pk_algo(), PublicKeyAlgorithm::MLDSA87_Ed448);
1416            let creation_time = key.creation_time();
1417            let mpis = key.mpis();
1418            let crate::crypto::mpi::PublicKey::MLDSA87_Ed448 {
1419                eddsa,
1420                mldsa,
1421            } = &mpis else {
1422                panic!("Key generate generated the wrong key");
1423            };
1424
1425            let imported_key = Key6::import_public_mldsa87_ed448(
1426                &mldsa[..], &eddsa[..], creation_time)
1427                .expect("Can import key");
1428
1429            assert_eq!(key.parts_into_public(), imported_key);
1430        }
1431
1432        Ok(())
1433    }
1434
1435
1436    #[test]
1437    fn import_public_slhdsa() -> Result<()> {
1438        if PublicKeyAlgorithm::SLHDSA128s.is_supported() {
1439            let key: Key6<SecretParts, UnspecifiedRole>
1440                = Key6::generate_slhdsa128s()
1441                .expect("failed to generate SLHDSA128s key, but it is supported.");
1442
1443            assert_eq!(key.pk_algo(), PublicKeyAlgorithm::SLHDSA128s);
1444            let creation_time = key.creation_time();
1445            let mpis = key.mpis();
1446            let crate::crypto::mpi::PublicKey::SLHDSA128s {
1447                public,
1448            } = &mpis else {
1449                panic!("Key generate generated the wrong key");
1450            };
1451
1452            let imported_key = Key6::import_public_slhdsa128s(
1453                &public[..], creation_time)
1454                .expect("Can import key");
1455
1456            assert_eq!(key.parts_into_public(), imported_key);
1457        }
1458
1459        if PublicKeyAlgorithm::SLHDSA128f.is_supported() {
1460            let key: Key6<SecretParts, UnspecifiedRole>
1461                = Key6::generate_slhdsa128f()
1462                .expect("failed to generate SLHDSA128f key, but it is supported.");
1463
1464            assert_eq!(key.pk_algo(), PublicKeyAlgorithm::SLHDSA128f);
1465            let creation_time = key.creation_time();
1466            let mpis = key.mpis();
1467            let crate::crypto::mpi::PublicKey::SLHDSA128f {
1468                public,
1469            } = &mpis else {
1470                panic!("Key generate generated the wrong key");
1471            };
1472
1473            let imported_key = Key6::import_public_slhdsa128f(
1474                &public[..], creation_time)
1475                .expect("Can import key");
1476
1477            assert_eq!(key.parts_into_public(), imported_key);
1478        }
1479
1480        if PublicKeyAlgorithm::SLHDSA256s.is_supported() {
1481            let key: Key6<SecretParts, UnspecifiedRole>
1482                = Key6::generate_slhdsa256s()
1483                .expect("failed to generate SLHDSA256s key, but it is supported.");
1484
1485            assert_eq!(key.pk_algo(), PublicKeyAlgorithm::SLHDSA256s);
1486            let creation_time = key.creation_time();
1487            let mpis = key.mpis();
1488            let crate::crypto::mpi::PublicKey::SLHDSA256s {
1489                public,
1490            } = &mpis else {
1491                panic!("Key generate generated the wrong key");
1492            };
1493
1494            let imported_key = Key6::import_public_slhdsa256s(
1495                &public[..], creation_time)
1496                .expect("Can import key");
1497
1498            assert_eq!(key.parts_into_public(), imported_key);
1499        }
1500
1501        Ok(())
1502    }
1503
1504    #[test]
1505    fn import_public_mlkem() -> Result<()> {
1506        if PublicKeyAlgorithm::MLKEM768_X25519.is_supported() {
1507            let key: Key6<SecretParts, UnspecifiedRole>
1508                = Key6::generate_mlkem768_x25519()
1509                .expect("failed to generate ML-KEM768 key, but it is supported.");
1510
1511            assert_eq!(key.pk_algo(), PublicKeyAlgorithm::MLKEM768_X25519);
1512            let creation_time = key.creation_time();
1513            let mpis = key.mpis();
1514            let crate::crypto::mpi::PublicKey::MLKEM768_X25519 {
1515                ecdh,
1516                mlkem,
1517            } = &mpis else {
1518                panic!("Key generate generated the wrong key");
1519            };
1520
1521            let imported_key = Key6::import_public_mlkem768_x25519(
1522                &mlkem[..], &ecdh[..], creation_time)
1523                .expect("Can import key");
1524
1525            assert_eq!(key.parts_into_public(), imported_key);
1526        }
1527
1528        if PublicKeyAlgorithm::MLKEM1024_X448.is_supported() {
1529            let key: Key6<SecretParts, UnspecifiedRole>
1530                = Key6::generate_mlkem1024_x448()
1531                .expect("failed to generate ML-KEM1024 key, but it is supported.");
1532
1533            assert_eq!(key.pk_algo(), PublicKeyAlgorithm::MLKEM1024_X448);
1534            let creation_time = key.creation_time();
1535            let mpis = key.mpis();
1536            let crate::crypto::mpi::PublicKey::MLKEM1024_X448 {
1537                ecdh,
1538                mlkem,
1539            } = &mpis else {
1540                panic!("Key generate generated the wrong key");
1541            };
1542
1543            let imported_key = Key6::import_public_mlkem1024_x448(
1544                &mlkem[..], &ecdh[..], creation_time)
1545                .expect("Can import key");
1546
1547            assert_eq!(key.parts_into_public(), imported_key);
1548        }
1549
1550        Ok(())
1551    }
1552}