sequoia_openpgp/packet/pkesk/
v6.rs

1//! PublicKey-Encrypted Session Key packets version 6.
2//!
3//! The session key is needed to decrypt the actual ciphertext.  See
4//! [Version 6 Public Key Encrypted Session Key Packet Format] for
5//! details.
6//!
7//! [Version 6 Public Key Encrypted Session Key Packet Format]: https://www.rfc-editor.org/rfc/rfc9580.html#name-version-6-public-key-encryp
8
9#[cfg(test)]
10use quickcheck::{Arbitrary, Gen};
11
12use crate::packet::key;
13use crate::packet::Key;
14use crate::Fingerprint;
15use crate::crypto::Decryptor;
16use crate::crypto::mpi::Ciphertext;
17use crate::Packet;
18use crate::PublicKeyAlgorithm;
19use crate::Result;
20use crate::SymmetricAlgorithm;
21use crate::crypto::SessionKey;
22use crate::packet;
23
24/// Holds an asymmetrically encrypted session key.
25///
26/// The session key is needed to decrypt the actual ciphertext.  See
27/// [Version 6 Public Key Encrypted Session Key Packet Format] for
28/// details.
29///
30/// [Version 6 Public Key Encrypted Session Key Packet Format]: https://www.rfc-editor.org/rfc/rfc9580.html#name-version-6-public-key-encryp
31#[derive(Clone, Debug, PartialEq, Eq, Hash)]
32pub struct PKESK6 {
33    /// CTB header fields.
34    pub(crate) common: packet::Common,
35
36    /// Fingerprint of the key this is encrypted to.
37    ///
38    /// If the value is `None`, the recipient has not been specified
39    /// by the sender to decrease metadata leakage.
40    recipient: Option<Fingerprint>,
41
42    /// Public key algorithm used to encrypt the session key.
43    pk_algo: PublicKeyAlgorithm,
44
45    /// The encrypted session key.
46    esk: Ciphertext,
47}
48
49assert_send_and_sync!(PKESK6);
50
51impl PKESK6 {
52    /// Creates a new PKESK6 packet.
53    pub fn new(recipient: Option<Fingerprint>, pk_algo: PublicKeyAlgorithm,
54               encrypted_session_key: Ciphertext)
55               -> Result<PKESK6>
56    {
57        Ok(PKESK6 {
58            common: Default::default(),
59            recipient,
60            pk_algo,
61            esk: encrypted_session_key,
62        })
63    }
64
65    /// Creates a new PKESK6 packet for the given recipient.
66    ///
67    /// The given symmetric algorithm must match the algorithm that is
68    /// used to encrypt the payload.
69    pub fn for_recipient<P, R>(session_key: &SessionKey,
70                               recipient: &Key<P, R>)
71                               -> Result<PKESK6>
72    where
73        P: key::KeyParts,
74        R: key::KeyRole,
75    {
76        // ElGamal is phased out in RFC 9580.
77        #[allow(deprecated)]
78        if recipient.pk_algo() == PublicKeyAlgorithm::ElGamalEncrypt
79            || recipient.pk_algo() == PublicKeyAlgorithm::ElGamalEncryptSign
80        {
81            return Err(crate::Error::InvalidOperation(
82                "MUST NOT encrypt with version 6 ElGamal keys".into())
83                       .into());
84        }
85
86        Ok(PKESK6 {
87            common: Default::default(),
88            recipient: Some(recipient.fingerprint()),
89            pk_algo: recipient.pk_algo(),
90            esk: packet::PKESK::encrypt_common(
91                None, session_key,
92                recipient.parts_as_unspecified().role_as_unspecified())?,
93        })
94    }
95
96    /// Gets the recipient.
97    pub fn recipient(&self) -> Option<&Fingerprint> {
98        self.recipient.as_ref()
99    }
100
101    /// Sets the recipient.
102    pub fn set_recipient(&mut self, recipient: Option<Fingerprint>)
103                         -> Option<Fingerprint> {
104        std::mem::replace(&mut self.recipient, recipient)
105    }
106
107    /// Gets the public key algorithm.
108    pub fn pk_algo(&self) -> PublicKeyAlgorithm {
109        self.pk_algo
110    }
111
112    /// Sets the public key algorithm.
113    pub fn set_pk_algo(&mut self, algo: PublicKeyAlgorithm)
114                       -> PublicKeyAlgorithm {
115        std::mem::replace(&mut self.pk_algo, algo)
116    }
117
118    /// Gets the encrypted session key.
119    pub fn esk(&self) -> &Ciphertext {
120        &self.esk
121    }
122
123    /// Sets the encrypted session key.
124    pub fn set_esk(&mut self, esk: Ciphertext) -> Ciphertext {
125        std::mem::replace(&mut self.esk, esk)
126    }
127
128    /// Decrypts the encrypted session key.
129    ///
130    /// If the symmetric algorithm used to encrypt the message is
131    /// known in advance, it should be given as argument.  This allows
132    /// us to reduce the side-channel leakage of the decryption
133    /// operation for RSA.
134    ///
135    /// Returns the session key and symmetric algorithm used to
136    /// encrypt the following payload.
137    ///
138    /// Returns `None` on errors.  This prevents leaking information
139    /// to an attacker, which could lead to compromise of secret key
140    /// material with certain algorithms (RSA).  See [Avoiding Leaks
141    /// from PKCS#1 Errors].
142    ///
143    /// [Avoiding Leaks from PKCS#1 Errors]: https://www.rfc-editor.org/rfc/rfc9580.html#name-avoiding-leaks-from-pkcs1-e
144    pub fn decrypt(&self, decryptor: &mut dyn Decryptor,
145                   sym_algo_hint: Option<SymmetricAlgorithm>)
146                   -> Option<SessionKey>
147    {
148        self.decrypt_insecure(decryptor, sym_algo_hint).ok()
149    }
150
151    fn decrypt_insecure(&self, decryptor: &mut dyn Decryptor,
152                        sym_algo_hint: Option<SymmetricAlgorithm>)
153                        -> Result<SessionKey>
154    {
155        packet::PKESK::decrypt_common(&self.esk, decryptor, sym_algo_hint, false)
156            .map(|(_sym_algo, key)| key)
157    }
158}
159
160impl From<PKESK6> for packet::PKESK {
161    fn from(p: PKESK6) -> Self {
162        packet::PKESK::V6(p)
163    }
164}
165
166impl From<PKESK6> for Packet {
167    fn from(p: PKESK6) -> Self {
168        Packet::PKESK(p.into())
169    }
170}
171
172#[cfg(test)]
173impl Arbitrary for PKESK6 {
174    fn arbitrary(g: &mut Gen) -> Self {
175        let (ciphertext, pk_algo) = loop {
176            let ciphertext = Ciphertext::arbitrary(g);
177            if let Some(pk_algo) = ciphertext.pk_algo() {
178                break (ciphertext, pk_algo);
179            }
180        };
181
182        PKESK6::new(bool::arbitrary(g).then(|| Fingerprint::arbitrary_v6(g)),
183                    pk_algo, ciphertext).unwrap()
184    }
185}
186
187#[cfg(test)]
188mod tests {
189    use super::*;
190    use crate::parse::Parse;
191    use crate::serialize::MarshalInto;
192
193    quickcheck! {
194        fn roundtrip(p: PKESK6) -> bool {
195            let q = PKESK6::from_bytes(&p.to_vec().unwrap()).unwrap();
196            assert_eq!(p, q);
197            true
198        }
199    }
200}