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::types::{
26    Curve,
27    HashAlgorithm,
28    PublicKeyAlgorithm,
29    SymmetricAlgorithm,
30};
31use crate::crypto::hash::{self, Hash};
32use crate::crypto::mem::{secure_cmp, Protected};
33use crate::serialize::Marshal;
34
35use crate::Error;
36use crate::Result;
37
38/// A Multiprecision Integer.
39#[derive(Clone)]
40pub struct MPI {
41    /// Integer value as big-endian with leading zeros stripped.
42    value: Box<[u8]>,
43}
44assert_send_and_sync!(MPI);
45
46impl From<Vec<u8>> for MPI {
47    fn from(v: Vec<u8>) -> Self {
48        // XXX: This will leak secrets in v into the heap.  But,
49        // eagerly clearing the memory may have a very high overhead,
50        // after all, most MPIs that we encounter will not contain
51        // secrets.  I think it is better to avoid creating MPIs that
52        // contain secrets in the first place.  In 2.0, we can remove
53        // the impl From<MPI> for ProtectedMPI.
54        Self::new(&v)
55    }
56}
57
58impl From<Box<[u8]>> for MPI {
59    fn from(v: Box<[u8]>) -> Self {
60        // XXX: This will leak secrets in v into the heap.  But,
61        // eagerly clearing the memory may have a very high overhead,
62        // after all, most MPIs that we encounter will not contain
63        // secrets.  I think it is better to avoid creating MPIs that
64        // contain secrets in the first place.  In 2.0, we can remove
65        // the impl From<MPI> for ProtectedMPI.
66        Self::new(&v)
67    }
68}
69
70impl MPI {
71    /// Trims leading zero octets.
72    fn trim_leading_zeros(v: &[u8]) -> &[u8] {
73        let offset = v.iter().take_while(|&&o| o == 0).count();
74        &v[offset..]
75    }
76
77    /// Creates a new MPI.
78    ///
79    /// This function takes care of removing leading zeros.
80    pub fn new(value: &[u8]) -> Self {
81        let value = Self::trim_leading_zeros(value).to_vec().into_boxed_slice();
82
83        MPI {
84            value,
85        }
86    }
87
88    /// Creates new MPI encoding an uncompressed EC point.
89    ///
90    /// Encodes the given point on an elliptic curve (see [Section 6 of
91    /// RFC 6637] for details).  This is used to encode public keys
92    /// and ciphertexts for the NIST curves (`NistP256`, `NistP384`,
93    /// and `NistP521`).
94    ///
95    ///   [Section 6 of RFC 6637]: https://tools.ietf.org/html/rfc6637#section-6
96    pub fn new_point(x: &[u8], y: &[u8], field_bits: usize) -> Self {
97        Self::new_point_common(x, y, field_bits).into()
98    }
99
100    /// Common implementation shared between MPI and ProtectedMPI.
101    fn new_point_common(x: &[u8], y: &[u8], field_bits: usize) -> Vec<u8> {
102        let field_sz = if field_bits % 8 > 0 { 1 } else { 0 } + field_bits / 8;
103        let mut val = vec![0x0u8; 1 + 2 * field_sz];
104        let x_missing = field_sz - x.len();
105        let y_missing = field_sz - y.len();
106
107        val[0] = 0x4;
108        val[1 + x_missing..1 + field_sz].copy_from_slice(x);
109        val[1 + field_sz + y_missing..].copy_from_slice(y);
110        val
111    }
112
113    /// Creates new MPI encoding a compressed EC point using native
114    /// encoding.
115    ///
116    /// Encodes the given point on an elliptic curve (see [Section 13.2
117    /// of RFC4880bis] for details).  This is used to encode public
118    /// keys and ciphertexts for the Bernstein curves (currently
119    /// `X25519`).
120    ///
121    ///   [Section 13.2 of RFC4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-09#section-13.2
122    pub fn new_compressed_point(x: &[u8]) -> Self {
123        Self::new_compressed_point_common(x).into()
124    }
125
126    /// Common implementation shared between MPI and ProtectedMPI.
127    fn new_compressed_point_common(x: &[u8]) -> Vec<u8> {
128        let mut val = vec![0; 1 + x.len()];
129        val[0] = 0x40;
130        val[1..].copy_from_slice(x);
131        val
132    }
133
134    /// Creates a new MPI representing zero.
135    pub fn zero() -> Self {
136        Self::new(&[])
137    }
138
139    /// Tests whether the MPI represents zero.
140    pub fn is_zero(&self) -> bool {
141        self.value().is_empty()
142    }
143
144    /// Returns the length of the MPI in bits.
145    ///
146    /// Leading zero-bits are not included in the returned size.
147    pub fn bits(&self) -> usize {
148        self.value.len() * 8
149            - self.value.get(0).map(|&b| b.leading_zeros() as usize)
150                  .unwrap_or(0)
151    }
152
153    /// Returns the value of this MPI.
154    ///
155    /// Note that due to stripping of zero-bytes, the returned value
156    /// may be shorter than expected.
157    pub fn value(&self) -> &[u8] {
158        &self.value
159    }
160
161    /// Returns the value of this MPI zero-padded to the given length.
162    ///
163    /// MPI-encoding strips leading zero-bytes.  This function adds
164    /// them back, if necessary.  If the size exceeds `to`, an error
165    /// is returned.
166    pub fn value_padded(&self, to: usize) -> Result<Cow<[u8]>> {
167        crate::crypto::pad(self.value(), to)
168    }
169
170    /// Decodes an EC point encoded as MPI.
171    ///
172    /// Decodes the MPI into a point on an elliptic curve (see
173    /// [Section 6 of RFC 6637] and [Section 13.2 of RFC4880bis] for
174    /// details).  If the point is not compressed, the function
175    /// returns `(x, y)`.  If it is compressed, `y` will be empty.
176    ///
177    ///   [Section 6 of RFC 6637]: https://tools.ietf.org/html/rfc6637#section-6
178    ///   [Section 13.2 of RFC4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-09#section-13.2
179    ///
180    /// # Errors
181    ///
182    /// Returns `Error::UnsupportedEllipticCurve` if the curve is not
183    /// supported, `Error::MalformedMPI` if the point is formatted
184    /// incorrectly, `Error::InvalidOperation` if the given curve is
185    /// operating on native octet strings.
186    pub fn decode_point(&self, curve: &Curve) -> Result<(&[u8], &[u8])> {
187        Self::decode_point_common(self.value(), curve)
188    }
189
190    /// Common implementation shared between MPI and ProtectedMPI.
191    fn decode_point_common<'a>(value: &'a [u8], curve: &Curve)
192                               -> Result<(&'a [u8], &'a [u8])> {
193        const ED25519_KEY_SIZE: usize = 32;
194        const CURVE25519_SIZE: usize = 32;
195        use self::Curve::*;
196        match &curve {
197            Ed25519 | Cv25519 => {
198                assert_eq!(CURVE25519_SIZE, ED25519_KEY_SIZE);
199                // This curve uses a custom compression format which
200                // only contains the X coordinate.
201                if value.len() != 1 + CURVE25519_SIZE {
202                    return Err(Error::MalformedMPI(
203                        format!("Bad size of Curve25519 key: {} expected: {}",
204                                value.len(),
205                                1 + CURVE25519_SIZE
206                        )
207                    ).into());
208                }
209
210                if value.get(0).map(|&b| b != 0x40).unwrap_or(true) {
211                    return Err(Error::MalformedMPI(
212                        "Bad encoding of Curve25519 key".into()).into());
213                }
214
215                Ok((&value[1..], &[]))
216            },
217
218            NistP256
219                | NistP384
220                | NistP521
221                | BrainpoolP256
222                | BrainpoolP384
223                | BrainpoolP512
224                =>
225            {
226                // Length of one coordinate in bytes, rounded up.
227                let coordinate_length = curve.field_size()?;
228
229                // Check length of Q.
230                let expected_length =
231                    1 // 0x04.
232                    + (2 // (x, y)
233                       * coordinate_length);
234
235                if value.len() != expected_length {
236                    return Err(Error::MalformedMPI(
237                        format!("Invalid length of MPI: {} (expected {})",
238                                value.len(), expected_length)).into());
239                }
240
241                if value.get(0).map(|&b| b != 0x04).unwrap_or(true) {
242                    return Err(Error::MalformedMPI(
243                        format!("Bad prefix: {:?} (expected Some(0x04))",
244                                value.get(0))).into());
245                }
246
247                Ok((&value[1..1 + coordinate_length],
248                    &value[1 + coordinate_length..]))
249            },
250
251            Unknown(_) =>
252                Err(Error::UnsupportedEllipticCurve(curve.clone()).into()),
253        }
254    }
255
256    /// Securely compares two MPIs in constant time.
257    fn secure_memcmp(&self, other: &Self) -> Ordering {
258        let cmp = unsafe {
259            if self.value.len() == other.value.len() {
260                ::memsec::memcmp(self.value.as_ptr(), other.value.as_ptr(),
261                                 other.value.len())
262            } else {
263                self.value.len() as i32 - other.value.len() as i32
264            }
265        };
266
267        match cmp {
268            0 => Ordering::Equal,
269            x if x < 0 => Ordering::Less,
270            _ => Ordering::Greater,
271        }
272    }
273}
274
275impl fmt::Debug for MPI {
276    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
277        f.write_fmt(format_args!(
278            "{} bits: {}", self.bits(),
279            crate::fmt::to_hex(&*self.value, true)))
280    }
281}
282
283impl Hash for MPI {
284    fn hash(&self, hash: &mut hash::Context) -> Result<()> {
285        let len = self.bits() as u16;
286
287        hash.update(&len.to_be_bytes());
288        hash.update(&self.value);
289        Ok(())
290    }
291}
292
293#[cfg(test)]
294impl Arbitrary for MPI {
295    fn arbitrary(g: &mut Gen) -> Self {
296        loop {
297            let buf = <Vec<u8>>::arbitrary(g);
298
299            if !buf.is_empty() && buf[0] != 0 {
300                break MPI::new(&buf);
301            }
302        }
303    }
304}
305
306impl PartialOrd for MPI {
307    fn partial_cmp(&self, other: &MPI) -> Option<Ordering> {
308        Some(self.cmp(other))
309    }
310}
311
312impl Ord for MPI {
313    fn cmp(&self, other: &MPI) -> Ordering {
314        self.secure_memcmp(other)
315    }
316}
317
318impl PartialEq for MPI {
319    fn eq(&self, other: &MPI) -> bool {
320        self.cmp(other) == Ordering::Equal
321    }
322}
323
324impl Eq for MPI {}
325
326impl std::hash::Hash for MPI {
327    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
328        self.value.hash(state);
329    }
330}
331
332/// Holds a single MPI containing secrets.
333///
334/// The memory will be cleared when the object is dropped.  Used by
335/// [`SecretKeyMaterial`] to protect secret keys.
336///
337#[derive(Clone)]
338pub struct ProtectedMPI {
339    /// Integer value as big-endian.
340    value: Protected,
341}
342assert_send_and_sync!(ProtectedMPI);
343
344impl From<&[u8]> for ProtectedMPI {
345    fn from(m: &[u8]) -> Self {
346        let value = Protected::from(MPI::trim_leading_zeros(m));
347        ProtectedMPI {
348            value,
349        }
350    }
351}
352
353impl From<Vec<u8>> for ProtectedMPI {
354    fn from(m: Vec<u8>) -> Self {
355        let value = Protected::from(MPI::trim_leading_zeros(&m));
356        drop(Protected::from(m)); // Erase source.
357        ProtectedMPI {
358            value,
359        }
360    }
361}
362
363impl From<Box<[u8]>> for ProtectedMPI {
364    fn from(m: Box<[u8]>) -> Self {
365        let value = Protected::from(MPI::trim_leading_zeros(&m));
366        drop(Protected::from(m)); // Erase source.
367        ProtectedMPI {
368            value,
369        }
370    }
371}
372
373impl From<Protected> for ProtectedMPI {
374    fn from(m: Protected) -> Self {
375        let value = Protected::from(MPI::trim_leading_zeros(&m));
376        drop(m); // Erase source.
377        ProtectedMPI {
378            value,
379        }
380    }
381}
382
383impl PartialOrd for ProtectedMPI {
384    fn partial_cmp(&self, other: &ProtectedMPI) -> Option<Ordering> {
385        Some(self.cmp(other))
386    }
387}
388
389impl Ord for ProtectedMPI {
390    fn cmp(&self, other: &ProtectedMPI) -> Ordering {
391        self.secure_memcmp(other)
392    }
393}
394
395impl PartialEq for ProtectedMPI {
396    fn eq(&self, other: &ProtectedMPI) -> bool {
397        self.cmp(other) == Ordering::Equal
398    }
399}
400
401impl Eq for ProtectedMPI {}
402
403impl std::hash::Hash for ProtectedMPI {
404    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
405        self.value.hash(state);
406    }
407}
408
409#[cfg(test)]
410impl Arbitrary for ProtectedMPI {
411    fn arbitrary(g: &mut Gen) -> Self {
412        loop {
413            let buf = <Vec<u8>>::arbitrary(g);
414
415            if ! buf.is_empty() && buf[0] != 0 {
416                break ProtectedMPI::from(buf);
417            }
418        }
419    }
420}
421
422impl ProtectedMPI {
423    /// Creates new MPI encoding an uncompressed EC point.
424    ///
425    /// Encodes the given point on an elliptic curve (see [Section 6 of
426    /// RFC 6637] for details).  This is used to encode public keys
427    /// and ciphertexts for the NIST curves (`NistP256`, `NistP384`,
428    /// and `NistP521`).
429    ///
430    ///   [Section 6 of RFC 6637]: https://tools.ietf.org/html/rfc6637#section-6
431    pub fn new_point(x: &[u8], y: &[u8], field_bits: usize) -> Self {
432        MPI::new_point_common(x, y, field_bits).into()
433    }
434
435    /// Creates new MPI encoding a compressed EC point using native
436    /// encoding.
437    ///
438    /// Encodes the given point on an elliptic curve (see [Section 13.2
439    /// of RFC4880bis] for details).  This is used to encode public
440    /// keys and ciphertexts for the Bernstein curves (currently
441    /// `X25519`).
442    ///
443    ///   [Section 13.2 of RFC4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-09#section-13.2
444    pub fn new_compressed_point(x: &[u8]) -> Self {
445        MPI::new_compressed_point_common(x).into()
446    }
447
448    /// Returns the length of the MPI in bits.
449    ///
450    /// Leading zero-bits are not included in the returned size.
451    pub fn bits(&self) -> usize {
452        self.value.len() * 8
453            - self.value.get(0).map(|&b| b.leading_zeros() as usize)
454                  .unwrap_or(0)
455    }
456
457    /// Returns the value of this MPI.
458    ///
459    /// Note that due to stripping of zero-bytes, the returned value
460    /// may be shorter than expected.
461    pub fn value(&self) -> &[u8] {
462        &self.value
463    }
464
465    /// Returns the value of this MPI zero-padded to the given length.
466    ///
467    /// MPI-encoding strips leading zero-bytes.  This function adds
468    /// them back.  This operation is done unconditionally to avoid
469    /// timing differences.  If the size exceeds `to`, the result is
470    /// silently truncated to avoid timing differences.
471    pub fn value_padded(&self, to: usize) -> Protected {
472        let missing = to.saturating_sub(self.value.len());
473        let limit = self.value.len().min(to);
474        let mut v: Protected = vec![0; to].into();
475        v[missing..].copy_from_slice(&self.value()[..limit]);
476        v
477    }
478
479    /// Decodes an EC point encoded as MPI.
480    ///
481    /// Decodes the MPI into a point on an elliptic curve (see
482    /// [Section 6 of RFC 6637] and [Section 13.2 of RFC4880bis] for
483    /// details).  If the point is not compressed, the function
484    /// returns `(x, y)`.  If it is compressed, `y` will be empty.
485    ///
486    ///   [Section 6 of RFC 6637]: https://tools.ietf.org/html/rfc6637#section-6
487    ///   [Section 13.2 of RFC4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-09#section-13.2
488    ///
489    /// # Errors
490    ///
491    /// Returns `Error::UnsupportedEllipticCurve` if the curve is not
492    /// supported, `Error::MalformedMPI` if the point is formatted
493    /// incorrectly, `Error::InvalidOperation` if the given curve is
494    /// operating on native octet strings.
495    pub fn decode_point(&self, curve: &Curve) -> Result<(&[u8], &[u8])> {
496        MPI::decode_point_common(self.value(), curve)
497    }
498
499    /// Securely compares two MPIs in constant time.
500    fn secure_memcmp(&self, other: &Self) -> Ordering {
501        (self.value.len() as i32).cmp(&(other.value.len() as i32))
502            .then(
503                // Protected compares in constant time.
504                self.value.cmp(&other.value))
505    }
506}
507
508impl fmt::Debug for ProtectedMPI {
509    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
510        if cfg!(debug_assertions) {
511            f.write_fmt(format_args!(
512                "{} bits: {}", self.bits(),
513                crate::fmt::to_hex(&*self.value, true)))
514        } else {
515            f.write_str("<Redacted>")
516        }
517    }
518}
519
520/// A public key.
521///
522/// Provides a typed and structured way of storing multiple MPIs (and
523/// the occasional elliptic curve) in [`Key`] packets.
524///
525///   [`Key`]: crate::packet::Key
526#[non_exhaustive]
527#[derive(Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
528pub enum PublicKey {
529    /// RSA public key.
530    RSA {
531        /// Public exponent
532        e: MPI,
533        /// Public modulo N = pq.
534        n: MPI,
535    },
536
537    /// NIST DSA public key.
538    DSA {
539        /// Prime of the ring Zp.
540        p: MPI,
541        /// Order of `g` in Zp.
542        q: MPI,
543        /// Public generator of Zp.
544        g: MPI,
545        /// Public key g^x mod p.
546        y: MPI,
547    },
548
549    /// ElGamal public key.
550    ElGamal {
551        /// Prime of the ring Zp.
552        p: MPI,
553        /// Generator of Zp.
554        g: MPI,
555        /// Public key g^x mod p.
556        y: MPI,
557    },
558
559    /// DJB's "Twisted" Edwards curve DSA public key.
560    EdDSA {
561        /// Curve we're using. Must be curve 25519.
562        curve: Curve,
563        /// Public point.
564        q: MPI,
565    },
566
567    /// NIST's Elliptic Curve DSA public key.
568    ECDSA {
569        /// Curve we're using.
570        curve: Curve,
571        /// Public point.
572        q: MPI,
573    },
574
575    /// Elliptic Curve Diffie-Hellman public key.
576    ECDH {
577        /// Curve we're using.
578        curve: Curve,
579        /// Public point.
580        q: MPI,
581        /// Algorithm used to derive the Key Encapsulation Key.
582        hash: HashAlgorithm,
583        /// Algorithm used to encapsulate the session key.
584        sym: SymmetricAlgorithm,
585    },
586
587    /// X25519 public key.
588    X25519 {
589        /// The public key, an opaque string.
590        u: [u8; 32],
591    },
592
593    /// X448 public key.
594    X448 {
595        /// The public key, an opaque string.
596        u: Box<[u8; 56]>,
597    },
598
599    /// Ed25519 public key.
600    Ed25519 {
601        /// The public key, an opaque string.
602        a: [u8; 32],
603    },
604
605    /// Ed448 public key.
606    Ed448 {
607        /// The public key, an opaque string.
608        a: Box<[u8; 57]>,
609    },
610
611    /// Unknown number of MPIs for an unknown algorithm.
612    Unknown {
613        /// The successfully parsed MPIs.
614        mpis: Box<[MPI]>,
615        /// Any data that failed to parse.
616        rest: Box<[u8]>,
617    },
618}
619assert_send_and_sync!(PublicKey);
620
621impl PublicKey {
622    /// Returns the length of the public key in bits.
623    ///
624    /// For finite field crypto this returns the size of the field we
625    /// operate in, for ECC it returns `Curve::bits()`.
626    ///
627    /// Note: This information is useless and should not be used to
628    /// gauge the security of a particular key. This function exists
629    /// only because some legacy PGP application like HKP need it.
630    ///
631    /// Returns `None` for unknown keys and curves.
632    pub fn bits(&self) -> Option<usize> {
633        use self::PublicKey::*;
634        match self {
635            RSA { ref n,.. } => Some(n.bits()),
636            DSA { ref p,.. } => Some(p.bits()),
637            ElGamal { ref p,.. } => Some(p.bits()),
638            EdDSA { ref curve,.. } => curve.bits().ok(),
639            ECDSA { ref curve,.. } => curve.bits().ok(),
640            ECDH { ref curve,.. } => curve.bits().ok(),
641            X25519 { .. } => Some(256),
642            X448 { .. } => Some(448),
643            Ed25519 { .. } => Some(256),
644            Ed448 { .. } => Some(456),
645            Unknown { .. } => None,
646        }
647    }
648
649    /// Returns, if known, the public-key algorithm for this public
650    /// key.
651    pub fn algo(&self) -> Option<PublicKeyAlgorithm> {
652        use self::PublicKey::*;
653        #[allow(deprecated)]
654        match self {
655            RSA { .. } => Some(PublicKeyAlgorithm::RSAEncryptSign),
656            DSA { .. } => Some(PublicKeyAlgorithm::DSA),
657            ElGamal { .. } => Some(PublicKeyAlgorithm::ElGamalEncrypt),
658            EdDSA { .. } => Some(PublicKeyAlgorithm::EdDSA),
659            ECDSA { .. } => Some(PublicKeyAlgorithm::ECDSA),
660            ECDH { .. } => Some(PublicKeyAlgorithm::ECDH),
661            X25519 { .. } => Some(PublicKeyAlgorithm::X25519),
662            X448 { .. } => Some(PublicKeyAlgorithm::X448),
663            Ed25519 { .. } => Some(PublicKeyAlgorithm::Ed25519),
664            Ed448 { .. } => Some(PublicKeyAlgorithm::Ed448),
665            Unknown { .. } => None,
666        }
667    }
668}
669
670impl Hash for PublicKey {
671    fn hash(&self, mut hash: &mut hash::Context) -> Result<()> {
672        self.serialize(&mut hash as &mut dyn Write)
673    }
674}
675
676#[cfg(test)]
677impl Arbitrary for PublicKey {
678    fn arbitrary(g: &mut Gen) -> Self {
679        use self::PublicKey::*;
680        use crate::arbitrary_helper::gen_arbitrary_from_range;
681
682        match gen_arbitrary_from_range(0..10, g) {
683            0 => RSA {
684                e: MPI::arbitrary(g),
685                n: MPI::arbitrary(g),
686            },
687
688            1 => DSA {
689                p: MPI::arbitrary(g),
690                q: MPI::arbitrary(g),
691                g: MPI::arbitrary(g),
692                y: MPI::arbitrary(g),
693            },
694
695            2 => ElGamal {
696                p: MPI::arbitrary(g),
697                g: MPI::arbitrary(g),
698                y: MPI::arbitrary(g),
699            },
700
701            3 => EdDSA {
702                curve: Curve::arbitrary(g),
703                q: MPI::arbitrary(g),
704            },
705
706            4 => ECDSA {
707                curve: Curve::arbitrary(g),
708                q: MPI::arbitrary(g),
709            },
710
711            5 => ECDH {
712                curve: Curve::arbitrary(g),
713                q: MPI::arbitrary(g),
714                hash: HashAlgorithm::arbitrary(g),
715                sym: SymmetricAlgorithm::arbitrary(g),
716            },
717
718            6 => X25519 { u: arbitrary(g) },
719            7 => X448 { u: Box::new(arbitrarize(g, [0; 56])) },
720            8 => Ed25519 { a: arbitrary(g) },
721            9 => Ed448 { a: Box::new(arbitrarize(g, [0; 57])) },
722
723            _ => unreachable!(),
724        }
725    }
726}
727
728#[cfg(test)]
729pub(crate) fn arbitrarize<T: AsMut<[u8]>>(g: &mut Gen, mut a: T) -> T
730{
731    a.as_mut().iter_mut().for_each(|p| *p = Arbitrary::arbitrary(g));
732    a
733}
734
735#[cfg(test)]
736pub(crate) fn arbitrary<T: Default + AsMut<[u8]>>(g: &mut Gen) -> T
737{
738    arbitrarize(g, Default::default())
739}
740
741
742/// A secret key.
743///
744/// Provides a typed and structured way of storing multiple MPIs in
745/// [`Key`] packets.  Secret key components are protected by storing
746/// them using [`ProtectedMPI`].
747///
748///   [`Key`]: crate::packet::Key
749// Deriving Hash here is okay: PartialEq is manually implemented to
750// ensure that secrets are compared in constant-time.
751#[non_exhaustive]
752#[allow(clippy::derived_hash_with_manual_eq)]
753#[derive(Clone, Hash)]
754pub enum SecretKeyMaterial {
755    /// RSA secret key.
756    RSA {
757        /// Secret exponent, inverse of e in Phi(N).
758        d: ProtectedMPI,
759        /// Smaller secret prime.
760        p: ProtectedMPI,
761        /// Larger secret prime.
762        q: ProtectedMPI,
763        /// Inverse of p mod q.
764        u: ProtectedMPI,
765    },
766
767    /// NIST DSA secret key.
768    DSA {
769        /// Secret key log_g(y) in Zp.
770        x: ProtectedMPI,
771    },
772
773    /// ElGamal secret key.
774    ElGamal {
775        /// Secret key log_g(y) in Zp.
776        x: ProtectedMPI,
777    },
778
779    /// DJB's "Twisted" Edwards curve DSA secret key.
780    EdDSA {
781        /// Secret scalar.
782        scalar: ProtectedMPI,
783    },
784
785    /// NIST's Elliptic Curve DSA secret key.
786    ECDSA {
787        /// Secret scalar.
788        scalar: ProtectedMPI,
789    },
790
791    /// Elliptic Curve Diffie-Hellman secret key.
792    ECDH {
793        /// Secret scalar.
794        scalar: ProtectedMPI,
795    },
796
797    /// X25519 secret key.
798    X25519 {
799        /// The secret key, an opaque string.
800        x: Protected,
801    },
802
803    /// X448 secret key.
804    X448 {
805        /// The secret key, an opaque string.
806        x: Protected,
807    },
808
809    /// Ed25519 secret key.
810    Ed25519 {
811        /// The secret key, an opaque string.
812        x: Protected,
813    },
814
815    /// Ed448 secret key.
816    Ed448 {
817        /// The secret key, an opaque string.
818        x: Protected,
819    },
820
821    /// Unknown number of MPIs for an unknown algorithm.
822    Unknown {
823        /// The successfully parsed MPIs.
824        mpis: Box<[ProtectedMPI]>,
825        /// Any data that failed to parse.
826        rest: Protected,
827    },
828}
829assert_send_and_sync!(SecretKeyMaterial);
830
831impl fmt::Debug for SecretKeyMaterial {
832    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
833        if cfg!(debug_assertions) {
834            match self {
835                SecretKeyMaterial::RSA{ ref d, ref p, ref q, ref u } =>
836                    write!(f, "RSA {{ d: {:?}, p: {:?}, q: {:?}, u: {:?} }}", d, p, q, u),
837                SecretKeyMaterial::DSA{ ref x } =>
838                    write!(f, "DSA {{ x: {:?} }}", x),
839                SecretKeyMaterial::ElGamal{ ref x } =>
840                    write!(f, "ElGamal {{ x: {:?} }}", x),
841                SecretKeyMaterial::EdDSA{ ref scalar } =>
842                    write!(f, "EdDSA {{ scalar: {:?} }}", scalar),
843                SecretKeyMaterial::ECDSA{ ref scalar } =>
844                    write!(f, "ECDSA {{ scalar: {:?} }}", scalar),
845                SecretKeyMaterial::ECDH{ ref scalar } =>
846                    write!(f, "ECDH {{ scalar: {:?} }}", scalar),
847                SecretKeyMaterial::X25519 { x } =>
848                    write!(f, "X25519 {{ x: {:?} }}", x),
849                SecretKeyMaterial::X448 { x } =>
850                    write!(f, "X448 {{ x: {:?} }}", x),
851                SecretKeyMaterial::Ed25519 { x } =>
852                    write!(f, "Ed25519 {{ x: {:?} }}", x),
853                SecretKeyMaterial::Ed448 { x } =>
854                    write!(f, "Ed448 {{ x: {:?} }}", x),
855                SecretKeyMaterial::Unknown{ ref mpis, ref rest } =>
856                    write!(f, "Unknown {{ mips: {:?}, rest: {:?} }}", mpis, rest),
857            }
858        } else {
859            match self {
860                SecretKeyMaterial::RSA{ .. } =>
861                    f.write_str("RSA { <Redacted> }"),
862                SecretKeyMaterial::DSA{ .. } =>
863                    f.write_str("DSA { <Redacted> }"),
864                SecretKeyMaterial::ElGamal{ .. } =>
865                    f.write_str("ElGamal { <Redacted> }"),
866                SecretKeyMaterial::EdDSA{ .. } =>
867                    f.write_str("EdDSA { <Redacted> }"),
868                SecretKeyMaterial::ECDSA{ .. } =>
869                    f.write_str("ECDSA { <Redacted> }"),
870                SecretKeyMaterial::ECDH{ .. } =>
871                    f.write_str("ECDH { <Redacted> }"),
872                SecretKeyMaterial::X25519 { .. } =>
873                    f.write_str("X25519 { <Redacted> }"),
874                SecretKeyMaterial::X448 { .. } =>
875                    f.write_str("X448 { <Redacted> }"),
876                SecretKeyMaterial::Ed25519 { .. } =>
877                    f.write_str("Ed25519 { <Redacted> }"),
878                SecretKeyMaterial::Ed448 { .. } =>
879                    f.write_str("Ed448 { <Redacted> }"),
880                SecretKeyMaterial::Unknown{ .. } =>
881                    f.write_str("Unknown { <Redacted> }"),
882            }
883        }
884    }
885}
886
887impl PartialOrd for SecretKeyMaterial {
888    fn partial_cmp(&self, other: &SecretKeyMaterial) -> Option<Ordering> {
889        Some(self.cmp(other))
890    }
891}
892
893impl Ord for SecretKeyMaterial {
894    fn cmp(&self, other: &Self) -> Ordering {
895        use std::iter;
896
897        fn discriminant(sk: &SecretKeyMaterial) -> usize {
898            match sk {
899                SecretKeyMaterial::RSA{ .. } => 0,
900                SecretKeyMaterial::DSA{ .. } => 1,
901                SecretKeyMaterial::ElGamal{ .. } => 2,
902                SecretKeyMaterial::EdDSA{ .. } => 3,
903                SecretKeyMaterial::ECDSA{ .. } => 4,
904                SecretKeyMaterial::ECDH{ .. } => 5,
905                SecretKeyMaterial::X25519 { .. } => 6,
906                SecretKeyMaterial::X448 { .. } => 7,
907                SecretKeyMaterial::Ed25519 { .. } => 8,
908                SecretKeyMaterial::Ed448 { .. } => 9,
909                SecretKeyMaterial::Unknown { .. } => 10,
910            }
911        }
912
913        let ret = match (self, other) {
914            (&SecretKeyMaterial::RSA{ d: ref d1, p: ref p1, q: ref q1, u: ref u1 }
915            ,&SecretKeyMaterial::RSA{ d: ref d2, p: ref p2, q: ref q2, u: ref u2 }) => {
916                let o1 = d1.cmp(d2);
917                let o2 = p1.cmp(p2);
918                let o3 = q1.cmp(q2);
919                let o4 = u1.cmp(u2);
920
921                if o1 != Ordering::Equal { return o1; }
922                if o2 != Ordering::Equal { return o2; }
923                if o3 != Ordering::Equal { return o3; }
924                o4
925            }
926            (&SecretKeyMaterial::DSA{ x: ref x1 }
927            ,&SecretKeyMaterial::DSA{ x: ref x2 }) => {
928                x1.cmp(x2)
929            }
930            (&SecretKeyMaterial::ElGamal{ x: ref x1 }
931            ,&SecretKeyMaterial::ElGamal{ x: ref x2 }) => {
932                x1.cmp(x2)
933            }
934            (&SecretKeyMaterial::EdDSA{ scalar: ref scalar1 }
935            ,&SecretKeyMaterial::EdDSA{ scalar: ref scalar2 }) => {
936                scalar1.cmp(scalar2)
937            }
938            (&SecretKeyMaterial::ECDSA{ scalar: ref scalar1 }
939            ,&SecretKeyMaterial::ECDSA{ scalar: ref scalar2 }) => {
940                scalar1.cmp(scalar2)
941            }
942            (&SecretKeyMaterial::ECDH{ scalar: ref scalar1 }
943            ,&SecretKeyMaterial::ECDH{ scalar: ref scalar2 }) => {
944                scalar1.cmp(scalar2)
945            }
946            (SecretKeyMaterial::X25519 { x: x0 },
947             SecretKeyMaterial::X25519 { x: x1 }) => x0.cmp(x1),
948            (SecretKeyMaterial::X448 { x: x0 },
949             SecretKeyMaterial::X448 { x: x1 }) => x0.cmp(x1),
950            (SecretKeyMaterial::Ed25519 { x: x0 },
951             SecretKeyMaterial::Ed25519 { x: x1 }) => x0.cmp(x1),
952            (SecretKeyMaterial::Ed448 { x: x0 },
953             SecretKeyMaterial::Ed448 { x: x1 }) => x0.cmp(x1),
954
955            (&SecretKeyMaterial::Unknown{ mpis: ref mpis1, rest: ref rest1 }
956            ,&SecretKeyMaterial::Unknown{ mpis: ref mpis2, rest: ref rest2 }) => {
957                let o1 = secure_cmp(rest1, rest2);
958                let o2 = mpis1.len().cmp(&mpis2.len());
959                let on = mpis1.iter().zip(mpis2.iter()).map(|(a,b)| {
960                    a.cmp(b)
961                }).collect::<Vec<_>>();
962
963                iter::once(o1)
964                    .chain(iter::once(o2))
965                    .chain(on.iter().cloned())
966                    .fold(Ordering::Equal, |acc, x| acc.then(x))
967            }
968
969            (a, b) => {
970                let ret = discriminant(a).cmp(&discriminant(b));
971
972                assert!(ret != Ordering::Equal);
973                ret
974            }
975        };
976
977        ret
978    }
979}
980
981impl PartialEq for SecretKeyMaterial {
982    fn eq(&self, other: &Self) -> bool { self.cmp(other) == Ordering::Equal }
983}
984
985impl Eq for SecretKeyMaterial {}
986
987impl SecretKeyMaterial {
988    /// Returns, if known, the public-key algorithm for this secret
989    /// key.
990    pub fn algo(&self) -> Option<PublicKeyAlgorithm> {
991        use self::SecretKeyMaterial::*;
992        #[allow(deprecated)]
993        match self {
994            RSA { .. } => Some(PublicKeyAlgorithm::RSAEncryptSign),
995            DSA { .. } => Some(PublicKeyAlgorithm::DSA),
996            ElGamal { .. } => Some(PublicKeyAlgorithm::ElGamalEncrypt),
997            EdDSA { .. } => Some(PublicKeyAlgorithm::EdDSA),
998            ECDSA { .. } => Some(PublicKeyAlgorithm::ECDSA),
999            ECDH { .. } => Some(PublicKeyAlgorithm::ECDH),
1000            X25519 { .. } => Some(PublicKeyAlgorithm::X25519),
1001            X448 { .. } => Some(PublicKeyAlgorithm::X448),
1002            Ed25519 { .. } => Some(PublicKeyAlgorithm::Ed25519),
1003            Ed448 { .. } => Some(PublicKeyAlgorithm::Ed448),
1004            Unknown { .. } => None,
1005        }
1006    }
1007}
1008
1009impl Hash for SecretKeyMaterial {
1010    fn hash(&self, mut hash: &mut hash::Context) -> Result<()> {
1011        self.serialize(&mut hash as &mut dyn Write)
1012    }
1013}
1014
1015#[cfg(test)]
1016impl SecretKeyMaterial {
1017    pub(crate) fn arbitrary_for(g: &mut Gen, pk: PublicKeyAlgorithm) -> Result<Self> {
1018        use self::PublicKeyAlgorithm::*;
1019        #[allow(deprecated)]
1020        match pk {
1021            RSAEncryptSign | RSASign | RSAEncrypt => Ok(SecretKeyMaterial::RSA {
1022                d: ProtectedMPI::arbitrary(g),
1023                p: ProtectedMPI::arbitrary(g),
1024                q: ProtectedMPI::arbitrary(g),
1025                u: ProtectedMPI::arbitrary(g),
1026            }),
1027
1028            DSA => Ok(SecretKeyMaterial::DSA {
1029                x: ProtectedMPI::arbitrary(g),
1030            }),
1031
1032            ElGamalEncryptSign | ElGamalEncrypt => Ok(SecretKeyMaterial::ElGamal {
1033                x: ProtectedMPI::arbitrary(g),
1034            }),
1035
1036            EdDSA => Ok(SecretKeyMaterial::EdDSA {
1037                scalar: ProtectedMPI::arbitrary(g),
1038            }),
1039
1040            ECDSA => Ok(SecretKeyMaterial::ECDSA {
1041                scalar: ProtectedMPI::arbitrary(g),
1042            }),
1043
1044            ECDH => Ok(SecretKeyMaterial::ECDH {
1045                scalar: ProtectedMPI::arbitrary(g),
1046            }),
1047
1048            X25519 => Ok(SecretKeyMaterial::X25519 {
1049                x: arbitrarize(g, vec![0; 32]).into(),
1050            }),
1051            X448 => Ok(SecretKeyMaterial::X448 {
1052                x: arbitrarize(g, vec![0; 56]).into(),
1053            }),
1054            Ed25519 => Ok(SecretKeyMaterial::Ed25519 {
1055                x: arbitrarize(g, vec![0; 32]).into(),
1056            }),
1057            Ed448 => Ok(SecretKeyMaterial::Ed448 {
1058                x: arbitrarize(g, vec![0; 57]).into(),
1059            }),
1060
1061            Private(_) | Unknown(_) =>
1062                Err(Error::UnsupportedPublicKeyAlgorithm(pk).into()),
1063        }
1064    }
1065}
1066#[cfg(test)]
1067impl Arbitrary for SecretKeyMaterial {
1068    fn arbitrary(g: &mut Gen) -> Self {
1069        let pk = *g.choose(
1070            &crate::crypto::types::public_key_algorithm::PUBLIC_KEY_ALGORITHM_VARIANTS)
1071            .expect("not empty");
1072        Self::arbitrary_for(g, pk).expect("only known variants")
1073    }
1074}
1075
1076/// Checksum method for secret key material.
1077///
1078/// Secret key material may be protected by a checksum.  See [Section
1079/// 5.5.3 of RFC 9580] for details.
1080///
1081///   [Section 5.5.3 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.5.3
1082#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
1083pub enum SecretKeyChecksum {
1084    /// SHA1 over the decrypted secret key.
1085    SHA1,
1086
1087    /// Sum of the decrypted secret key octets modulo 65536.
1088    Sum16,
1089}
1090assert_send_and_sync!(SecretKeyChecksum);
1091
1092impl Default for SecretKeyChecksum {
1093    fn default() -> Self {
1094        SecretKeyChecksum::SHA1
1095    }
1096}
1097
1098impl SecretKeyChecksum {
1099    /// Returns the on-wire length of the checksum.
1100    pub(crate) fn len(&self) -> usize {
1101        match self {
1102            SecretKeyChecksum::SHA1 => 20,
1103            SecretKeyChecksum::Sum16 => 2,
1104        }
1105    }
1106}
1107
1108/// An encrypted session key.
1109///
1110/// Provides a typed and structured way of storing multiple MPIs in
1111/// [`PKESK`] packets.
1112///
1113///   [`PKESK`]: crate::packet::PKESK
1114#[non_exhaustive]
1115#[derive(Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
1116pub enum Ciphertext {
1117    /// RSA ciphertext.
1118    RSA {
1119        ///  m^e mod N.
1120        c: MPI,
1121    },
1122
1123    /// ElGamal ciphertext.
1124    ElGamal {
1125        /// Ephemeral key.
1126        e: MPI,
1127        /// Ciphertext.
1128        c: MPI,
1129    },
1130
1131    /// Elliptic curve ElGamal public key.
1132    ECDH {
1133        /// Ephemeral key.
1134        e: MPI,
1135        /// Symmetrically encrypted session key.
1136        key: Box<[u8]>,
1137    },
1138
1139    /// X25519 ciphertext.
1140    X25519 {
1141        /// Ephermeral key.
1142        e: Box<[u8; 32]>,
1143        /// Symmetrically encrypted session key.
1144        key: Box<[u8]>,
1145    },
1146
1147    /// X448 ciphertext.
1148    X448 {
1149        /// Ephermeral key.
1150        e: Box<[u8; 56]>,
1151        /// Symmetrically encrypted session key.
1152        key: Box<[u8]>,
1153    },
1154
1155    /// Unknown number of MPIs for an unknown algorithm.
1156    Unknown {
1157        /// The successfully parsed MPIs.
1158        mpis: Box<[MPI]>,
1159        /// Any data that failed to parse.
1160        rest: Box<[u8]>,
1161    },
1162}
1163assert_send_and_sync!(Ciphertext);
1164
1165impl Ciphertext {
1166    /// Returns, if known, the public-key algorithm for this
1167    /// ciphertext.
1168    pub fn pk_algo(&self) -> Option<PublicKeyAlgorithm> {
1169        use self::Ciphertext::*;
1170
1171        // Fields are mostly MPIs that consist of two octets length
1172        // plus the big endian value itself. All other field types are
1173        // commented.
1174        #[allow(deprecated)]
1175        match self {
1176            RSA { .. } => Some(PublicKeyAlgorithm::RSAEncryptSign),
1177            ElGamal { .. } => Some(PublicKeyAlgorithm::ElGamalEncrypt),
1178            ECDH { .. } => Some(PublicKeyAlgorithm::ECDH),
1179            X25519 { .. } => Some(PublicKeyAlgorithm::X25519),
1180            X448 { .. } => Some(PublicKeyAlgorithm::X448),
1181            Unknown { .. } => None,
1182        }
1183    }
1184}
1185
1186impl Hash for Ciphertext {
1187    fn hash(&self, mut hash: &mut hash::Context) -> Result<()> {
1188        self.serialize(&mut hash as &mut dyn Write)
1189    }
1190}
1191
1192#[cfg(test)]
1193impl Arbitrary for Ciphertext {
1194    fn arbitrary(g: &mut Gen) -> Self {
1195        use crate::arbitrary_helper::gen_arbitrary_from_range;
1196
1197        match gen_arbitrary_from_range(0..5, g) {
1198            0 => Ciphertext::RSA {
1199                c: MPI::arbitrary(g),
1200            },
1201
1202            1 => Ciphertext::ElGamal {
1203                e: MPI::arbitrary(g),
1204                c: MPI::arbitrary(g)
1205            },
1206
1207            2 => Ciphertext::ECDH {
1208                e: MPI::arbitrary(g),
1209                key: {
1210                    let mut k = <Vec<u8>>::arbitrary(g);
1211                    k.truncate(255);
1212                    k.into_boxed_slice()
1213                },
1214            },
1215
1216            3 => Ciphertext::X25519 {
1217                e: Box::new(arbitrary(g)),
1218                key: {
1219                    let mut k = <Vec<u8>>::arbitrary(g);
1220                    k.truncate(255);
1221                    k.into_boxed_slice()
1222                },
1223            },
1224
1225            4 => Ciphertext::X448 {
1226                e: Box::new(arbitrarize(g, [0; 56])),
1227                key: {
1228                    let mut k = <Vec<u8>>::arbitrary(g);
1229                    k.truncate(255);
1230                    k.into_boxed_slice()
1231                },
1232            },
1233            _ => unreachable!(),
1234        }
1235    }
1236}
1237
1238/// A cryptographic signature.
1239///
1240/// Provides a typed and structured way of storing multiple MPIs in
1241/// [`Signature`] packets.
1242///
1243///   [`Signature`]: crate::packet::Signature
1244#[non_exhaustive]
1245#[derive(Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
1246pub enum Signature {
1247    /// RSA signature.
1248    RSA {
1249        /// Signature m^d mod N.
1250        s: MPI,
1251    },
1252
1253    /// NIST's DSA signature.
1254    DSA {
1255        /// `r` value.
1256        r: MPI,
1257        /// `s` value.
1258        s: MPI,
1259    },
1260
1261    /// ElGamal signature.
1262    ElGamal {
1263        /// `r` value.
1264        r: MPI,
1265        /// `s` value.
1266        s: MPI,
1267    },
1268
1269    /// DJB's "Twisted" Edwards curve DSA signature.
1270    EdDSA {
1271        /// `r` value.
1272        r: MPI,
1273        /// `s` value.
1274        s: MPI,
1275    },
1276
1277    /// NIST's Elliptic curve DSA signature.
1278    ECDSA {
1279        /// `r` value.
1280        r: MPI,
1281        /// `s` value.
1282        s: MPI,
1283    },
1284
1285    /// Ed25519 signature.
1286    Ed25519 {
1287        /// The signature.
1288        s: Box<[u8; 64]>,
1289    },
1290
1291    /// Ed448 signature.
1292    Ed448 {
1293        /// The signature.
1294        s: Box<[u8; 114]>,
1295    },
1296
1297    /// Unknown number of MPIs for an unknown algorithm.
1298    Unknown {
1299        /// The successfully parsed MPIs.
1300        mpis: Box<[MPI]>,
1301        /// Any data that failed to parse.
1302        rest: Box<[u8]>,
1303    },
1304}
1305assert_send_and_sync!(Signature);
1306
1307impl Hash for Signature {
1308    fn hash(&self, mut hash: &mut hash::Context) -> Result<()> {
1309        self.serialize(&mut hash as &mut dyn Write)
1310    }
1311}
1312
1313#[cfg(test)]
1314impl Arbitrary for Signature {
1315    fn arbitrary(g: &mut Gen) -> Self {
1316        use crate::arbitrary_helper::gen_arbitrary_from_range;
1317
1318        match gen_arbitrary_from_range(0..6, g) {
1319            0 => Signature::RSA  {
1320                s: MPI::arbitrary(g),
1321            },
1322
1323            1 => Signature::DSA {
1324                r: MPI::arbitrary(g),
1325                s: MPI::arbitrary(g),
1326            },
1327
1328            2 => Signature::EdDSA  {
1329                r: MPI::arbitrary(g),
1330                s: MPI::arbitrary(g),
1331            },
1332
1333            3 => Signature::ECDSA  {
1334                r: MPI::arbitrary(g),
1335                s: MPI::arbitrary(g),
1336            },
1337
1338            4 => Signature::Ed25519  {
1339                s: Box::new(arbitrarize(g, [0; 64])),
1340            },
1341
1342            5 => Signature::Ed448  {
1343                s: Box::new(arbitrarize(g, [0; 114])),
1344            },
1345
1346            _ => unreachable!(),
1347        }
1348    }
1349}
1350
1351#[cfg(test)]
1352mod tests {
1353    use super::*;
1354    use crate::parse::Parse;
1355
1356    quickcheck! {
1357        fn mpi_roundtrip(mpi: MPI) -> bool {
1358            let mut buf = Vec::new();
1359            mpi.serialize(&mut buf).unwrap();
1360            MPI::from_bytes(&buf).unwrap() == mpi
1361        }
1362    }
1363
1364    quickcheck! {
1365        fn pk_roundtrip(pk: PublicKey) -> bool {
1366            use std::io::Cursor;
1367            use crate::PublicKeyAlgorithm::*;
1368
1369            let mut buf = Vec::new();
1370            pk.serialize(&mut buf).unwrap();
1371            let cur = Cursor::new(buf);
1372
1373            #[allow(deprecated)]
1374            let pk_ = match &pk {
1375                PublicKey::RSA { .. } =>
1376                    PublicKey::parse(RSAEncryptSign, cur).unwrap(),
1377                PublicKey::DSA { .. } =>
1378                    PublicKey::parse(DSA, cur).unwrap(),
1379                PublicKey::ElGamal { .. } =>
1380                    PublicKey::parse(ElGamalEncrypt, cur).unwrap(),
1381                PublicKey::EdDSA { .. } =>
1382                    PublicKey::parse(EdDSA, cur).unwrap(),
1383                PublicKey::ECDSA { .. } =>
1384                    PublicKey::parse(ECDSA, cur).unwrap(),
1385                PublicKey::ECDH { .. } =>
1386                    PublicKey::parse(ECDH, cur).unwrap(),
1387                PublicKey::X25519 { .. } =>
1388                    PublicKey::parse(X25519, cur).unwrap(),
1389                PublicKey::X448 { .. } =>
1390                    PublicKey::parse(X448, cur).unwrap(),
1391                PublicKey::Ed25519 { .. } =>
1392                    PublicKey::parse(Ed25519, cur).unwrap(),
1393                PublicKey::Ed448 { .. } =>
1394                    PublicKey::parse(Ed448, cur).unwrap(),
1395
1396                PublicKey::Unknown { .. } => unreachable!(),
1397            };
1398
1399            pk == pk_
1400        }
1401    }
1402
1403    #[test]
1404    fn pk_bits() {
1405        for (name, key_no, bits) in &[
1406            ("testy.pgp", 0, 2048),
1407            ("testy-new.pgp", 1, 256),
1408            ("dennis-simon-anton.pgp", 0, 2048),
1409            ("dsa2048-elgamal3072.pgp", 1, 3072),
1410            ("emmelie-dorothea-dina-samantha-awina-ed25519.pgp", 0, 256),
1411            ("erika-corinna-daniela-simone-antonia-nistp256.pgp", 0, 256),
1412            ("erika-corinna-daniela-simone-antonia-nistp384.pgp", 0, 384),
1413            ("erika-corinna-daniela-simone-antonia-nistp521.pgp", 0, 521),
1414        ] {
1415            let cert = crate::Cert::from_bytes(crate::tests::key(name)).unwrap();
1416            let ka = cert.keys().nth(*key_no).unwrap();
1417            assert_eq!(ka.key().mpis().bits().unwrap(), *bits,
1418                       "Cert {}, key no {}", name, *key_no);
1419        }
1420    }
1421
1422    quickcheck! {
1423        fn sk_roundtrip(sk: SecretKeyMaterial) -> bool {
1424            use std::io::Cursor;
1425            use crate::PublicKeyAlgorithm::*;
1426
1427            let mut buf = Vec::new();
1428            sk.serialize(&mut buf).unwrap();
1429            let cur = Cursor::new(buf);
1430
1431            #[allow(deprecated)]
1432            let sk_ = match &sk {
1433                SecretKeyMaterial::RSA { .. } =>
1434                    SecretKeyMaterial::parse(RSAEncryptSign, cur).unwrap(),
1435                SecretKeyMaterial::DSA { .. } =>
1436                    SecretKeyMaterial::parse(DSA, cur).unwrap(),
1437                SecretKeyMaterial::EdDSA { .. } =>
1438                    SecretKeyMaterial::parse(EdDSA, cur).unwrap(),
1439                SecretKeyMaterial::ECDSA { .. } =>
1440                    SecretKeyMaterial::parse(ECDSA, cur).unwrap(),
1441                SecretKeyMaterial::ECDH { .. } =>
1442                    SecretKeyMaterial::parse(ECDH, cur).unwrap(),
1443                SecretKeyMaterial::ElGamal { .. } =>
1444                    SecretKeyMaterial::parse(ElGamalEncrypt, cur).unwrap(),
1445                SecretKeyMaterial::X25519 { .. } =>
1446                    SecretKeyMaterial::parse(X25519, cur).unwrap(),
1447                SecretKeyMaterial::X448 { .. } =>
1448                    SecretKeyMaterial::parse(X448, cur).unwrap(),
1449                SecretKeyMaterial::Ed25519 { .. } =>
1450                    SecretKeyMaterial::parse(Ed25519, cur).unwrap(),
1451                SecretKeyMaterial::Ed448 { .. } =>
1452                    SecretKeyMaterial::parse(Ed448, cur).unwrap(),
1453
1454                SecretKeyMaterial::Unknown { .. } => unreachable!(),
1455            };
1456
1457            sk == sk_
1458        }
1459    }
1460
1461    quickcheck! {
1462        fn ct_roundtrip(ct: Ciphertext) -> bool {
1463            use std::io::Cursor;
1464            use crate::PublicKeyAlgorithm::*;
1465
1466            let mut buf = Vec::new();
1467            ct.serialize(&mut buf).unwrap();
1468            let cur = Cursor::new(buf);
1469
1470            #[allow(deprecated)]
1471            let ct_ = match &ct {
1472                Ciphertext::RSA { .. } =>
1473                    Ciphertext::parse(RSAEncryptSign, cur).unwrap(),
1474                Ciphertext::ElGamal { .. } =>
1475                    Ciphertext::parse(ElGamalEncrypt, cur).unwrap(),
1476                Ciphertext::ECDH { .. } =>
1477                    Ciphertext::parse(ECDH, cur).unwrap(),
1478                Ciphertext::X25519 { .. } =>
1479                    Ciphertext::parse(X25519, cur).unwrap(),
1480                Ciphertext::X448 { .. } =>
1481                    Ciphertext::parse(X448, cur).unwrap(),
1482
1483                Ciphertext::Unknown { .. } => unreachable!(),
1484            };
1485
1486            ct == ct_
1487        }
1488    }
1489
1490    quickcheck! {
1491        fn signature_roundtrip(sig: Signature) -> bool {
1492            use std::io::Cursor;
1493            use crate::PublicKeyAlgorithm::*;
1494
1495            let mut buf = Vec::new();
1496            sig.serialize(&mut buf).unwrap();
1497            let cur = Cursor::new(buf);
1498
1499            #[allow(deprecated)]
1500            let sig_ = match &sig {
1501                Signature::RSA { .. } =>
1502                    Signature::parse(RSAEncryptSign, cur).unwrap(),
1503                Signature::DSA { .. } =>
1504                    Signature::parse(DSA, cur).unwrap(),
1505                Signature::ElGamal { .. } =>
1506                    Signature::parse(ElGamalEncryptSign, cur).unwrap(),
1507                Signature::EdDSA { .. } =>
1508                    Signature::parse(EdDSA, cur).unwrap(),
1509                Signature::ECDSA { .. } =>
1510                    Signature::parse(ECDSA, cur).unwrap(),
1511                Signature::Ed25519 { .. } =>
1512                    Signature::parse(Ed25519, cur).unwrap(),
1513                Signature::Ed448 { .. } =>
1514                    Signature::parse(Ed448, cur).unwrap(),
1515
1516                Signature::Unknown { .. } => unreachable!(),
1517            };
1518
1519            sig == sig_
1520        }
1521    }
1522}