Skip to main content

sequoia_openpgp/crypto/
mpi.rs

1//! Multiprecision Integers.
2//!
3//! Cryptographic objects like [public keys], [secret keys],
4//! [ciphertexts], and [signatures] are scalar numbers of arbitrary
5//! precision.  OpenPGP specifies that these are stored encoded as
6//! big-endian integers with leading zeros stripped (See [Section 3.2
7//! of RFC 9580]).  Multiprecision integers in OpenPGP are extended by
8//! [Section 3.2.1 of RFC 9580] to store curves and coordinates used
9//! in elliptic curve cryptography (ECC).
10//!
11//!   [public keys]: PublicKey
12//!   [secret keys]: SecretKeyMaterial
13//!   [ciphertexts]: Ciphertext
14//!   [signatures]: Signature
15//!   [Section 3.2 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-3.2
16//!   [Section 3.2.1 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-3.2.1
17use std::fmt;
18use std::cmp::Ordering;
19use std::io::Write;
20use std::borrow::Cow;
21
22#[cfg(test)]
23use quickcheck::{Arbitrary, Gen};
24
25use crate::fmt::hex;
26use crate::types::{
27    Curve,
28    HashAlgorithm,
29    PublicKeyAlgorithm,
30    SymmetricAlgorithm,
31};
32use crate::crypto::hash::{self, Hash};
33use crate::crypto::mem::{secure_cmp, Protected};
34use crate::serialize::Marshal;
35
36use crate::Error;
37use crate::Result;
38
39/// A Multiprecision Integer.
40#[derive(Clone)]
41pub struct MPI {
42    /// Integer value as big-endian with leading zeros stripped.
43    value: Box<[u8]>,
44}
45assert_send_and_sync!(MPI);
46
47impl From<Vec<u8>> for MPI {
48    fn from(v: Vec<u8>) -> Self {
49        // XXX: This will leak secrets in v into the heap.  But,
50        // eagerly clearing the memory may have a very high overhead,
51        // after all, most MPIs that we encounter will not contain
52        // secrets.  I think it is better to avoid creating MPIs that
53        // contain secrets in the first place.  In 2.0, we can remove
54        // the impl From<MPI> for ProtectedMPI.
55        Self::new(&v)
56    }
57}
58
59impl From<Box<[u8]>> for MPI {
60    fn from(v: Box<[u8]>) -> Self {
61        // XXX: This will leak secrets in v into the heap.  But,
62        // eagerly clearing the memory may have a very high overhead,
63        // after all, most MPIs that we encounter will not contain
64        // secrets.  I think it is better to avoid creating MPIs that
65        // contain secrets in the first place.  In 2.0, we can remove
66        // the impl From<MPI> for ProtectedMPI.
67        Self::new(&v)
68    }
69}
70
71impl MPI {
72    /// Trims leading zero octets.
73    fn trim_leading_zeros(v: &[u8]) -> &[u8] {
74        let offset = v.iter().take_while(|&&o| o == 0).count();
75        &v[offset..]
76    }
77
78    /// Creates a new MPI.
79    ///
80    /// This function takes care of removing leading zeros.
81    pub fn new(value: &[u8]) -> Self {
82        let value = Self::trim_leading_zeros(value).to_vec().into_boxed_slice();
83
84        MPI {
85            value,
86        }
87    }
88
89    /// Creates new MPI encoding an uncompressed EC point.
90    ///
91    /// Encodes the given point on an elliptic curve (see [Section 6 of
92    /// RFC 6637] for details).  This is used to encode public keys
93    /// and ciphertexts for the NIST curves (`NistP256`, `NistP384`,
94    /// and `NistP521`).
95    ///
96    ///   [Section 6 of RFC 6637]: https://tools.ietf.org/html/rfc6637#section-6
97    pub fn new_point(x: &[u8], y: &[u8], field_bits: usize) -> Self {
98        Self::new_point_common(x, y, field_bits).into()
99    }
100
101    /// Common implementation shared between MPI and ProtectedMPI.
102    fn new_point_common(x: &[u8], y: &[u8], field_bits: usize) -> Vec<u8> {
103        let field_sz = if field_bits % 8 > 0 { 1 } else { 0 } + field_bits / 8;
104        let mut val = vec![0x0u8; 1 + 2 * field_sz];
105        let x_missing = field_sz - x.len();
106        let y_missing = field_sz - y.len();
107
108        val[0] = 0x4;
109        val[1 + x_missing..1 + field_sz].copy_from_slice(x);
110        val[1 + field_sz + y_missing..].copy_from_slice(y);
111        val
112    }
113
114    /// Creates new MPI encoding a compressed EC point using native
115    /// encoding.
116    ///
117    /// Encodes the given point on an elliptic curve (see [Section 13.2
118    /// of RFC4880bis] for details).  This is used to encode public
119    /// keys and ciphertexts for the Bernstein curves (currently
120    /// `X25519`).
121    ///
122    ///   [Section 13.2 of RFC4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-09#section-13.2
123    pub fn new_compressed_point(x: &[u8]) -> Self {
124        Self::new_compressed_point_common(x).into()
125    }
126
127    /// Common implementation shared between MPI and ProtectedMPI.
128    fn new_compressed_point_common(x: &[u8]) -> Vec<u8> {
129        let mut val = vec![0; 1 + x.len()];
130        val[0] = 0x40;
131        val[1..].copy_from_slice(x);
132        val
133    }
134
135    /// Creates a new MPI representing zero.
136    pub fn zero() -> Self {
137        Self::new(&[])
138    }
139
140    /// Tests whether the MPI represents zero.
141    pub fn is_zero(&self) -> bool {
142        self.value().is_empty()
143    }
144
145    /// Returns the length of the MPI in bits.
146    ///
147    /// Leading zero-bits are not included in the returned size.
148    pub fn bits(&self) -> usize {
149        self.value.len() * 8
150            - self.value.get(0).map(|&b| b.leading_zeros() as usize)
151                  .unwrap_or(0)
152    }
153
154    /// Returns the value of this MPI.
155    ///
156    /// Note that due to stripping of zero-bytes, the returned value
157    /// may be shorter than expected.
158    pub fn value(&self) -> &[u8] {
159        &self.value
160    }
161
162    /// Returns the value of this MPI zero-padded to the given length.
163    ///
164    /// MPI-encoding strips leading zero-bytes.  This function adds
165    /// them back, if necessary.  If the size exceeds `to`, an error
166    /// is returned.
167    pub fn value_padded(&self, to: usize) -> Result<Cow<'_, [u8]>> {
168        crate::crypto::pad(self.value(), to)
169    }
170
171    /// Decodes an EC point encoded as MPI.
172    ///
173    /// Decodes the MPI into a point on an elliptic curve (see
174    /// [Section 6 of RFC 6637] and [Section 13.2 of RFC4880bis] for
175    /// details).  If the point is not compressed, the function
176    /// returns `(x, y)`.  If it is compressed, `y` will be empty.
177    ///
178    ///   [Section 6 of RFC 6637]: https://tools.ietf.org/html/rfc6637#section-6
179    ///   [Section 13.2 of RFC4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-09#section-13.2
180    ///
181    /// # Errors
182    ///
183    /// Returns `Error::UnsupportedEllipticCurve` if the curve is not
184    /// supported, `Error::MalformedMPI` if the point is formatted
185    /// incorrectly, `Error::InvalidOperation` if the given curve is
186    /// operating on native octet strings.
187    pub fn decode_point(&self, curve: &Curve) -> Result<(&[u8], &[u8])> {
188        Self::decode_point_common(self.value(), curve)
189    }
190
191    /// Common implementation shared between MPI and ProtectedMPI.
192    fn decode_point_common<'a>(value: &'a [u8], curve: &Curve)
193                               -> Result<(&'a [u8], &'a [u8])> {
194        const ED25519_KEY_SIZE: usize = 32;
195        const CURVE25519_SIZE: usize = 32;
196        use self::Curve::*;
197        match &curve {
198            Ed25519 | Cv25519 => {
199                assert_eq!(CURVE25519_SIZE, ED25519_KEY_SIZE);
200                // This curve uses a custom compression format which
201                // only contains the X coordinate.
202                if value.len() != 1 + CURVE25519_SIZE {
203                    return Err(Error::MalformedMPI(
204                        format!("Bad size of Curve25519 key: {} expected: {}",
205                                value.len(),
206                                1 + CURVE25519_SIZE
207                        )
208                    ).into());
209                }
210
211                if value.get(0).map(|&b| b != 0x40).unwrap_or(true) {
212                    return Err(Error::MalformedMPI(
213                        "Bad encoding of Curve25519 key".into()).into());
214                }
215
216                Ok((&value[1..], &[]))
217            },
218
219            NistP256
220                | NistP384
221                | NistP521
222                | BrainpoolP256
223                | BrainpoolP384
224                | BrainpoolP512
225                =>
226            {
227                // Length of one coordinate in bytes, rounded up.
228                let coordinate_length = curve.field_size()?;
229
230                // Check length of Q.
231                let expected_length =
232                    1 // 0x04.
233                    + (2 // (x, y)
234                       * coordinate_length);
235
236                if value.len() != expected_length {
237                    return Err(Error::MalformedMPI(
238                        format!("Invalid length of MPI: {} (expected {})",
239                                value.len(), expected_length)).into());
240                }
241
242                if value.get(0).map(|&b| b != 0x04).unwrap_or(true) {
243                    return Err(Error::MalformedMPI(
244                        format!("Bad prefix: {:?} (expected Some(0x04))",
245                                value.get(0))).into());
246                }
247
248                Ok((&value[1..1 + coordinate_length],
249                    &value[1 + coordinate_length..]))
250            },
251
252            Unknown(_) =>
253                Err(Error::UnsupportedEllipticCurve(curve.clone()).into()),
254        }
255    }
256
257    /// Securely compares two MPIs in constant time.
258    fn secure_memcmp(&self, other: &Self) -> Ordering {
259        let cmp = unsafe {
260            if self.value.len() == other.value.len() {
261                ::memsec::memcmp(self.value.as_ptr(), other.value.as_ptr(),
262                                 other.value.len())
263            } else {
264                self.value.len() as i32 - other.value.len() as i32
265            }
266        };
267
268        match cmp {
269            0 => Ordering::Equal,
270            x if x < 0 => Ordering::Less,
271            _ => Ordering::Greater,
272        }
273    }
274}
275
276impl fmt::Debug for MPI {
277    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
278        f.write_fmt(format_args!(
279            "{} bits: {}", self.bits(),
280            crate::fmt::to_hex(&*self.value, true)))
281    }
282}
283
284impl Hash for MPI {
285    fn hash(&self, hash: &mut hash::Context) -> Result<()> {
286        let len = self.bits() as u16;
287
288        hash.update(&len.to_be_bytes());
289        hash.update(&self.value);
290        Ok(())
291    }
292}
293
294#[cfg(test)]
295impl Arbitrary for MPI {
296    fn arbitrary(g: &mut Gen) -> Self {
297        loop {
298            let buf = <Vec<u8>>::arbitrary(g);
299
300            if !buf.is_empty() && buf[0] != 0 {
301                break MPI::new(&buf);
302            }
303        }
304    }
305}
306
307impl PartialOrd for MPI {
308    fn partial_cmp(&self, other: &MPI) -> Option<Ordering> {
309        Some(self.cmp(other))
310    }
311}
312
313impl Ord for MPI {
314    fn cmp(&self, other: &MPI) -> Ordering {
315        self.secure_memcmp(other)
316    }
317}
318
319impl PartialEq for MPI {
320    fn eq(&self, other: &MPI) -> bool {
321        self.cmp(other) == Ordering::Equal
322    }
323}
324
325impl Eq for MPI {}
326
327impl std::hash::Hash for MPI {
328    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
329        self.value.hash(state);
330    }
331}
332
333/// Holds a single MPI containing secrets.
334///
335/// The memory will be cleared when the object is dropped.  Used by
336/// [`SecretKeyMaterial`] to protect secret keys.
337///
338#[derive(Clone)]
339pub struct ProtectedMPI {
340    /// Integer value as big-endian.
341    value: Protected,
342}
343assert_send_and_sync!(ProtectedMPI);
344
345impl From<&[u8]> for ProtectedMPI {
346    fn from(m: &[u8]) -> Self {
347        let value = Protected::from(MPI::trim_leading_zeros(m));
348        ProtectedMPI {
349            value,
350        }
351    }
352}
353
354impl From<Vec<u8>> for ProtectedMPI {
355    fn from(m: Vec<u8>) -> Self {
356        let value = Protected::from(MPI::trim_leading_zeros(&m));
357        drop(Protected::from(m)); // Erase source.
358        ProtectedMPI {
359            value,
360        }
361    }
362}
363
364impl From<Box<[u8]>> for ProtectedMPI {
365    fn from(m: Box<[u8]>) -> Self {
366        let value = Protected::from(MPI::trim_leading_zeros(&m));
367        drop(Protected::from(m)); // Erase source.
368        ProtectedMPI {
369            value,
370        }
371    }
372}
373
374impl From<Protected> for ProtectedMPI {
375    fn from(m: Protected) -> Self {
376        let value = Protected::from(MPI::trim_leading_zeros(&m));
377        drop(m); // Erase source.
378        ProtectedMPI {
379            value,
380        }
381    }
382}
383
384impl PartialOrd for ProtectedMPI {
385    fn partial_cmp(&self, other: &ProtectedMPI) -> Option<Ordering> {
386        Some(self.cmp(other))
387    }
388}
389
390impl Ord for ProtectedMPI {
391    fn cmp(&self, other: &ProtectedMPI) -> Ordering {
392        self.secure_memcmp(other)
393    }
394}
395
396impl PartialEq for ProtectedMPI {
397    fn eq(&self, other: &ProtectedMPI) -> bool {
398        self.cmp(other) == Ordering::Equal
399    }
400}
401
402impl Eq for ProtectedMPI {}
403
404impl std::hash::Hash for ProtectedMPI {
405    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
406        self.value.hash(state);
407    }
408}
409
410#[cfg(test)]
411impl Arbitrary for ProtectedMPI {
412    fn arbitrary(g: &mut Gen) -> Self {
413        loop {
414            let buf = <Vec<u8>>::arbitrary(g);
415
416            if ! buf.is_empty() && buf[0] != 0 {
417                break ProtectedMPI::from(buf);
418            }
419        }
420    }
421}
422
423impl ProtectedMPI {
424    /// Creates new MPI encoding an uncompressed EC point.
425    ///
426    /// Encodes the given point on an elliptic curve (see [Section 6 of
427    /// RFC 6637] for details).  This is used to encode public keys
428    /// and ciphertexts for the NIST curves (`NistP256`, `NistP384`,
429    /// and `NistP521`).
430    ///
431    ///   [Section 6 of RFC 6637]: https://tools.ietf.org/html/rfc6637#section-6
432    pub fn new_point(x: &[u8], y: &[u8], field_bits: usize) -> Self {
433        MPI::new_point_common(x, y, field_bits).into()
434    }
435
436    /// Creates new MPI encoding a compressed EC point using native
437    /// encoding.
438    ///
439    /// Encodes the given point on an elliptic curve (see [Section 13.2
440    /// of RFC4880bis] for details).  This is used to encode public
441    /// keys and ciphertexts for the Bernstein curves (currently
442    /// `X25519`).
443    ///
444    ///   [Section 13.2 of RFC4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-09#section-13.2
445    pub fn new_compressed_point(x: &[u8]) -> Self {
446        MPI::new_compressed_point_common(x).into()
447    }
448
449    /// Returns the length of the MPI in bits.
450    ///
451    /// Leading zero-bits are not included in the returned size.
452    pub fn bits(&self) -> usize {
453        self.value.len() * 8
454            - self.value.get(0).map(|&b| b.leading_zeros() as usize)
455                  .unwrap_or(0)
456    }
457
458    /// Returns the value of this MPI.
459    ///
460    /// Note that due to stripping of zero-bytes, the returned value
461    /// may be shorter than expected.
462    pub fn value(&self) -> &[u8] {
463        &self.value
464    }
465
466    /// Returns the value of this MPI zero-padded to the given length.
467    ///
468    /// MPI-encoding strips leading zero-bytes.  This function adds
469    /// them back.  This operation is done unconditionally to avoid
470    /// timing differences.  If the size exceeds `to`, the result is
471    /// silently truncated to avoid timing differences.
472    pub fn value_padded(&self, to: usize) -> Protected {
473        let missing = to.saturating_sub(self.value.len());
474        let limit = self.value.len().min(to);
475        let mut v: Protected = vec![0; to].into();
476        v[missing..].copy_from_slice(&self.value()[..limit]);
477        v
478    }
479
480    /// Decodes an EC point encoded as MPI.
481    ///
482    /// Decodes the MPI into a point on an elliptic curve (see
483    /// [Section 6 of RFC 6637] and [Section 13.2 of RFC4880bis] for
484    /// details).  If the point is not compressed, the function
485    /// returns `(x, y)`.  If it is compressed, `y` will be empty.
486    ///
487    ///   [Section 6 of RFC 6637]: https://tools.ietf.org/html/rfc6637#section-6
488    ///   [Section 13.2 of RFC4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-09#section-13.2
489    ///
490    /// # Errors
491    ///
492    /// Returns `Error::UnsupportedEllipticCurve` if the curve is not
493    /// supported, `Error::MalformedMPI` if the point is formatted
494    /// incorrectly, `Error::InvalidOperation` if the given curve is
495    /// operating on native octet strings.
496    pub fn decode_point(&self, curve: &Curve) -> Result<(&[u8], &[u8])> {
497        MPI::decode_point_common(self.value(), curve)
498    }
499
500    /// Securely compares two MPIs in constant time.
501    fn secure_memcmp(&self, other: &Self) -> Ordering {
502        (self.value.len() as i32).cmp(&(other.value.len() as i32))
503            .then(
504                // Protected compares in constant time.
505                self.value.cmp(&other.value))
506    }
507}
508
509impl fmt::Debug for ProtectedMPI {
510    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
511        if cfg!(debug_assertions) {
512            f.write_fmt(format_args!(
513                "{} bits: {}", self.bits(),
514                crate::fmt::to_hex(&*self.value, true)))
515        } else {
516            f.write_str("<Redacted>")
517        }
518    }
519}
520
521/// A public key.
522///
523/// Provides a typed and structured way of storing multiple MPIs (and
524/// the occasional elliptic curve) in [`Key`] packets.
525///
526///   [`Key`]: crate::packet::Key
527#[non_exhaustive]
528#[allow(non_camel_case_types)]
529#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
530pub enum PublicKey {
531    /// RSA public key.
532    RSA {
533        /// Public exponent
534        e: MPI,
535        /// Public modulo N = pq.
536        n: MPI,
537    },
538
539    /// NIST DSA public key.
540    DSA {
541        /// Prime of the ring Zp.
542        p: MPI,
543        /// Order of `g` in Zp.
544        q: MPI,
545        /// Public generator of Zp.
546        g: MPI,
547        /// Public key g^x mod p.
548        y: MPI,
549    },
550
551    /// ElGamal public key.
552    ElGamal {
553        /// Prime of the ring Zp.
554        p: MPI,
555        /// Generator of Zp.
556        g: MPI,
557        /// Public key g^x mod p.
558        y: MPI,
559    },
560
561    /// DJB's "Twisted" Edwards curve DSA public key.
562    EdDSA {
563        /// Curve we're using. Must be curve 25519.
564        curve: Curve,
565        /// Public point.
566        q: MPI,
567    },
568
569    /// NIST's Elliptic Curve DSA public key.
570    ECDSA {
571        /// Curve we're using.
572        curve: Curve,
573        /// Public point.
574        q: MPI,
575    },
576
577    /// Elliptic Curve Diffie-Hellman public key.
578    ECDH {
579        /// Curve we're using.
580        curve: Curve,
581        /// Public point.
582        q: MPI,
583        /// Algorithm used to derive the Key Encapsulation Key.
584        hash: HashAlgorithm,
585        /// Algorithm used to encapsulate the session key.
586        sym: SymmetricAlgorithm,
587    },
588
589    /// X25519 public key.
590    X25519 {
591        /// The public key, an opaque string.
592        u: [u8; 32],
593    },
594
595    /// X448 public key.
596    X448 {
597        /// The public key, an opaque string.
598        u: Box<[u8; 56]>,
599    },
600
601    /// Ed25519 public key.
602    Ed25519 {
603        /// The public key, an opaque string.
604        a: [u8; 32],
605    },
606
607    /// Ed448 public key.
608    Ed448 {
609        /// The public key, an opaque string.
610        a: Box<[u8; 57]>,
611    },
612
613    /// Composite signature algorithm using ML-DSA-65 and Ed25519.
614    MLDSA65_Ed25519 {
615        /// The Ed25519 public key, an opaque string.
616        eddsa: Box<[u8; 32]>,
617
618        /// The ML-DSA public key, an opaque string.
619        mldsa: Box<[u8; 1952]>,
620    },
621
622    /// Composite signature algorithm using ML-DSA-87 and Ed448.
623    MLDSA87_Ed448 {
624        /// The Ed448 public key, an opaque string.
625        eddsa: Box<[u8; 57]>,
626
627        /// The ML-DSA public key, an opaque string.
628        mldsa: Box<[u8; 2592]>,
629    },
630
631    /// SLH-DSA-SHAKE-128s public key.
632    ///
633    /// Note: [RFC 9980 only specifies the SHAKE variant of the
634    /// SLH-DSA
635    /// algorithm](https://www.rfc-editor.org/rfc/rfc9980.html#name-supported-public-key-algori).
636    SLHDSA128s {
637        /// The public key, an opaque string.
638        public: [u8; 32],
639    },
640
641    /// SLH-DSA-SHAKE-128f public key.
642    ///
643    /// Note: [RFC 9980 only specifies the SHAKE variant of the
644    /// SLH-DSA
645    /// algorithm](https://www.rfc-editor.org/rfc/rfc9980.html#name-supported-public-key-algori).
646    SLHDSA128f {
647        /// The public key, an opaque string.
648        public: [u8; 32],
649    },
650
651    /// SLH-DSA-SHAKE-256s public key.
652    ///
653    /// Note: [RFC 9980 only specifies the SHAKE variant of the
654    /// SLH-DSA
655    /// algorithm](https://www.rfc-editor.org/rfc/rfc9980.html#name-supported-public-key-algori).
656    SLHDSA256s {
657        /// The public key, an opaque string.
658        public: Box<[u8; 64]>,
659    },
660
661    /// Composite KEM using ML-KEM-768 and X25519.
662    MLKEM768_X25519 {
663        /// The X25519 public key, an opaque string.
664        ecdh: Box<[u8; 32]>,
665
666        /// The ML-KEM public key, an opaque string.
667        mlkem: Box<[u8; 1184]>,
668    },
669
670    /// Composite KEM using ML-KEM-1024 and X448.
671    MLKEM1024_X448 {
672        /// The X448 public key, an opaque string.
673        ecdh: Box<[u8; 56]>,
674
675        /// The ML-KEM public key, an opaque string.
676        mlkem: Box<[u8; 1568]>,
677    },
678
679    /// Unknown number of MPIs for an unknown algorithm.
680    Unknown {
681        /// The successfully parsed MPIs.
682        mpis: Box<[MPI]>,
683        /// Any data that failed to parse.
684        rest: Box<[u8]>,
685    },
686}
687assert_send_and_sync!(PublicKey);
688
689impl fmt::Debug for PublicKey {
690    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
691        match self {
692            PublicKey::RSA { e, n } =>
693                f.debug_struct("RSA")
694                .field("e", e)
695                .field("n", n)
696                .finish(),
697
698            PublicKey::DSA { p, q, g, y } =>
699                f.debug_struct("DSA")
700                .field("p", p)
701                .field("q", q)
702                .field("g", g)
703                .field("y", y)
704                .finish(),
705
706            PublicKey::ElGamal { p, g, y } =>
707                f.debug_struct("ElGamal")
708                .field("p", p)
709                .field("g", g)
710                .field("y", y)
711                .finish(),
712
713            PublicKey::EdDSA { curve, q } =>
714                f.debug_struct("EdDSA")
715                .field("curve", curve)
716                .field("q", q)
717                .finish(),
718
719            PublicKey::ECDSA { curve, q } =>
720                f.debug_struct("ECDSA")
721                .field("curve", curve)
722                .field("q", q)
723                .finish(),
724
725            PublicKey::ECDH { curve, q, hash, sym } =>
726                f.debug_struct("ECDH")
727                .field("curve", curve)
728                .field("q", q)
729                .field("hash", hash)
730                .field("sym", sym)
731                .finish(),
732
733            PublicKey::X25519 { u } =>
734                f.debug_struct("X25519")
735                .field("u", &hex::encode(u))
736                .finish(),
737
738            PublicKey::X448 { u } =>
739                f.debug_struct("X448")
740                .field("u", &hex::encode(u.as_ref()))
741                .finish(),
742
743            PublicKey::Ed25519 { a } =>
744                f.debug_struct("Ed25519")
745                .field("a", &hex::encode(a))
746                .finish(),
747
748            PublicKey::Ed448 { a } =>
749                f.debug_struct("Ed448")
750                .field("a", &hex::encode(a.as_ref()))
751                .finish(),
752
753            PublicKey::MLDSA65_Ed25519 { eddsa, mldsa } =>
754                f.debug_struct("MLDSA65_Ed25519")
755                .field("eddsa", &hex::encode(eddsa.as_ref()))
756                .field("mldsa", &hex::encode(mldsa.as_ref()))
757                .finish(),
758
759            PublicKey::MLDSA87_Ed448 { eddsa, mldsa } =>
760                f.debug_struct("MLDSA87_Ed448")
761                .field("eddsa", &hex::encode(eddsa.as_ref()))
762                .field("mldsa", &hex::encode(mldsa.as_ref()))
763                .finish(),
764
765            PublicKey::SLHDSA128s { public } =>
766                f.debug_struct("SLHDSA128s")
767                .field("public", &hex::encode(public))
768                .finish(),
769
770            PublicKey::SLHDSA128f { public } =>
771                f.debug_struct("SLHDSA128f")
772                .field("public", &hex::encode(public))
773                .finish(),
774
775            PublicKey::SLHDSA256s { public } =>
776                f.debug_struct("SLHDSA256s")
777                .field("public", &hex::encode(public.as_ref()))
778                .finish(),
779
780            PublicKey::MLKEM768_X25519 { ecdh, mlkem } =>
781                f.debug_struct("MLKEM768_X25519")
782                .field("ecdh", &hex::encode(ecdh.as_ref()))
783                .field("mlkem", &hex::encode(mlkem.as_ref()))
784                .finish(),
785
786            PublicKey::MLKEM1024_X448 { ecdh, mlkem } =>
787                f.debug_struct("MLKEM1024_X448")
788                .field("ecdh", &hex::encode(ecdh.as_ref()))
789                .field("mlkem", &hex::encode(mlkem.as_ref()))
790                .finish(),
791
792            PublicKey::Unknown { mpis, rest } =>
793                f.debug_struct("Unknown")
794                .field("mpis", mpis)
795                .field("rest", &hex::encode(rest))
796                .finish(),
797        }
798    }
799}
800
801impl PublicKey {
802    /// Returns the length of the public key in bits.
803    ///
804    /// For finite field crypto this returns the size of the field we
805    /// operate in, for ECC it returns `Curve::bits()`.
806    ///
807    /// Note: This information is useless and should not be used to
808    /// gauge the security of a particular key. This function exists
809    /// only because some legacy PGP application like HKP need it.
810    ///
811    /// Returns `None` for unknown keys and curves.
812    pub fn bits(&self) -> Option<usize> {
813        use self::PublicKey::*;
814        match self {
815            RSA { ref n,.. } => Some(n.bits()),
816            DSA { ref p,.. } => Some(p.bits()),
817            ElGamal { ref p,.. } => Some(p.bits()),
818            EdDSA { ref curve,.. } => curve.bits().ok(),
819            ECDSA { ref curve,.. } => curve.bits().ok(),
820            ECDH { ref curve,.. } => curve.bits().ok(),
821            X25519 { .. } => Some(256),
822            X448 { .. } => Some(448),
823            Ed25519 { .. } => Some(256),
824            Ed448 { .. } => Some(456),
825            MLDSA65_Ed25519 { .. } => None,
826            MLDSA87_Ed448 { .. } => None,
827            SLHDSA128s { .. } => None,
828            SLHDSA128f { .. } => None,
829            SLHDSA256s { .. } => None,
830            MLKEM768_X25519 { .. } => None,
831            MLKEM1024_X448 { .. } => None,
832            Unknown { .. } => None,
833        }
834    }
835
836    /// Returns, if known, the public-key algorithm for this public
837    /// key.
838    pub fn algo(&self) -> Option<PublicKeyAlgorithm> {
839        use self::PublicKey::*;
840        #[allow(deprecated)]
841        match self {
842            RSA { .. } => Some(PublicKeyAlgorithm::RSAEncryptSign),
843            DSA { .. } => Some(PublicKeyAlgorithm::DSA),
844            ElGamal { .. } => Some(PublicKeyAlgorithm::ElGamalEncrypt),
845            EdDSA { .. } => Some(PublicKeyAlgorithm::EdDSA),
846            ECDSA { .. } => Some(PublicKeyAlgorithm::ECDSA),
847            ECDH { .. } => Some(PublicKeyAlgorithm::ECDH),
848            X25519 { .. } => Some(PublicKeyAlgorithm::X25519),
849            X448 { .. } => Some(PublicKeyAlgorithm::X448),
850            Ed25519 { .. } => Some(PublicKeyAlgorithm::Ed25519),
851            Ed448 { .. } => Some(PublicKeyAlgorithm::Ed448),
852            MLDSA65_Ed25519 { .. } =>
853                Some(PublicKeyAlgorithm::MLDSA65_Ed25519),
854            MLDSA87_Ed448 { .. } =>
855                Some(PublicKeyAlgorithm::MLDSA87_Ed448),
856            SLHDSA128s { .. } => Some(PublicKeyAlgorithm::SLHDSA128s),
857            SLHDSA128f { .. } => Some(PublicKeyAlgorithm::SLHDSA128f),
858            SLHDSA256s { .. } => Some(PublicKeyAlgorithm::SLHDSA256s),
859            MLKEM768_X25519 { .. } =>
860                Some(PublicKeyAlgorithm::MLKEM768_X25519),
861            MLKEM1024_X448 { .. } =>
862                Some(PublicKeyAlgorithm::MLKEM1024_X448),
863            Unknown { .. } => None,
864        }
865    }
866}
867
868impl Hash for PublicKey {
869    fn hash(&self, mut hash: &mut hash::Context) -> Result<()> {
870        self.serialize(&mut hash as &mut dyn Write)
871    }
872}
873
874#[cfg(test)]
875impl Arbitrary for PublicKey {
876    fn arbitrary(g: &mut Gen) -> Self {
877        use self::PublicKey::*;
878        use crate::arbitrary_helper::gen_arbitrary_from_range;
879
880        match gen_arbitrary_from_range(0..17, g) {
881            0 => RSA {
882                e: MPI::arbitrary(g),
883                n: MPI::arbitrary(g),
884            },
885
886            1 => DSA {
887                p: MPI::arbitrary(g),
888                q: MPI::arbitrary(g),
889                g: MPI::arbitrary(g),
890                y: MPI::arbitrary(g),
891            },
892
893            2 => ElGamal {
894                p: MPI::arbitrary(g),
895                g: MPI::arbitrary(g),
896                y: MPI::arbitrary(g),
897            },
898
899            3 => EdDSA {
900                curve: Curve::arbitrary(g),
901                q: MPI::arbitrary(g),
902            },
903
904            4 => ECDSA {
905                curve: Curve::arbitrary(g),
906                q: MPI::arbitrary(g),
907            },
908
909            5 => ECDH {
910                curve: Curve::arbitrary(g),
911                q: MPI::arbitrary(g),
912                hash: HashAlgorithm::arbitrary(g),
913                sym: SymmetricAlgorithm::arbitrary(g),
914            },
915
916            6 => X25519 { u: arbitrary(g) },
917            7 => X448 { u: Box::new(arbitrarize(g, [0; 56])) },
918            8 => Ed25519 { a: arbitrary(g) },
919            9 => Ed448 { a: Box::new(arbitrarize(g, [0; 57])) },
920
921            10 => MLDSA65_Ed25519 {
922                eddsa: Box::new(arbitrarize(g, [0; 32])),
923                mldsa: Box::new(arbitrarize(g, [0; 1952])),
924            },
925
926            11 => MLDSA87_Ed448 {
927                eddsa: Box::new(arbitrarize(g, [0; 57])),
928                mldsa: Box::new(arbitrarize(g, [0; 2592])),
929            },
930
931            12 => SLHDSA128s {
932                public: arbitrary(g),
933            },
934
935            13 => SLHDSA128f {
936                public: arbitrary(g),
937            },
938
939            14 => SLHDSA256s {
940                public: Box::new(arbitrarize(g, [0; 64])),
941            },
942
943            15 => MLKEM768_X25519 {
944                ecdh: Box::new(arbitrarize(g, [0; 32])),
945                mlkem: Box::new(arbitrarize(g, [0; 1184])),
946            },
947
948            16 => MLKEM1024_X448 {
949                ecdh: Box::new(arbitrarize(g, [0; 56])),
950                mlkem: Box::new(arbitrarize(g, [0; 1568])),
951            },
952
953            _ => unreachable!(),
954        }
955    }
956}
957
958#[cfg(test)]
959pub(crate) fn arbitrarize<T: AsMut<[u8]>>(g: &mut Gen, mut a: T) -> T
960{
961    a.as_mut().iter_mut().for_each(|p| *p = Arbitrary::arbitrary(g));
962    a
963}
964
965#[cfg(test)]
966pub(crate) fn arbitrary<T: Default + AsMut<[u8]>>(g: &mut Gen) -> T
967{
968    arbitrarize(g, Default::default())
969}
970
971
972/// A secret key.
973///
974/// Provides a typed and structured way of storing multiple MPIs in
975/// [`Key`] packets.  Secret key components are protected by storing
976/// them using [`ProtectedMPI`].
977///
978///   [`Key`]: crate::packet::Key
979// Deriving Hash here is okay: PartialEq is manually implemented to
980// ensure that secrets are compared in constant-time.
981#[non_exhaustive]
982#[allow(non_camel_case_types)]
983#[allow(clippy::derived_hash_with_manual_eq)]
984#[derive(Clone, Hash)]
985pub enum SecretKeyMaterial {
986    /// RSA secret key.
987    RSA {
988        /// Secret exponent, inverse of e in Phi(N).
989        d: ProtectedMPI,
990        /// Smaller secret prime.
991        p: ProtectedMPI,
992        /// Larger secret prime.
993        q: ProtectedMPI,
994        /// Inverse of p mod q.
995        u: ProtectedMPI,
996    },
997
998    /// NIST DSA secret key.
999    DSA {
1000        /// Secret key log_g(y) in Zp.
1001        x: ProtectedMPI,
1002    },
1003
1004    /// ElGamal secret key.
1005    ElGamal {
1006        /// Secret key log_g(y) in Zp.
1007        x: ProtectedMPI,
1008    },
1009
1010    /// DJB's "Twisted" Edwards curve DSA secret key.
1011    EdDSA {
1012        /// Secret scalar.
1013        scalar: ProtectedMPI,
1014    },
1015
1016    /// NIST's Elliptic Curve DSA secret key.
1017    ECDSA {
1018        /// Secret scalar.
1019        scalar: ProtectedMPI,
1020    },
1021
1022    /// Elliptic Curve Diffie-Hellman secret key.
1023    ECDH {
1024        /// Secret scalar.
1025        scalar: ProtectedMPI,
1026    },
1027
1028    /// X25519 secret key.
1029    X25519 {
1030        /// The secret key, an opaque string.
1031        x: Protected,
1032    },
1033
1034    /// X448 secret key.
1035    X448 {
1036        /// The secret key, an opaque string.
1037        x: Protected,
1038    },
1039
1040    /// Ed25519 secret key.
1041    Ed25519 {
1042        /// The secret key, an opaque string.
1043        x: Protected,
1044    },
1045
1046    /// Ed448 secret key.
1047    Ed448 {
1048        /// The secret key, an opaque string.
1049        x: Protected,
1050    },
1051
1052    /// Composite signature algorithm using ML-DSA-65 and Ed25519.
1053    MLDSA65_Ed25519 {
1054        /// The Ed25519 secret key, an opaque string.
1055        eddsa: Protected,
1056
1057        /// The ML-DSA secret key, an opaque string.
1058        mldsa: Protected,
1059    },
1060
1061    /// Composite signature algorithm using ML-DSA-87 and Ed448.
1062    MLDSA87_Ed448 {
1063        /// The Ed448 secret key, an opaque string.
1064        eddsa: Protected,
1065
1066        /// The ML-DSA secret key, an opaque string.
1067        mldsa: Protected,
1068    },
1069
1070    /// SLH-DSA-SHAKE-128s secret key.
1071    SLHDSA128s {
1072        /// The secret key, an opaque string.
1073        secret: Protected,
1074    },
1075
1076    /// SLH-DSA-SHAKE-128f secret key.
1077    SLHDSA128f {
1078        /// The secret key, an opaque string.
1079        secret: Protected,
1080    },
1081
1082    /// SLH-DSA-SHAKE-256s secret key.
1083    SLHDSA256s {
1084        /// The secret key, an opaque string.
1085        secret: Protected,
1086    },
1087
1088    /// Composite KEM using ML-KEM-768 and X25519.
1089    MLKEM768_X25519 {
1090        /// The X25519 secret key, an opaque string.
1091        ecdh: Protected,
1092
1093        /// The ML-KEM secret key, an opaque string.
1094        mlkem: Protected,
1095    },
1096
1097    /// Composite KEM using ML-KEM-1024 and X448.
1098    MLKEM1024_X448 {
1099        /// The X448 secret key, an opaque string.
1100        ecdh: Protected,
1101
1102        /// The ML-KEM secret key, an opaque string.
1103        mlkem: Protected,
1104    },
1105
1106    /// Unknown number of MPIs for an unknown algorithm.
1107    Unknown {
1108        /// The successfully parsed MPIs.
1109        mpis: Box<[ProtectedMPI]>,
1110        /// Any data that failed to parse.
1111        rest: Protected,
1112    },
1113}
1114assert_send_and_sync!(SecretKeyMaterial);
1115
1116impl fmt::Debug for SecretKeyMaterial {
1117    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1118        if cfg!(debug_assertions) {
1119            match self {
1120                SecretKeyMaterial::RSA { d, p, q, u } =>
1121                    f.debug_struct("RSA")
1122                    .field("d", d)
1123                    .field("p", p)
1124                    .field("q", q)
1125                    .field("u", u)
1126                    .finish(),
1127
1128                SecretKeyMaterial::DSA { x } =>
1129                    f.debug_struct("DSA")
1130                    .field("x", x)
1131                    .finish(),
1132
1133                SecretKeyMaterial::ElGamal { x } =>
1134                    f.debug_struct("ElGamal")
1135                    .field("x", x)
1136                    .finish(),
1137
1138                SecretKeyMaterial::EdDSA { scalar } =>
1139                    f.debug_struct("EdDSA")
1140                    .field("scalar", scalar)
1141                    .finish(),
1142
1143                SecretKeyMaterial::ECDSA { scalar } =>
1144                    f.debug_struct("ECDSA")
1145                    .field("scalar", scalar)
1146                    .finish(),
1147
1148                SecretKeyMaterial::ECDH { scalar } =>
1149                    f.debug_struct("ECDH")
1150                    .field("scalar", scalar)
1151                    .finish(),
1152
1153                SecretKeyMaterial::X25519 { x } =>
1154                    f.debug_struct("X25519")
1155                    .field("x", &hex::encode(x))
1156                    .finish(),
1157
1158                SecretKeyMaterial::X448 { x } =>
1159                    f.debug_struct("X448")
1160                    .field("x", &hex::encode(x))
1161                    .finish(),
1162
1163                SecretKeyMaterial::Ed25519 { x } =>
1164                    f.debug_struct("Ed25519")
1165                    .field("x", &hex::encode(x))
1166                    .finish(),
1167
1168                SecretKeyMaterial::Ed448 { x } =>
1169                    f.debug_struct("Ed448")
1170                    .field("x", &hex::encode(x))
1171                    .finish(),
1172
1173                SecretKeyMaterial::MLDSA65_Ed25519 { eddsa, mldsa } =>
1174                    f.debug_struct("MLDSA65_Ed25519")
1175                    .field("eddsa", &hex::encode(eddsa))
1176                    .field("mldsa", &hex::encode(mldsa))
1177                    .finish(),
1178
1179                SecretKeyMaterial::MLDSA87_Ed448 { eddsa, mldsa } =>
1180                    f.debug_struct("MLDSA87_Ed448")
1181                    .field("eddsa", &hex::encode(eddsa))
1182                    .field("mldsa", &hex::encode(mldsa))
1183                    .finish(),
1184
1185                SecretKeyMaterial::SLHDSA128s { secret } =>
1186                    f.debug_struct("SLHDSA128s")
1187                    .field("secret", &hex::encode(secret.as_ref()))
1188                    .finish(),
1189
1190                SecretKeyMaterial::SLHDSA128f { secret } =>
1191                    f.debug_struct("SLHDSA128f")
1192                    .field("secret", &hex::encode(secret.as_ref()))
1193                    .finish(),
1194
1195                SecretKeyMaterial::SLHDSA256s { secret } =>
1196                    f.debug_struct("SLHDSA256s")
1197                    .field("secret", &hex::encode(secret.as_ref()))
1198                    .finish(),
1199
1200                SecretKeyMaterial::MLKEM768_X25519 { ecdh, mlkem } =>
1201                    f.debug_struct("MLKEM768_X25519")
1202                    .field("ecdh", &hex::encode(ecdh))
1203                    .field("mlkem", &hex::encode(mlkem))
1204                    .finish(),
1205
1206                SecretKeyMaterial::MLKEM1024_X448 { ecdh, mlkem } =>
1207                    f.debug_struct("MLKEM1024_X448")
1208                    .field("ecdh", &hex::encode(ecdh))
1209                    .field("mlkem", &hex::encode(mlkem))
1210                    .finish(),
1211
1212                SecretKeyMaterial::Unknown{ mpis, rest } =>
1213                    f.debug_struct("Unknown")
1214                    .field("mpis", mpis)
1215                    .field("rest", &hex::encode(rest))
1216                    .finish(),
1217            }
1218        } else {
1219            match self {
1220                SecretKeyMaterial::RSA{ .. } =>
1221                    f.write_str("RSA { <Redacted> }"),
1222                SecretKeyMaterial::DSA{ .. } =>
1223                    f.write_str("DSA { <Redacted> }"),
1224                SecretKeyMaterial::ElGamal{ .. } =>
1225                    f.write_str("ElGamal { <Redacted> }"),
1226                SecretKeyMaterial::EdDSA{ .. } =>
1227                    f.write_str("EdDSA { <Redacted> }"),
1228                SecretKeyMaterial::ECDSA{ .. } =>
1229                    f.write_str("ECDSA { <Redacted> }"),
1230                SecretKeyMaterial::ECDH{ .. } =>
1231                    f.write_str("ECDH { <Redacted> }"),
1232                SecretKeyMaterial::X25519 { .. } =>
1233                    f.write_str("X25519 { <Redacted> }"),
1234                SecretKeyMaterial::X448 { .. } =>
1235                    f.write_str("X448 { <Redacted> }"),
1236                SecretKeyMaterial::Ed25519 { .. } =>
1237                    f.write_str("Ed25519 { <Redacted> }"),
1238                SecretKeyMaterial::Ed448 { .. } =>
1239                    f.write_str("Ed448 { <Redacted> }"),
1240                SecretKeyMaterial::MLDSA65_Ed25519 { .. } =>
1241                    f.write_str("MLDSA65_Ed25519 { <Redacted> }"),
1242                SecretKeyMaterial::MLDSA87_Ed448 { .. } =>
1243                    f.write_str("MLDSA87_Ed448 { <Redacted> }"),
1244                SecretKeyMaterial::SLHDSA128s { .. } =>
1245                    f.write_str("SLHDSA128s { <Redacted> }"),
1246                SecretKeyMaterial::SLHDSA128f { .. } =>
1247                    f.write_str("SLHDSA128f { <Redacted> }"),
1248                SecretKeyMaterial::SLHDSA256s { .. } =>
1249                    f.write_str("SLHDSA256s { <Redacted> }"),
1250                SecretKeyMaterial::MLKEM768_X25519 { .. } =>
1251                    f.write_str("MLKEM768_X25519 { <Redacted> }"),
1252                SecretKeyMaterial::MLKEM1024_X448 { .. } =>
1253                    f.write_str("MLKEM1024_X448 { <Redacted> }"),
1254                SecretKeyMaterial::Unknown{ .. } =>
1255                    f.write_str("Unknown { <Redacted> }"),
1256            }
1257        }
1258    }
1259}
1260
1261impl PartialOrd for SecretKeyMaterial {
1262    fn partial_cmp(&self, other: &SecretKeyMaterial) -> Option<Ordering> {
1263        Some(self.cmp(other))
1264    }
1265}
1266
1267impl Ord for SecretKeyMaterial {
1268    fn cmp(&self, other: &Self) -> Ordering {
1269        use std::iter;
1270
1271        fn discriminant(sk: &SecretKeyMaterial) -> usize {
1272            match sk {
1273                SecretKeyMaterial::RSA{ .. } => 0,
1274                SecretKeyMaterial::DSA{ .. } => 1,
1275                SecretKeyMaterial::ElGamal{ .. } => 2,
1276                SecretKeyMaterial::EdDSA{ .. } => 3,
1277                SecretKeyMaterial::ECDSA{ .. } => 4,
1278                SecretKeyMaterial::ECDH{ .. } => 5,
1279                SecretKeyMaterial::X25519 { .. } => 6,
1280                SecretKeyMaterial::X448 { .. } => 7,
1281                SecretKeyMaterial::Ed25519 { .. } => 8,
1282                SecretKeyMaterial::Ed448 { .. } => 9,
1283                SecretKeyMaterial::Unknown { .. } => 10,
1284                SecretKeyMaterial::MLDSA65_Ed25519 { .. } => 11,
1285                SecretKeyMaterial::MLDSA87_Ed448 { .. } => 12,
1286                SecretKeyMaterial::SLHDSA128s { .. } => 13,
1287                SecretKeyMaterial::SLHDSA128f { .. } => 14,
1288                SecretKeyMaterial::SLHDSA256s { .. } => 15,
1289                SecretKeyMaterial::MLKEM768_X25519 { .. } => 16,
1290                SecretKeyMaterial::MLKEM1024_X448 { .. } => 17,
1291            }
1292        }
1293
1294        let ret = match (self, other) {
1295            (&SecretKeyMaterial::RSA{ d: ref d1, p: ref p1, q: ref q1, u: ref u1 }
1296            ,&SecretKeyMaterial::RSA{ d: ref d2, p: ref p2, q: ref q2, u: ref u2 }) => {
1297                let o1 = d1.cmp(d2);
1298                let o2 = p1.cmp(p2);
1299                let o3 = q1.cmp(q2);
1300                let o4 = u1.cmp(u2);
1301
1302                if o1 != Ordering::Equal { return o1; }
1303                if o2 != Ordering::Equal { return o2; }
1304                if o3 != Ordering::Equal { return o3; }
1305                o4
1306            }
1307            (&SecretKeyMaterial::DSA{ x: ref x1 }
1308            ,&SecretKeyMaterial::DSA{ x: ref x2 }) => {
1309                x1.cmp(x2)
1310            }
1311            (&SecretKeyMaterial::ElGamal{ x: ref x1 }
1312            ,&SecretKeyMaterial::ElGamal{ x: ref x2 }) => {
1313                x1.cmp(x2)
1314            }
1315            (&SecretKeyMaterial::EdDSA{ scalar: ref scalar1 }
1316            ,&SecretKeyMaterial::EdDSA{ scalar: ref scalar2 }) => {
1317                scalar1.cmp(scalar2)
1318            }
1319            (&SecretKeyMaterial::ECDSA{ scalar: ref scalar1 }
1320            ,&SecretKeyMaterial::ECDSA{ scalar: ref scalar2 }) => {
1321                scalar1.cmp(scalar2)
1322            }
1323            (&SecretKeyMaterial::ECDH{ scalar: ref scalar1 }
1324            ,&SecretKeyMaterial::ECDH{ scalar: ref scalar2 }) => {
1325                scalar1.cmp(scalar2)
1326            }
1327            (SecretKeyMaterial::X25519 { x: x0 },
1328             SecretKeyMaterial::X25519 { x: x1 }) => x0.cmp(x1),
1329            (SecretKeyMaterial::X448 { x: x0 },
1330             SecretKeyMaterial::X448 { x: x1 }) => x0.cmp(x1),
1331            (SecretKeyMaterial::Ed25519 { x: x0 },
1332             SecretKeyMaterial::Ed25519 { x: x1 }) => x0.cmp(x1),
1333            (SecretKeyMaterial::Ed448 { x: x0 },
1334             SecretKeyMaterial::Ed448 { x: x1 }) => x0.cmp(x1),
1335
1336            (SecretKeyMaterial::MLDSA65_Ed25519 { eddsa: e0, mldsa: m0 },
1337             SecretKeyMaterial::MLDSA65_Ed25519 { eddsa: e1, mldsa: m1 }) =>
1338                iter::once(e0.cmp(e1))
1339                    .chain(iter::once(m0.cmp(m1)))
1340                    .fold(Ordering::Equal, |acc, x| acc.then(x)),
1341
1342            (SecretKeyMaterial::MLDSA87_Ed448 { eddsa: e0, mldsa: m0 },
1343             SecretKeyMaterial::MLDSA87_Ed448 { eddsa: e1, mldsa: m1 }) =>
1344                iter::once(e0.cmp(e1))
1345                    .chain(iter::once(m0.cmp(m1)))
1346                    .fold(Ordering::Equal, |acc, x| acc.then(x)),
1347
1348            (SecretKeyMaterial::SLHDSA128s { secret: s0 },
1349             SecretKeyMaterial::SLHDSA128s { secret: s1 }) => s0.cmp(s1),
1350
1351            (SecretKeyMaterial::SLHDSA128f { secret: s0 },
1352             SecretKeyMaterial::SLHDSA128f { secret: s1 }) => s0.cmp(s1),
1353
1354            (SecretKeyMaterial::SLHDSA256s { secret: s0 },
1355             SecretKeyMaterial::SLHDSA256s { secret: s1 }) => s0.cmp(s1),
1356
1357            (SecretKeyMaterial::MLKEM768_X25519 { ecdh: e0, mlkem: m0 },
1358             SecretKeyMaterial::MLKEM768_X25519 { ecdh: e1, mlkem: m1 }) =>
1359                iter::once(e0.cmp(e1))
1360                    .chain(iter::once(m0.cmp(m1)))
1361                    .fold(Ordering::Equal, |acc, x| acc.then(x)),
1362
1363            (SecretKeyMaterial::MLKEM1024_X448 { ecdh: e0, mlkem: m0 },
1364             SecretKeyMaterial::MLKEM1024_X448 { ecdh: e1, mlkem: m1 }) =>
1365                iter::once(e0.cmp(e1))
1366                    .chain(iter::once(m0.cmp(m1)))
1367                    .fold(Ordering::Equal, |acc, x| acc.then(x)),
1368
1369            (&SecretKeyMaterial::Unknown{ mpis: ref mpis1, rest: ref rest1 }
1370            ,&SecretKeyMaterial::Unknown{ mpis: ref mpis2, rest: ref rest2 }) => {
1371                let o1 = secure_cmp(rest1, rest2);
1372                let o2 = mpis1.len().cmp(&mpis2.len());
1373                let on = mpis1.iter().zip(mpis2.iter()).map(|(a,b)| {
1374                    a.cmp(b)
1375                }).collect::<Vec<_>>();
1376
1377                iter::once(o1)
1378                    .chain(iter::once(o2))
1379                    .chain(on.iter().cloned())
1380                    .fold(Ordering::Equal, |acc, x| acc.then(x))
1381            }
1382
1383            (a, b) => {
1384                let ret = discriminant(a).cmp(&discriminant(b));
1385
1386                assert!(ret != Ordering::Equal);
1387                ret
1388            }
1389        };
1390
1391        ret
1392    }
1393}
1394
1395impl PartialEq for SecretKeyMaterial {
1396    fn eq(&self, other: &Self) -> bool { self.cmp(other) == Ordering::Equal }
1397}
1398
1399impl Eq for SecretKeyMaterial {}
1400
1401impl SecretKeyMaterial {
1402    /// Returns, if known, the public-key algorithm for this secret
1403    /// key.
1404    pub fn algo(&self) -> Option<PublicKeyAlgorithm> {
1405        use self::SecretKeyMaterial::*;
1406        #[allow(deprecated)]
1407        match self {
1408            RSA { .. } => Some(PublicKeyAlgorithm::RSAEncryptSign),
1409            DSA { .. } => Some(PublicKeyAlgorithm::DSA),
1410            ElGamal { .. } => Some(PublicKeyAlgorithm::ElGamalEncrypt),
1411            EdDSA { .. } => Some(PublicKeyAlgorithm::EdDSA),
1412            ECDSA { .. } => Some(PublicKeyAlgorithm::ECDSA),
1413            ECDH { .. } => Some(PublicKeyAlgorithm::ECDH),
1414            X25519 { .. } => Some(PublicKeyAlgorithm::X25519),
1415            X448 { .. } => Some(PublicKeyAlgorithm::X448),
1416            Ed25519 { .. } => Some(PublicKeyAlgorithm::Ed25519),
1417            Ed448 { .. } => Some(PublicKeyAlgorithm::Ed448),
1418            MLDSA65_Ed25519 { .. } => Some(PublicKeyAlgorithm::MLDSA65_Ed25519),
1419            MLDSA87_Ed448 { .. } => Some(PublicKeyAlgorithm::MLDSA87_Ed448),
1420            SLHDSA128s { .. } => Some(PublicKeyAlgorithm::SLHDSA128s),
1421            SLHDSA128f { .. } => Some(PublicKeyAlgorithm::SLHDSA128f),
1422            SLHDSA256s { .. } => Some(PublicKeyAlgorithm::SLHDSA256s),
1423            MLKEM768_X25519 { .. } => Some(PublicKeyAlgorithm::MLKEM768_X25519),
1424            MLKEM1024_X448 { .. } => Some(PublicKeyAlgorithm::MLKEM1024_X448),
1425            Unknown { .. } => None,
1426        }
1427    }
1428}
1429
1430impl Hash for SecretKeyMaterial {
1431    fn hash(&self, mut hash: &mut hash::Context) -> Result<()> {
1432        self.serialize(&mut hash as &mut dyn Write)
1433    }
1434}
1435
1436#[cfg(test)]
1437impl SecretKeyMaterial {
1438    pub(crate) fn arbitrary_for(g: &mut Gen, pk: PublicKeyAlgorithm) -> Result<Self> {
1439        use self::PublicKeyAlgorithm::*;
1440        #[allow(deprecated)]
1441        match pk {
1442            RSAEncryptSign | RSASign | RSAEncrypt => Ok(SecretKeyMaterial::RSA {
1443                d: ProtectedMPI::arbitrary(g),
1444                p: ProtectedMPI::arbitrary(g),
1445                q: ProtectedMPI::arbitrary(g),
1446                u: ProtectedMPI::arbitrary(g),
1447            }),
1448
1449            DSA => Ok(SecretKeyMaterial::DSA {
1450                x: ProtectedMPI::arbitrary(g),
1451            }),
1452
1453            ElGamalEncryptSign | ElGamalEncrypt => Ok(SecretKeyMaterial::ElGamal {
1454                x: ProtectedMPI::arbitrary(g),
1455            }),
1456
1457            EdDSA => Ok(SecretKeyMaterial::EdDSA {
1458                scalar: ProtectedMPI::arbitrary(g),
1459            }),
1460
1461            ECDSA => Ok(SecretKeyMaterial::ECDSA {
1462                scalar: ProtectedMPI::arbitrary(g),
1463            }),
1464
1465            ECDH => Ok(SecretKeyMaterial::ECDH {
1466                scalar: ProtectedMPI::arbitrary(g),
1467            }),
1468
1469            X25519 => Ok(SecretKeyMaterial::X25519 {
1470                x: arbitrarize(g, vec![0; 32]).into(),
1471            }),
1472            X448 => Ok(SecretKeyMaterial::X448 {
1473                x: arbitrarize(g, vec![0; 56]).into(),
1474            }),
1475            Ed25519 => Ok(SecretKeyMaterial::Ed25519 {
1476                x: arbitrarize(g, vec![0; 32]).into(),
1477            }),
1478            Ed448 => Ok(SecretKeyMaterial::Ed448 {
1479                x: arbitrarize(g, vec![0; 57]).into(),
1480            }),
1481
1482            MLDSA65_Ed25519 => Ok(SecretKeyMaterial::MLDSA65_Ed25519 {
1483                eddsa: arbitrarize(g, vec![0; 32]).into(),
1484                mldsa: arbitrarize(g, vec![0; 32]).into(),
1485            }),
1486
1487            MLDSA87_Ed448 => Ok(SecretKeyMaterial::MLDSA87_Ed448 {
1488                eddsa: arbitrarize(g, vec![0; 57]).into(),
1489                mldsa: arbitrarize(g, vec![0; 32]).into(),
1490            }),
1491
1492            SLHDSA128s => Ok(SecretKeyMaterial::SLHDSA128s {
1493                secret: arbitrarize(g, [0; 64]).into(),
1494            }),
1495
1496            SLHDSA128f => Ok(SecretKeyMaterial::SLHDSA128f {
1497                secret: arbitrarize(g, [0; 64]).into(),
1498            }),
1499
1500            SLHDSA256s => Ok(SecretKeyMaterial::SLHDSA256s {
1501                secret: arbitrarize(g, [0; 128]).into(),
1502            }),
1503
1504            MLKEM768_X25519 => Ok(SecretKeyMaterial::MLKEM768_X25519 {
1505                ecdh: arbitrarize(g, vec![0; 32]).into(),
1506                mlkem: arbitrarize(g, vec![0; 64]).into(),
1507            }),
1508
1509            MLKEM1024_X448 => Ok(SecretKeyMaterial::MLKEM1024_X448 {
1510                ecdh: arbitrarize(g, vec![0; 56]).into(),
1511                mlkem: arbitrarize(g, vec![0; 64]).into(),
1512            }),
1513
1514            Private(_) | Unknown(_) =>
1515                Err(Error::UnsupportedPublicKeyAlgorithm(pk).into()),
1516        }
1517    }
1518}
1519#[cfg(test)]
1520impl Arbitrary for SecretKeyMaterial {
1521    fn arbitrary(g: &mut Gen) -> Self {
1522        let pk = *g.choose(
1523            &crate::crypto::types::public_key_algorithm::PUBLIC_KEY_ALGORITHM_VARIANTS)
1524            .expect("not empty");
1525        Self::arbitrary_for(g, pk).expect("only known variants")
1526    }
1527}
1528
1529/// Checksum method for secret key material.
1530///
1531/// Secret key material may be protected by a checksum.  See [Section
1532/// 5.5.3 of RFC 9580] for details.
1533///
1534///   [Section 5.5.3 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.5.3
1535#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
1536pub enum SecretKeyChecksum {
1537    /// SHA1 over the decrypted secret key.
1538    SHA1,
1539
1540    /// Sum of the decrypted secret key octets modulo 65536.
1541    Sum16,
1542}
1543assert_send_and_sync!(SecretKeyChecksum);
1544
1545impl Default for SecretKeyChecksum {
1546    fn default() -> Self {
1547        SecretKeyChecksum::SHA1
1548    }
1549}
1550
1551impl SecretKeyChecksum {
1552    /// Returns the on-wire length of the checksum.
1553    pub(crate) fn len(&self) -> usize {
1554        match self {
1555            SecretKeyChecksum::SHA1 => 20,
1556            SecretKeyChecksum::Sum16 => 2,
1557        }
1558    }
1559}
1560
1561/// An encrypted session key.
1562///
1563/// Provides a typed and structured way of storing multiple MPIs in
1564/// [`PKESK`] packets.
1565///
1566///   [`PKESK`]: crate::packet::PKESK
1567#[non_exhaustive]
1568#[allow(non_camel_case_types)]
1569#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1570pub enum Ciphertext {
1571    /// RSA ciphertext.
1572    RSA {
1573        ///  m^e mod N.
1574        c: MPI,
1575    },
1576
1577    /// ElGamal ciphertext.
1578    ElGamal {
1579        /// Ephemeral key.
1580        e: MPI,
1581        /// Ciphertext.
1582        c: MPI,
1583    },
1584
1585    /// Elliptic curve ElGamal public key.
1586    ECDH {
1587        /// Ephemeral key.
1588        e: MPI,
1589        /// Symmetrically encrypted session key.
1590        key: Box<[u8]>,
1591    },
1592
1593    /// X25519 ciphertext.
1594    X25519 {
1595        /// Ephermeral key.
1596        e: Box<[u8; 32]>,
1597        /// Symmetrically encrypted session key.
1598        key: Box<[u8]>,
1599    },
1600
1601    /// X448 ciphertext.
1602    X448 {
1603        /// Ephermeral key.
1604        e: Box<[u8; 56]>,
1605        /// Symmetrically encrypted session key.
1606        key: Box<[u8]>,
1607    },
1608
1609    /// Composite KEM using ML-KEM-768 and X25519.
1610    MLKEM768_X25519 {
1611        /// The X25519 ciphertext, an opaque string.
1612        ecdh: Box<[u8; 32]>,
1613
1614        /// The ML-KEM ciphertext, an opaque string.
1615        mlkem: Box<[u8; 1088]>,
1616
1617        /// Symmetrically encrypted session key.
1618        esk: Box<[u8]>,
1619    },
1620
1621    /// Composite KEM using ML-KEM-1024 and X448.
1622    MLKEM1024_X448 {
1623        /// The X448 ciphertext, an opaque string.
1624        ecdh: Box<[u8; 56]>,
1625
1626        /// The ML-KEM ciphertext, an opaque string.
1627        mlkem: Box<[u8; 1568]>,
1628
1629        /// Symmetrically encrypted session key.
1630        esk: Box<[u8]>,
1631    },
1632
1633    /// Unknown number of MPIs for an unknown algorithm.
1634    Unknown {
1635        /// The successfully parsed MPIs.
1636        mpis: Box<[MPI]>,
1637        /// Any data that failed to parse.
1638        rest: Box<[u8]>,
1639    },
1640}
1641assert_send_and_sync!(Ciphertext);
1642
1643impl fmt::Debug for Ciphertext {
1644    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1645        match self {
1646            Ciphertext::RSA { c } =>
1647                f.debug_struct("RSA")
1648                .field("c", c)
1649                .finish(),
1650
1651            Ciphertext::ElGamal { e, c } =>
1652                f.debug_struct("ElGamal")
1653                .field("e", e)
1654                .field("c", c)
1655                .finish(),
1656
1657            Ciphertext::ECDH { e, key } =>
1658                f.debug_struct("ECDH")
1659                .field("e", e)
1660                .field("key", &hex::encode(key))
1661                .finish(),
1662
1663            Ciphertext::X25519 { e, key } =>
1664                f.debug_struct("X25519")
1665                .field("e", &hex::encode(&e[..]))
1666                .field("key", &hex::encode(key))
1667                .finish(),
1668
1669            Ciphertext::X448 { e, key } =>
1670                f.debug_struct("X448")
1671                .field("e", &hex::encode(&e[..]))
1672                .field("key", &hex::encode(key))
1673                .finish(),
1674
1675            Ciphertext::MLKEM768_X25519 { ecdh, mlkem, esk } =>
1676                f.debug_struct("MLKEM768_X25519")
1677                .field("ecdh", &hex::encode(&ecdh[..]))
1678                .field("mlkem", &hex::encode(&mlkem[..]))
1679                .field("esk", &hex::encode(esk))
1680                .finish(),
1681
1682            Ciphertext::MLKEM1024_X448 { ecdh, mlkem, esk } =>
1683                f.debug_struct("MLKEM1024_X448")
1684                .field("ecdh", &hex::encode(&ecdh[..]))
1685                .field("mlkem", &hex::encode(&mlkem[..]))
1686                .field("esk", &hex::encode(esk))
1687                .finish(),
1688
1689            Ciphertext::Unknown { mpis, rest } =>
1690                f.debug_struct("Unknown")
1691                .field("mpis", mpis)
1692                .field("rest", &hex::encode(rest))
1693                .finish(),
1694        }
1695    }
1696}
1697
1698impl Ciphertext {
1699    /// Returns, if known, the public-key algorithm for this
1700    /// ciphertext.
1701    pub fn pk_algo(&self) -> Option<PublicKeyAlgorithm> {
1702        use self::Ciphertext::*;
1703
1704        // Fields are mostly MPIs that consist of two octets length
1705        // plus the big endian value itself. All other field types are
1706        // commented.
1707        #[allow(deprecated)]
1708        match self {
1709            RSA { .. } => Some(PublicKeyAlgorithm::RSAEncryptSign),
1710            ElGamal { .. } => Some(PublicKeyAlgorithm::ElGamalEncrypt),
1711            ECDH { .. } => Some(PublicKeyAlgorithm::ECDH),
1712            X25519 { .. } => Some(PublicKeyAlgorithm::X25519),
1713            X448 { .. } => Some(PublicKeyAlgorithm::X448),
1714            MLKEM768_X25519 { .. } => Some(PublicKeyAlgorithm::MLKEM768_X25519),
1715            MLKEM1024_X448 { .. } => Some(PublicKeyAlgorithm::MLKEM1024_X448),
1716            Unknown { .. } => None,
1717        }
1718    }
1719}
1720
1721impl Hash for Ciphertext {
1722    fn hash(&self, mut hash: &mut hash::Context) -> Result<()> {
1723        self.serialize(&mut hash as &mut dyn Write)
1724    }
1725}
1726
1727#[cfg(test)]
1728impl Arbitrary for Ciphertext {
1729    fn arbitrary(g: &mut Gen) -> Self {
1730        use crate::arbitrary_helper::gen_arbitrary_from_range;
1731
1732        match gen_arbitrary_from_range(0..7, g) {
1733            0 => Ciphertext::RSA {
1734                c: MPI::arbitrary(g),
1735            },
1736
1737            1 => Ciphertext::ElGamal {
1738                e: MPI::arbitrary(g),
1739                c: MPI::arbitrary(g)
1740            },
1741
1742            2 => Ciphertext::ECDH {
1743                e: MPI::arbitrary(g),
1744                key: {
1745                    let mut k = <Vec<u8>>::arbitrary(g);
1746                    k.truncate(255);
1747                    k.into_boxed_slice()
1748                },
1749            },
1750
1751            3 => Ciphertext::X25519 {
1752                e: Box::new(arbitrary(g)),
1753                key: {
1754                    let mut k = <Vec<u8>>::arbitrary(g);
1755                    k.truncate(255);
1756                    k.into_boxed_slice()
1757                },
1758            },
1759
1760            4 => Ciphertext::X448 {
1761                e: Box::new(arbitrarize(g, [0; 56])),
1762                key: {
1763                    let mut k = <Vec<u8>>::arbitrary(g);
1764                    k.truncate(255);
1765                    k.into_boxed_slice()
1766                },
1767            },
1768
1769            5 => Ciphertext::MLKEM768_X25519 {
1770                ecdh: Box::new(arbitrarize(g, [0; 32])),
1771                mlkem: Box::new(arbitrarize(g, [0; 1088])),
1772                esk: {
1773                    let mut k = <Vec<u8>>::arbitrary(g);
1774                    k.truncate(255);
1775                    k.into_boxed_slice()
1776                },
1777            },
1778
1779            6 => Ciphertext::MLKEM1024_X448 {
1780                ecdh: Box::new(arbitrarize(g, [0; 56])),
1781                mlkem: Box::new(arbitrarize(g, [0; 1568])),
1782                esk: {
1783                    let mut k = <Vec<u8>>::arbitrary(g);
1784                    k.truncate(255);
1785                    k.into_boxed_slice()
1786                },
1787            },
1788
1789            _ => unreachable!(),
1790        }
1791    }
1792}
1793
1794/// A cryptographic signature.
1795///
1796/// Provides a typed and structured way of storing multiple MPIs in
1797/// [`Signature`] packets.
1798///
1799///   [`Signature`]: crate::packet::Signature
1800#[non_exhaustive]
1801#[allow(non_camel_case_types)]
1802#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1803pub enum Signature {
1804    /// RSA signature.
1805    RSA {
1806        /// Signature m^d mod N.
1807        s: MPI,
1808    },
1809
1810    /// NIST's DSA signature.
1811    DSA {
1812        /// `r` value.
1813        r: MPI,
1814        /// `s` value.
1815        s: MPI,
1816    },
1817
1818    /// ElGamal signature.
1819    ElGamal {
1820        /// `r` value.
1821        r: MPI,
1822        /// `s` value.
1823        s: MPI,
1824    },
1825
1826    /// DJB's "Twisted" Edwards curve DSA signature.
1827    EdDSA {
1828        /// `r` value.
1829        r: MPI,
1830        /// `s` value.
1831        s: MPI,
1832    },
1833
1834    /// NIST's Elliptic curve DSA signature.
1835    ECDSA {
1836        /// `r` value.
1837        r: MPI,
1838        /// `s` value.
1839        s: MPI,
1840    },
1841
1842    /// Ed25519 signature.
1843    Ed25519 {
1844        /// The signature.
1845        s: Box<[u8; 64]>,
1846    },
1847
1848    /// Ed448 signature.
1849    Ed448 {
1850        /// The signature.
1851        s: Box<[u8; 114]>,
1852    },
1853
1854    /// Composite signature algorithm using ML-DSA-65 and Ed25519.
1855    MLDSA65_Ed25519 {
1856        /// The Ed25519 signature, an opaque string.
1857        eddsa: Box<[u8; 64]>,
1858
1859        /// The ML-DSA signature, an opaque string.
1860        mldsa: Box<[u8; 3309]>,
1861    },
1862
1863    /// Composite signature algorithm using ML-DSA-87 and Ed448.
1864    MLDSA87_Ed448 {
1865        /// The Ed448 signature, an opaque string.
1866        eddsa: Box<[u8; 114]>,
1867
1868        /// The ML-DSA signature, an opaque string.
1869        mldsa: Box<[u8; 4627]>,
1870    },
1871
1872    /// SLH-DSA-SHAKE-128s signature.
1873    ///
1874    /// Note: [RFC 9980 only specifies the SHAKE variant of the
1875    /// SLH-DSA
1876    /// algorithm](https://www.rfc-editor.org/rfc/rfc9980.html#name-supported-public-key-algori).
1877    SLHDSA128s {
1878        /// The signature, an opaque string.
1879        sig: Box<[u8; 7856]>,
1880    },
1881
1882    /// SLH-DSA-SHAKE-128f signature.
1883    ///
1884    /// Note: [RFC 9980 only specifies the SHAKE variant of the
1885    /// SLH-DSA
1886    /// algorithm](https://www.rfc-editor.org/rfc/rfc9980.html#name-supported-public-key-algori).
1887    SLHDSA128f {
1888        /// The signature, an opaque string.
1889        sig: Box<[u8; 17088]>,
1890    },
1891
1892    /// SLH-DSA-SHAKE-256s signature.
1893    ///
1894    /// Note: [RFC 9980 only specifies the SHAKE variant of the
1895    /// SLH-DSA
1896    /// algorithm](https://www.rfc-editor.org/rfc/rfc9980.html#name-supported-public-key-algori).
1897    SLHDSA256s {
1898        /// The signature, an opaque string.
1899        sig: Box<[u8; 29792]>,
1900    },
1901
1902    /// Unknown number of MPIs for an unknown algorithm.
1903    Unknown {
1904        /// The successfully parsed MPIs.
1905        mpis: Box<[MPI]>,
1906        /// Any data that failed to parse.
1907        rest: Box<[u8]>,
1908    },
1909}
1910assert_send_and_sync!(Signature);
1911
1912impl fmt::Debug for Signature {
1913    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1914        match self {
1915            Signature::RSA { s } =>
1916                f.debug_struct("RSA")
1917                .field("s", s)
1918                .finish(),
1919
1920            Signature::DSA { r, s } =>
1921                f.debug_struct("DSA")
1922                .field("r", r)
1923                .field("s", s)
1924                .finish(),
1925
1926            Signature::ElGamal { r, s } =>
1927                f.debug_struct("ElGamal")
1928                .field("r", r)
1929                .field("s", s)
1930                .finish(),
1931
1932            Signature::EdDSA { r, s } =>
1933                f.debug_struct("EdDSA")
1934                .field("r", r)
1935                .field("s", s)
1936                .finish(),
1937
1938            Signature::ECDSA { r, s } =>
1939                f.debug_struct("ECDSA")
1940                .field("r", r)
1941                .field("s", s)
1942                .finish(),
1943
1944            Signature::Ed25519 { s } =>
1945                f.debug_struct("Ed25519")
1946                .field("s", &hex::encode(&s[..]))
1947                .finish(),
1948
1949            Signature::Ed448 { s } =>
1950                f.debug_struct("Ed448")
1951                .field("s", &hex::encode(&s[..]))
1952                .finish(),
1953
1954            Signature::MLDSA65_Ed25519 { eddsa, mldsa } =>
1955                f.debug_struct("MLDSA65_Ed25519")
1956                .field("eddsa", &hex::encode(&eddsa[..]))
1957                .field("mldsa", &hex::encode(&mldsa[..]))
1958                .finish(),
1959
1960            Signature::MLDSA87_Ed448 { eddsa, mldsa } =>
1961                f.debug_struct("MLDSA87_Ed448")
1962                .field("eddsa", &hex::encode(&eddsa[..]))
1963                .field("mldsa", &hex::encode(&mldsa[..]))
1964                .finish(),
1965
1966            Signature::SLHDSA128s { sig } =>
1967                f.debug_struct("SLHDSA128s")
1968                .field("sig", &hex::encode(sig.as_ref()))
1969                .finish(),
1970
1971            Signature::SLHDSA128f { sig } =>
1972                f.debug_struct("SLHDSA128f")
1973                .field("sig", &hex::encode(sig.as_ref()))
1974                .finish(),
1975
1976            Signature::SLHDSA256s { sig } =>
1977                f.debug_struct("SLHDSA256s")
1978                .field("sig", &hex::encode(sig.as_ref()))
1979                .finish(),
1980
1981            Signature::Unknown { mpis, rest } =>
1982                f.debug_struct("Unknown")
1983                .field("mpis", mpis)
1984                .field("rest", &hex::encode(rest))
1985                .finish(),
1986        }
1987    }
1988}
1989
1990impl Hash for Signature {
1991    fn hash(&self, mut hash: &mut hash::Context) -> Result<()> {
1992        self.serialize(&mut hash as &mut dyn Write)
1993    }
1994}
1995
1996#[cfg(test)]
1997impl Arbitrary for Signature {
1998    fn arbitrary(g: &mut Gen) -> Self {
1999        use crate::arbitrary_helper::gen_arbitrary_from_range;
2000
2001        match gen_arbitrary_from_range(0..8, g) {
2002            0 => Signature::RSA  {
2003                s: MPI::arbitrary(g),
2004            },
2005
2006            1 => Signature::DSA {
2007                r: MPI::arbitrary(g),
2008                s: MPI::arbitrary(g),
2009            },
2010
2011            2 => Signature::EdDSA  {
2012                r: MPI::arbitrary(g),
2013                s: MPI::arbitrary(g),
2014            },
2015
2016            3 => Signature::ECDSA  {
2017                r: MPI::arbitrary(g),
2018                s: MPI::arbitrary(g),
2019            },
2020
2021            4 => Signature::Ed25519  {
2022                s: Box::new(arbitrarize(g, [0; 64])),
2023            },
2024
2025            5 => Signature::Ed448  {
2026                s: Box::new(arbitrarize(g, [0; 114])),
2027            },
2028
2029            6 => Signature::MLDSA65_Ed25519  {
2030                eddsa: Box::new(arbitrarize(g, [0; 64])),
2031                mldsa: Box::new(arbitrarize(g, [0; 3309])),
2032            },
2033
2034            7 => Signature::MLDSA87_Ed448  {
2035                eddsa: Box::new(arbitrarize(g, [0; 114])),
2036                mldsa: Box::new(arbitrarize(g, [0; 4627])),
2037            },
2038
2039            _ => unreachable!(),
2040        }
2041    }
2042}
2043
2044#[cfg(test)]
2045mod tests {
2046    use super::*;
2047    use crate::parse::Parse;
2048
2049    quickcheck! {
2050        fn mpi_roundtrip(mpi: MPI) -> bool {
2051            let mut buf = Vec::new();
2052            mpi.serialize(&mut buf).unwrap();
2053            MPI::from_bytes(&buf).unwrap() == mpi
2054        }
2055    }
2056
2057    quickcheck! {
2058        fn pk_roundtrip(pk: PublicKey) -> bool {
2059            use std::io::Cursor;
2060
2061            let mut buf = Vec::new();
2062            pk.serialize(&mut buf).unwrap();
2063            let cur = Cursor::new(buf);
2064            let pk_ = PublicKey::parse(pk.algo().unwrap(), cur).unwrap();
2065
2066            pk == pk_
2067        }
2068    }
2069
2070    #[test]
2071    fn pk_bits() {
2072        for (name, key_no, bits) in &[
2073            ("testy.pgp", 0, 2048),
2074            ("testy-new.pgp", 1, 256),
2075            ("dennis-simon-anton.pgp", 0, 2048),
2076            ("dsa2048-elgamal3072.pgp", 1, 3072),
2077            ("emmelie-dorothea-dina-samantha-awina-ed25519.pgp", 0, 256),
2078            ("erika-corinna-daniela-simone-antonia-nistp256.pgp", 0, 256),
2079            ("erika-corinna-daniela-simone-antonia-nistp384.pgp", 0, 384),
2080            ("erika-corinna-daniela-simone-antonia-nistp521.pgp", 0, 521),
2081        ] {
2082            let cert = crate::Cert::from_bytes(crate::tests::key(name)).unwrap();
2083            let ka = cert.keys().nth(*key_no).unwrap();
2084            assert_eq!(ka.key().mpis().bits().unwrap(), *bits,
2085                       "Cert {}, key no {}", name, *key_no);
2086        }
2087    }
2088
2089    quickcheck! {
2090        fn sk_roundtrip(sk: SecretKeyMaterial) -> bool {
2091            let mut buf = Vec::new();
2092            sk.serialize(&mut buf).unwrap();
2093            let sk_ =
2094                SecretKeyMaterial::from_bytes(sk.algo().unwrap(),
2095                                              &buf).unwrap();
2096
2097            sk == sk_
2098        }
2099    }
2100
2101    quickcheck! {
2102        fn ct_roundtrip(ct: Ciphertext) -> bool {
2103            use std::io::Cursor;
2104
2105            let mut buf = Vec::new();
2106            ct.serialize(&mut buf).unwrap();
2107            let cur = Cursor::new(buf);
2108            let ct_ = Ciphertext::parse(ct.pk_algo().unwrap(), cur).unwrap();
2109
2110            ct == ct_
2111        }
2112    }
2113
2114    quickcheck! {
2115        fn signature_roundtrip(sig: Signature) -> bool {
2116            use std::io::Cursor;
2117            use crate::PublicKeyAlgorithm::*;
2118
2119            let mut buf = Vec::new();
2120            sig.serialize(&mut buf).unwrap();
2121            let cur = Cursor::new(buf);
2122
2123            #[allow(deprecated)]
2124            let sig_ = match &sig {
2125                Signature::RSA { .. } =>
2126                    Signature::parse(RSAEncryptSign, cur).unwrap(),
2127                Signature::DSA { .. } =>
2128                    Signature::parse(DSA, cur).unwrap(),
2129                Signature::ElGamal { .. } =>
2130                    Signature::parse(ElGamalEncryptSign, cur).unwrap(),
2131                Signature::EdDSA { .. } =>
2132                    Signature::parse(EdDSA, cur).unwrap(),
2133                Signature::ECDSA { .. } =>
2134                    Signature::parse(ECDSA, cur).unwrap(),
2135                Signature::Ed25519 { .. } =>
2136                    Signature::parse(Ed25519, cur).unwrap(),
2137                Signature::Ed448 { .. } =>
2138                    Signature::parse(Ed448, cur).unwrap(),
2139
2140                Signature::MLDSA65_Ed25519 { .. } =>
2141                    Signature::parse(MLDSA65_Ed25519, cur).unwrap(),
2142                Signature::MLDSA87_Ed448 { .. } =>
2143                    Signature::parse(MLDSA87_Ed448, cur).unwrap(),
2144
2145                Signature::SLHDSA128s { .. } =>
2146                    Signature::parse(SLHDSA128s, cur).unwrap(),
2147                Signature::SLHDSA128f { .. } =>
2148                    Signature::parse(SLHDSA128f, cur).unwrap(),
2149                Signature::SLHDSA256s { .. } =>
2150                    Signature::parse(SLHDSA256s, cur).unwrap(),
2151
2152                Signature::Unknown { .. } => unreachable!(),
2153            };
2154
2155            sig == sig_
2156        }
2157    }
2158}