sequoia_openpgp/packet/skesk/
v6.rs

1//! Symmetric-Key Encrypted Session Key Packets.
2//!
3//! SKESK packets hold symmetrically encrypted session keys.  The
4//! session key is needed to decrypt the actual ciphertext.  See
5//! [Section 5.3 of RFC 9580] for details.
6//!
7//! [Section 5.3 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.3
8
9#[cfg(test)]
10use quickcheck::{Arbitrary, Gen};
11
12use crate::Result;
13use crate::crypto::{
14    self,
15    S2K,
16    Password,
17    SessionKey,
18    backend::{Backend, interface::Kdf},
19};
20use crate::Error;
21use crate::types::{
22    AEADAlgorithm,
23    SymmetricAlgorithm,
24};
25use crate::packet::{
26    Packet,
27    SKESK,
28    skesk::SKESK4,
29};
30
31/// Holds a symmetrically encrypted session key version 6.
32///
33/// Holds a symmetrically encrypted session key.  The session key is
34/// needed to decrypt the actual ciphertext.  See [Version 6 Symmetric
35/// Key Encrypted Session Key Packet Format] for details.
36///
37/// [Version 6 Symmetric Key Encrypted Session Key Packet Format]: https://www.rfc-editor.org/rfc/rfc9580.html#name-version-6-symmetric-key-enc
38#[derive(Clone, Debug, PartialEq, Eq, Hash)]
39pub struct SKESK6 {
40    /// Common fields.
41    pub(crate) skesk4: SKESK4,
42
43    /// AEAD algorithm.
44    aead_algo: AEADAlgorithm,
45
46    /// Initialization vector for the AEAD algorithm.
47    aead_iv: Box<[u8]>,
48}
49assert_send_and_sync!(SKESK6);
50
51impl SKESK6 {
52    /// Creates a new SKESK version 6 packet.
53    ///
54    /// The given symmetric algorithm is the one used to encrypt the
55    /// session key.
56    pub fn new(sym_algo: SymmetricAlgorithm,
57               aead_algo: AEADAlgorithm,
58               s2k: S2K,
59               aead_iv: Box<[u8]>,
60               esk: Box<[u8]>)
61               -> Result<Self> {
62        Ok(SKESK6 {
63            skesk4: SKESK4 {
64                common: Default::default(),
65                version: 6,
66                sym_algo,
67                s2k,
68                esk: Ok(Some(esk)),
69            },
70            aead_algo,
71            aead_iv,
72        })
73    }
74
75    /// Creates a new SKESK version 6 packet with the given password.
76    ///
77    /// This function takes two [`SymmetricAlgorithm`] arguments: The
78    /// first, `payload_algo`, is the algorithm used to encrypt the
79    /// message's payload (i.e. the one used in the [`SEIP`]), and the
80    /// second, `esk_algo`, is used to encrypt the session key.
81    /// Usually, one should use the same algorithm, but if they
82    /// differ, the `esk_algo` should be at least as strong as the
83    /// `payload_algo` as not to weaken the security of the payload
84    /// encryption.
85    ///
86    ///   [`SymmetricAlgorithm`]: crate::types::SymmetricAlgorithm
87    ///   [`SEIP`]: crate::packet::SEIP
88    pub fn with_password(payload_algo: SymmetricAlgorithm,
89                         esk_algo: SymmetricAlgorithm,
90                         esk_aead: AEADAlgorithm, s2k: S2K,
91                         session_key: &SessionKey, password: &Password)
92                         -> Result<Self> {
93        if session_key.len() != payload_algo.key_size()? {
94            return Err(Error::InvalidArgument(format!(
95                "Invalid size of session key, got {} want {}",
96                session_key.len(), payload_algo.key_size()?)).into());
97        }
98
99        // Derive key and make a cipher.
100        let ad = [0xc3, 6, esk_algo.into(), esk_aead.into()];
101        let key = s2k.derive_key(password, esk_algo.key_size()?)?;
102
103        let mut kek: SessionKey = vec![0; esk_algo.key_size()?].into();
104        Backend::hkdf_sha256(&key, None, &ad, &mut kek)?;
105
106
107        // Encrypt the session key with the KEK.
108        let mut iv = vec![0u8; esk_aead.nonce_size()?];
109        crypto::random(&mut iv)?;
110        let mut ctx =
111            esk_aead.context(esk_algo, &kek, &ad, &iv)?.for_encryption()?;
112        let mut esk_digest =
113            vec![0u8; session_key.len() + esk_aead.digest_size()?];
114        ctx.encrypt_seal(&mut esk_digest, session_key)?;
115
116        // Attach digest to the ESK, we model it as one.
117        SKESK6::new(esk_algo, esk_aead, s2k, iv.into_boxed_slice(),
118                    esk_digest.into())
119    }
120
121    /// Derives the key inside this `SKESK6` from `password`.
122    ///
123    /// Returns a tuple containing a placeholder symmetric cipher and
124    /// the key itself.  `SKESK6` packets do not contain the symmetric
125    /// cipher algorithm and instead rely on the `SEIPDv2` packet that
126    /// contains it.
127    pub fn decrypt(&self, password: &Password)
128                   -> Result<SessionKey> {
129        let key = self.s2k().derive_key(password,
130                                        self.symmetric_algo().key_size()?)?;
131
132        let mut kek: SessionKey =
133            vec![0; self.symmetric_algo().key_size()?].into();
134        let ad = [0xc3,
135                  6 /* Version.  */,
136                  self.symmetric_algo().into(),
137                  self.aead_algo.into()];
138        Backend::hkdf_sha256(&key, None, &ad, &mut kek)?;
139
140        // Use the derived key to decrypt the ESK.
141        let mut cipher = self.aead_algo.context(
142            self.symmetric_algo(), &kek, &ad, self.aead_iv())?
143            .for_decryption()?;
144
145        let mut plain: SessionKey =
146            vec![0; self.esk().len() - self.aead_algo.digest_size()?].into();
147        cipher.decrypt_verify(&mut plain, self.esk())?;
148        Ok(plain)
149    }
150
151    /// Gets the symmetric encryption algorithm.
152    pub fn symmetric_algo(&self) -> SymmetricAlgorithm {
153        self.skesk4.sym_algo
154    }
155
156    /// Sets the symmetric encryption algorithm.
157    pub fn set_symmetric_algo(&mut self, algo: SymmetricAlgorithm) -> SymmetricAlgorithm {
158        ::std::mem::replace(&mut self.skesk4.sym_algo, algo)
159    }
160
161    /// Gets the key derivation method.
162    pub fn s2k(&self) -> &S2K {
163        &self.skesk4.s2k
164    }
165
166    /// Sets the key derivation method.
167    pub fn set_s2k(&mut self, s2k: S2K) -> S2K {
168        ::std::mem::replace(&mut self.skesk4.s2k, s2k)
169    }
170
171    /// Gets the AEAD algorithm.
172    pub fn aead_algo(&self) -> AEADAlgorithm {
173        self.aead_algo
174    }
175
176    /// Sets the AEAD algorithm.
177    pub fn set_aead_algo(&mut self, algo: AEADAlgorithm) -> AEADAlgorithm {
178        ::std::mem::replace(&mut self.aead_algo, algo)
179    }
180
181    /// Gets the AEAD initialization vector.
182    pub fn aead_iv(&self) -> &[u8] {
183        &self.aead_iv
184    }
185
186    /// Sets the AEAD initialization vector.
187    pub fn set_aead_iv(&mut self, iv: Box<[u8]>) -> Box<[u8]> {
188        ::std::mem::replace(&mut self.aead_iv, iv)
189    }
190
191    /// Gets the encrypted session key.
192    pub fn esk(&self) -> &[u8] {
193        self.skesk4.raw_esk()
194    }
195
196    /// Sets the encrypted session key.
197    pub fn set_esk(&mut self, esk: Box<[u8]>) -> Box<[u8]> {
198        ::std::mem::replace(&mut self.skesk4.esk, Ok(Some(esk)))
199            .expect("v6 SKESK can always be parsed")
200            .expect("v6 SKESK packets always have an ESK")
201    }
202}
203
204impl From<SKESK6> for super::SKESK {
205    fn from(p: SKESK6) -> Self {
206        super::SKESK::V6(p)
207    }
208}
209
210impl From<SKESK6> for Packet {
211    fn from(s: SKESK6) -> Self {
212        Packet::SKESK(SKESK::V6(s))
213    }
214}
215
216#[cfg(test)]
217impl Arbitrary for SKESK6 {
218    fn arbitrary(g: &mut Gen) -> Self {
219        let symm = SymmetricAlgorithm::arbitrary(g);
220        let aead = AEADAlgorithm::arbitrary(g);
221        let mut iv = vec![0u8; aead.nonce_size().unwrap_or(16)];
222        for b in iv.iter_mut() {
223            *b = u8::arbitrary(g);
224        }
225        let esk_len =
226            symm.key_size().unwrap_or(16) + aead.digest_size().unwrap_or(16);
227        let mut esk = vec![0u8; esk_len];
228        for b in esk.iter_mut() {
229            *b = u8::arbitrary(g);
230        }
231        SKESK6::new(symm,
232                    aead,
233                    S2K::arbitrary(g),
234                    iv.into(),
235                    esk.into())
236            .unwrap()
237    }
238}
239
240#[cfg(test)]
241mod test {
242    use super::*;
243    use crate::PacketPile;
244    use crate::parse::Parse;
245    use crate::serialize::MarshalInto;
246
247    quickcheck! {
248        fn roundtrip_v6(p: SKESK6) -> bool {
249            let p = SKESK::from(p);
250            let q = SKESK::from_bytes(&p.to_vec().unwrap()).unwrap();
251            assert_eq!(p, q);
252            true
253        }
254    }
255
256    /// This sample packet is from RFC9580.
257    #[test]
258    fn v6skesk_aes128_ocb() -> Result<()> {
259        sample_skesk6_packet(
260            SymmetricAlgorithm::AES128,
261            AEADAlgorithm::OCB,
262            "crypto-refresh/v6skesk-aes128-ocb.pgp",
263            b"\xe8\x0d\xe2\x43\xa3\x62\xd9\x3b\
264              \x9d\xc6\x07\xed\xe9\x6a\x73\x56",
265            b"\x28\xe7\x9a\xb8\x23\x97\xd3\xc6\
266              \x3d\xe2\x4a\xc2\x17\xd7\xb7\x91")
267    }
268
269    /// This sample packet is from RFC9580.
270    #[test]
271    fn v6skesk_aes128_eax() -> Result<()> {
272        sample_skesk6_packet(
273            SymmetricAlgorithm::AES128,
274            AEADAlgorithm::EAX,
275            "crypto-refresh/v6skesk-aes128-eax.pgp",
276            b"\x15\x49\x67\xe5\x90\xaa\x1f\x92\
277              \x3e\x1c\x0a\xc6\x4c\x88\xf2\x3d",
278            b"\x38\x81\xba\xfe\x98\x54\x12\x45\
279              \x9b\x86\xc3\x6f\x98\xcb\x9a\x5e")
280    }
281
282    /// This sample packet is from RFC9580.
283    #[test]
284    fn v6skesk_aes128_gcm() -> Result<()> {
285        sample_skesk6_packet(
286            SymmetricAlgorithm::AES128,
287            AEADAlgorithm::GCM,
288            "crypto-refresh/v6skesk-aes128-gcm.pgp",
289            b"\x25\x02\x81\x71\x5b\xba\x78\x28\
290              \xef\x71\xef\x64\xc4\x78\x47\x53",
291            b"\x19\x36\xfc\x85\x68\x98\x02\x74\
292              \xbb\x90\x0d\x83\x19\x36\x0c\x77")
293    }
294
295    fn sample_skesk6_packet(cipher: SymmetricAlgorithm,
296                            aead: AEADAlgorithm,
297                            name: &str,
298                            derived_key: &[u8],
299                            session_key: &[u8])
300                            -> Result<()> {
301        let password: Password = String::from("password").into();
302        let packets: Vec<Packet> =
303            PacketPile::from_bytes(
304                crate::tests::file(name))?
305            .into_children().collect();
306        assert_eq!(packets.len(), 2);
307        if let Packet::SKESK(SKESK::V6(ref s)) = packets[0] {
308            let derived = s.s2k().derive_key(
309                &password, s.symmetric_algo().key_size()?)?;
310            eprintln!("derived: {:x?}", &derived[..]);
311            assert_eq!(&derived[..], derived_key);
312
313            use crate::crypto::backend::{Backend, interface::Aead};
314            if Backend::supports_algo_with_symmetric(aead, cipher)
315            {
316                let sk = s.decrypt(&password)?;
317                eprintln!("sk: {:x?}", &sk[..]);
318                assert_eq!(&sk[..], session_key);
319            } else {
320                eprintln!("{}-{} is not supported, skipping decryption.",
321                          cipher, aead);
322            }
323        } else {
324            panic!("bad packet, expected v6 SKESK: {:?}", packets[0]);
325        }
326
327        Ok(())
328    }
329}