sequoia_openpgp/packet/pkesk/
v3.rs

1//! PublicKey-Encrypted Session Key packets.
2//!
3//! The session key is needed to decrypt the actual ciphertext.  See
4//! [Section 5.1 of RFC 9580] for details.
5//!
6//!   [Section 5.1 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.1
7
8#[cfg(test)]
9use quickcheck::{Arbitrary, Gen};
10
11use crate::packet::key;
12use crate::packet::Key;
13use crate::KeyID;
14use crate::crypto::Decryptor;
15use crate::crypto::mpi::Ciphertext;
16use crate::Packet;
17use crate::PublicKeyAlgorithm;
18use crate::Result;
19use crate::SymmetricAlgorithm;
20use crate::crypto::SessionKey;
21use crate::packet;
22
23/// Holds an asymmetrically encrypted session key.
24///
25/// The session key is needed to decrypt the actual ciphertext.  See
26/// [Section 5.1 of RFC 9580] for details.
27///
28///   [Section 5.1 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.1
29// IMPORTANT: If you add fields to this struct, you need to explicitly
30// IMPORTANT: implement PartialEq, Eq, and Hash.
31#[derive(Clone, Debug, PartialEq, Eq, Hash)]
32pub struct PKESK3 {
33    /// CTB header fields.
34    pub(crate) common: packet::Common,
35    /// Key ID of the key this is encrypted to.
36    recipient: Option<KeyID>,
37    /// Public key algorithm used to encrypt the session key.
38    pk_algo: PublicKeyAlgorithm,
39    /// The encrypted session key.
40    esk: Ciphertext,
41}
42
43assert_send_and_sync!(PKESK3);
44
45impl PKESK3 {
46    /// Creates a new PKESK3 packet.
47    pub fn new(recipient: Option<KeyID>,
48               pk_algo: PublicKeyAlgorithm,
49               encrypted_session_key: Ciphertext)
50               -> Result<PKESK3> {
51        // Normalize recipient.
52        let recipient =
53            if recipient.as_ref().map(KeyID::is_wildcard).unwrap_or(true) {
54                None
55            } else {
56                recipient
57            };
58
59        Ok(PKESK3 {
60            common: Default::default(),
61            recipient,
62            pk_algo,
63            esk: encrypted_session_key,
64        })
65    }
66
67    /// Creates a new PKESK3 packet for the given recipient.
68    ///
69    /// The given symmetric algorithm must match the algorithm that is
70    /// used to encrypt the payload.
71    pub fn for_recipient<P, R>(algo: SymmetricAlgorithm,
72                               session_key: &SessionKey,
73                               recipient: &Key<P, R>)
74        -> Result<PKESK3>
75        where P: key::KeyParts,
76              R: key::KeyRole,
77    {
78        Ok(PKESK3{
79            common: Default::default(),
80            recipient: Some(recipient.keyid()),
81            pk_algo: recipient.pk_algo(),
82            esk: packet::PKESK::encrypt_common(
83                Some(algo), session_key,
84                recipient.parts_as_unspecified().role_as_unspecified())?,
85        })
86    }
87
88    /// Gets the recipient.
89    pub fn recipient(&self) -> Option<&KeyID> {
90        self.recipient.as_ref()
91    }
92
93    /// Sets the recipient.
94    pub fn set_recipient(&mut self, recipient: Option<KeyID>) -> Option<KeyID> {
95        ::std::mem::replace(&mut self.recipient, recipient)
96    }
97
98    /// Gets the public key algorithm.
99    pub fn pk_algo(&self) -> PublicKeyAlgorithm {
100        self.pk_algo
101    }
102
103    /// Sets the public key algorithm.
104    pub fn set_pk_algo(&mut self, algo: PublicKeyAlgorithm) -> PublicKeyAlgorithm {
105        ::std::mem::replace(&mut self.pk_algo, algo)
106    }
107
108    /// Gets the encrypted session key.
109    pub fn esk(&self) -> &Ciphertext {
110        &self.esk
111    }
112
113    /// Sets the encrypted session key.
114    pub fn set_esk(&mut self, esk: Ciphertext) -> Ciphertext {
115        ::std::mem::replace(&mut self.esk, esk)
116    }
117
118    /// Decrypts the encrypted session key.
119    ///
120    /// If the symmetric algorithm used to encrypt the message is
121    /// known in advance, it should be given as argument.  This allows
122    /// us to reduce the side-channel leakage of the decryption
123    /// operation for RSA.
124    ///
125    /// Returns the session key and symmetric algorithm used to
126    /// encrypt the following payload.
127    ///
128    /// Returns `None` on errors.  This prevents leaking information
129    /// to an attacker, which could lead to compromise of secret key
130    /// material with certain algorithms (RSA).  See [Section 13 of
131    /// RFC 9580].
132    ///
133    ///   [Section 13 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-13
134    pub fn decrypt(&self, decryptor: &mut dyn Decryptor,
135                   sym_algo_hint: Option<SymmetricAlgorithm>)
136        -> Option<(SymmetricAlgorithm, SessionKey)>
137    {
138        self.decrypt_insecure(decryptor, sym_algo_hint).ok()
139    }
140
141    fn decrypt_insecure(&self, decryptor: &mut dyn Decryptor,
142                        sym_algo_hint: Option<SymmetricAlgorithm>)
143        -> Result<(SymmetricAlgorithm, SessionKey)>
144    {
145        packet::PKESK::decrypt_common(&self.esk, decryptor, sym_algo_hint, true)
146            .map(|(sym_algo, sk)| (sym_algo.expect("known for v3 PKESK"), sk))
147    }
148}
149
150impl From<PKESK3> for crate::packet::PKESK {
151    fn from(p: PKESK3) -> Self {
152        crate::packet::PKESK::V3(p)
153    }
154}
155
156impl From<PKESK3> for Packet {
157    fn from(p: PKESK3) -> Self {
158        Packet::PKESK(p.into())
159    }
160}
161
162#[cfg(test)]
163impl Arbitrary for PKESK3 {
164    fn arbitrary(g: &mut Gen) -> Self {
165        let (ciphertext, pk_algo) = loop {
166            let ciphertext = Ciphertext::arbitrary(g);
167            if let Some(pk_algo) = ciphertext.pk_algo() {
168                break (ciphertext, pk_algo);
169            }
170        };
171
172        let keyid = bool::arbitrary(g).then_some(KeyID::arbitrary(g));
173        PKESK3::new(keyid, pk_algo, ciphertext).unwrap()
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180    use crate::Cert;
181    use crate::PacketPile;
182    use crate::packet::*;
183    use crate::parse::Parse;
184    use crate::serialize::MarshalInto;
185    use crate::types::Curve;
186
187    quickcheck! {
188        fn roundtrip(p: PKESK3) -> bool {
189            let q = PKESK3::from_bytes(&p.to_vec().unwrap()).unwrap();
190            assert_eq!(p, q);
191            true
192        }
193    }
194
195    #[test]
196    fn decrypt_rsa() {
197        if ! PublicKeyAlgorithm::RSAEncryptSign.is_supported() {
198            eprintln!("Skipping test, algorithm is not supported.");
199            return;
200        }
201
202        let cert = Cert::from_bytes(
203            crate::tests::key("testy-private.pgp")).unwrap();
204        let pile = PacketPile::from_bytes(
205            crate::tests::message("encrypted-to-testy.pgp")).unwrap();
206        let mut keypair =
207            cert.subkeys().next().unwrap()
208            .key().clone().parts_into_secret().unwrap().into_keypair().unwrap();
209
210        let pkesk: &PKESK =
211            pile.descendants().next().unwrap().downcast_ref().unwrap();
212
213        let plain = pkesk.decrypt(&mut keypair, None).unwrap();
214        let plain_ =
215            pkesk.decrypt(&mut keypair, Some(SymmetricAlgorithm::AES256))
216            .unwrap();
217        assert_eq!(plain, plain_);
218
219        eprintln!("plain: {:?}", plain);
220    }
221
222    #[test]
223    fn decrypt_ecdh_cv25519() {
224        if ! (PublicKeyAlgorithm::EdDSA.is_supported()
225              && Curve::Ed25519.is_supported()
226              && PublicKeyAlgorithm::ECDH.is_supported()
227              && Curve::Cv25519.is_supported()) {
228            eprintln!("Skipping test, algorithm is not supported.");
229            return;
230        }
231
232        let cert = Cert::from_bytes(
233            crate::tests::key("testy-new-private.pgp")).unwrap();
234        let pile = PacketPile::from_bytes(
235            crate::tests::message("encrypted-to-testy-new.pgp")).unwrap();
236        let mut keypair =
237            cert.subkeys().next().unwrap()
238            .key().clone().parts_into_secret().unwrap().into_keypair().unwrap();
239
240        let pkesk: &PKESK =
241            pile.descendants().next().unwrap().downcast_ref().unwrap();
242
243        let plain = pkesk.decrypt(&mut keypair, None).unwrap();
244        let plain_ =
245            pkesk.decrypt(&mut keypair, Some(SymmetricAlgorithm::AES256))
246            .unwrap();
247        assert_eq!(plain, plain_);
248
249        eprintln!("plain: {:?}", plain);
250    }
251
252    #[test]
253    fn decrypt_ecdh_nistp256() {
254        if ! (PublicKeyAlgorithm::ECDSA.is_supported()
255              && PublicKeyAlgorithm::ECDH.is_supported()
256              && Curve::NistP256.is_supported()) {
257            eprintln!("Skipping test, algorithm is not supported.");
258            return;
259        }
260
261        let cert = Cert::from_bytes(
262            crate::tests::key("testy-nistp256-private.pgp")).unwrap();
263        let pile = PacketPile::from_bytes(
264            crate::tests::message("encrypted-to-testy-nistp256.pgp")).unwrap();
265        let mut keypair =
266            cert.subkeys().next().unwrap()
267            .key().clone().parts_into_secret().unwrap().into_keypair().unwrap();
268
269        let pkesk: &PKESK =
270            pile.descendants().next().unwrap().downcast_ref().unwrap();
271
272        let plain = pkesk.decrypt(&mut keypair, None)
273            .expect("ECDH decryption using P-256 key should work");
274        let plain_ =
275            pkesk.decrypt(&mut keypair, Some(SymmetricAlgorithm::AES256))
276            .unwrap();
277        assert_eq!(plain, plain_);
278
279        eprintln!("plain: {:?}", plain);
280    }
281
282    #[test]
283    fn decrypt_ecdh_nistp384() {
284        if ! (PublicKeyAlgorithm::ECDSA.is_supported()
285              && PublicKeyAlgorithm::ECDH.is_supported()
286              && Curve::NistP384.is_supported()) {
287            eprintln!("Skipping test, algorithm is not supported.");
288            return;
289        }
290
291        let cert = Cert::from_bytes(
292            crate::tests::key("testy-nistp384-private.pgp")).unwrap();
293        let pile = PacketPile::from_bytes(
294            crate::tests::message("encrypted-to-testy-nistp384.pgp")).unwrap();
295        let mut keypair =
296            cert.subkeys().next().unwrap()
297            .key().clone().parts_into_secret().unwrap().into_keypair().unwrap();
298
299        let pkesk: &PKESK =
300            pile.descendants().next().unwrap().downcast_ref().unwrap();
301
302        let plain = pkesk.decrypt(&mut keypair, None)
303            .expect("ECDH decryption using P-384 key should work");
304        let plain_ =
305            pkesk.decrypt(&mut keypair, Some(SymmetricAlgorithm::AES256))
306            .unwrap();
307        assert_eq!(plain, plain_);
308
309        eprintln!("plain: {:?}", plain);
310    }
311
312    #[test]
313    #[allow(deprecated)]
314    fn decrypt_elgamal() -> Result<()> {
315        if ! (PublicKeyAlgorithm::DSA.is_supported()
316              && PublicKeyAlgorithm::ElGamalEncrypt.is_supported()) {
317            eprintln!("Skipping test, algorithm is not supported.");
318            return Ok(());
319        }
320
321        let cert = Cert::from_bytes(
322            crate::tests::key("dsa2048-elgamal3072-private.pgp"))?;
323        let pile = PacketPile::from_bytes(
324            crate::tests::message("encrypted-to-dsa2048-elgamal3072.pgp"))?;
325        let mut keypair =
326            cert.subkeys().next().unwrap()
327            .key().clone().parts_into_secret()?.into_keypair()?;
328
329        let pkesk: &PKESK =
330            pile.descendants().next().unwrap().downcast_ref().unwrap();
331
332        let plain = pkesk.decrypt(&mut keypair, None).unwrap();
333        let plain_ =
334            pkesk.decrypt(&mut keypair, Some(SymmetricAlgorithm::AES256))
335            .unwrap();
336        assert_eq!(plain, plain_);
337
338        eprintln!("plain: {:?}", plain);
339        Ok(())
340    }
341
342    #[test]
343    fn decrypt_ecdh_nistp521() {
344        if ! (PublicKeyAlgorithm::ECDSA.is_supported()
345              && PublicKeyAlgorithm::ECDH.is_supported()
346              && Curve::NistP521.is_supported()) {
347            eprintln!("Skipping test, algorithm is not supported.");
348            return;
349        }
350
351        let cert = Cert::from_bytes(
352            crate::tests::key("testy-nistp521-private.pgp")).unwrap();
353        let pile = PacketPile::from_bytes(
354            crate::tests::message("encrypted-to-testy-nistp521.pgp")).unwrap();
355        let mut keypair =
356            cert.subkeys().next().unwrap()
357            .key().clone().parts_into_secret().unwrap().into_keypair().unwrap();
358
359        let pkesk: &PKESK =
360            pile.descendants().next().unwrap().downcast_ref().unwrap();
361
362        let plain = pkesk.decrypt(&mut keypair, None)
363            .expect("ECDH decryption using P-521 key should work");
364        let plain_ =
365            pkesk.decrypt(&mut keypair, Some(SymmetricAlgorithm::AES256))
366            .unwrap();
367        assert_eq!(plain, plain_);
368
369        eprintln!("plain: {:?}", plain);
370    }
371
372
373    #[test]
374    fn decrypt_with_short_cv25519_secret_key() {
375        if ! (PublicKeyAlgorithm::ECDH.is_supported()
376              && Curve::Cv25519.is_supported()) {
377            eprintln!("Skipping test, algorithm is not supported.");
378            return;
379        }
380
381        use super::PKESK3;
382        use crate::crypto::SessionKey;
383        use crate::{HashAlgorithm, SymmetricAlgorithm};
384        use crate::packet::key::{Key4, UnspecifiedRole};
385
386        // 20 byte sec key
387        let mut secret_key = [
388            0x0,0x0,
389            0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
390            0x1,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,
391            0x1,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x0,0x0
392        ];
393        // Ensure that the key is at least somewhat valid, according to the
394        // generation procedure specified in "Responsibilities of the user":
395        // https://cr.yp.to/ecdh/curve25519-20060209.pdf#page=5
396        // Only perform the bit-twiddling on the last byte. This is done so that
397        // we can still have somewhat defined multiplication while still testing
398        // the "short" key logic.
399        // secret_key[0] &= 0xf8;
400        secret_key[31] &= 0x7f;
401        secret_key[31] |= 0x40;
402
403        let key: Key<_, UnspecifiedRole> = Key4::import_secret_cv25519(
404            &secret_key,
405            HashAlgorithm::SHA256,
406            SymmetricAlgorithm::AES256,
407            None,
408        ).unwrap().into();
409
410        let sess_key = SessionKey::new(32).unwrap();
411        let pkesk = PKESK3::for_recipient(SymmetricAlgorithm::AES256, &sess_key,
412                                          &key).unwrap();
413        let mut keypair = key.into_keypair().unwrap();
414        pkesk.decrypt(&mut keypair, None).unwrap();
415    }
416
417    /// Insufficient validation of RSA ciphertexts crash Nettle.
418    ///
419    /// See CVE-2021-3580.
420    #[test]
421    fn cve_2021_3580_ciphertext_too_long() -> Result<()> {
422        if ! PublicKeyAlgorithm::RSAEncryptSign.is_supported() {
423            eprintln!("Skipping test, algorithm is not supported.");
424            return Ok(());
425        }
426
427        // Get (any) 2k RSA key.
428        let cert = Cert::from_bytes(
429            crate::tests::key("testy-private.pgp"))?;
430        let mut keypair = cert.primary_key().key().clone()
431            .parts_into_secret()?.into_keypair()?;
432
433        let pile = PacketPile::from_bytes(b"-----BEGIN PGP ARMORED FILE-----
434
435wcDNAwAAAAAAAAAAAQwGI5SkpcRMjkiOKx332kxv+2Xh4y1QTefPilKOPOlHYFa0
436rnnLaQVEACKJNQ38YuCFUvtpK4IN2grjlj71IP24+KDp3ZuVWnVTS6JcyE10Y9iq
437uGvKdS0C17XCze2LD4ouVOrUZHGXpeDT47w6DsHb/0UE85h56wpk2CzO1XFQzHxX
438HR2DDLqqeFVzTv0peYiQfLHl7kWXijTNEqmYhFCzxuICXzuClAAJM+fVIRfcm2tm
4392R4AxOQGv9DlWfZwbkpKfj/uuo0CAe21n4NT+NzdVgPlff/hna3yGgPe1B+vjq4e
440jfxHg+pvo/HTLkV+c2HAGbM1bCb/5TedGd1nAMSAIOu/J/WQp/l3HtEv63HaVPZJ
441JInJ6L/KyPwjm/ieZx5EWOLJgFRWGCrBGnb8T81lkFey7uZR5Xiq+9KoUhHQFw8N
442joc0YUVyhUBVFf4B0zVZRUfqZyJtJ07Sl5xppI12U1HQCTjn7Fp8BHMPKuBotYzv
4431Q4f00k6Txctw+LDRM17/w==
444=VtwB
445-----END PGP ARMORED FILE-----
446")?;
447        let pkesk: &PKESK =
448            pile.descendants().next().unwrap().downcast_ref().unwrap();
449        // Boom goes the assertion.
450        let _ = pkesk.decrypt(&mut keypair, None);
451
452        Ok(())
453    }
454
455    /// Insufficient validation of RSA ciphertexts crash Nettle.
456    ///
457    /// See CVE-2021-3580.
458    #[test]
459    fn cve_2021_3580_zero_ciphertext() -> Result<()> {
460        if ! PublicKeyAlgorithm::RSAEncryptSign.is_supported() {
461            eprintln!("Skipping test, algorithm is not supported.");
462            return Ok(());
463        }
464
465        // Get (any) 2k RSA key.
466        let cert = Cert::from_bytes(
467            crate::tests::key("testy-private.pgp"))?;
468        let mut keypair = cert.primary_key().key().clone()
469            .parts_into_secret()?.into_keypair()?;
470
471        let pile = PacketPile::from_bytes(b"-----BEGIN PGP ARMORED FILE-----
472
473wQwDAAAAAAAAAAABAAA=
474=H/1T
475-----END PGP ARMORED FILE-----
476")?;
477        let pkesk: &PKESK =
478            pile.descendants().next().unwrap().downcast_ref().unwrap();
479        // Boom goes the memory safety.
480        let _ = pkesk.decrypt(&mut keypair, None);
481
482        Ok(())
483    }
484}