1#[cfg(test)]
9use quickcheck::{Arbitrary, Gen};
10
11use crate::Error;
12use crate::KeyHandle;
13use crate::packet::key;
14use crate::packet::Key;
15use crate::packet::Packet;
16use crate::crypto::Decryptor;
17use crate::crypto::mpi::Ciphertext;
18use crate::PublicKeyAlgorithm;
19use crate::Result;
20use crate::SymmetricAlgorithm;
21use crate::crypto::SessionKey;
22use crate::packet;
23
24mod v3;
25pub use v3::PKESK3;
26mod v6;
27pub use v6::PKESK6;
28
29#[non_exhaustive]
45#[derive(PartialEq, Eq, Hash, Clone, Debug)]
46pub enum PKESK {
47 V3(PKESK3),
49 V6(PKESK6),
51}
52assert_send_and_sync!(PKESK);
53
54impl PKESK {
55 pub fn version(&self) -> u8 {
57 match self {
58 PKESK::V3(_) => 3,
59 PKESK::V6(_) => 6,
60 }
61 }
62
63 pub fn recipient(&self) -> Option<KeyHandle> {
65 match self {
66 PKESK::V3(p) => p.recipient().map(Into::into),
67 PKESK::V6(p) => p.recipient().map(Into::into),
68 }
69 }
70
71 pub fn pk_algo(&self) -> PublicKeyAlgorithm {
73 match self {
74 PKESK::V3(p) => p.pk_algo(),
75 PKESK::V6(p) => p.pk_algo(),
76 }
77 }
78
79 pub fn esk(&self) -> &crate::crypto::mpi::Ciphertext {
81 match self {
82 PKESK::V3(p) => p.esk(),
83 PKESK::V6(p) => p.esk(),
84 }
85 }
86
87 pub fn decrypt(&self, decryptor: &mut dyn Decryptor,
104 sym_algo_hint: Option<SymmetricAlgorithm>)
105 -> Option<(Option<SymmetricAlgorithm>, SessionKey)>
106 {
107 match self {
108 PKESK::V3(p) => p.decrypt(decryptor, sym_algo_hint)
109 .map(|(s, k)| (Some(s), k)),
110 PKESK::V6(p) => p.decrypt(decryptor, sym_algo_hint)
111 .map(|k| (None, k)),
112 }
113 }
114}
115
116impl From<PKESK> for Packet {
117 fn from(p: PKESK) -> Self {
118 Packet::PKESK(p)
119 }
120}
121
122fn classify_pk_algo(algo: PublicKeyAlgorithm, seipdv1: bool)
127 -> Result<(bool, bool, bool)>
128{
129 #[allow(deprecated)]
130 match algo {
131 PublicKeyAlgorithm::RSAEncryptSign |
134 PublicKeyAlgorithm::RSAEncrypt |
135 PublicKeyAlgorithm::ElGamalEncrypt |
136 PublicKeyAlgorithm::ElGamalEncryptSign |
137 PublicKeyAlgorithm::ECDH =>
138 Ok((true, false, seipdv1)),
139
140 PublicKeyAlgorithm::X25519 |
144 PublicKeyAlgorithm::X448 |
145 PublicKeyAlgorithm::MLKEM768_X25519 =>
146 Ok((false, seipdv1, false)),
147
148 a @ PublicKeyAlgorithm::MLKEM1024_X448 =>
150 if seipdv1 {
151 Err(Error::UnsupportedPublicKeyAlgorithm(a).into())
152 } else {
153 Ok((false, seipdv1, false))
154 },
155
156 a @ PublicKeyAlgorithm::RSASign |
157 a @ PublicKeyAlgorithm::DSA |
158 a @ PublicKeyAlgorithm::ECDSA |
159 a @ PublicKeyAlgorithm::EdDSA |
160 a @ PublicKeyAlgorithm::Ed25519 |
161 a @ PublicKeyAlgorithm::Ed448 |
162 a @ PublicKeyAlgorithm::MLDSA65_Ed25519 |
163 a @ PublicKeyAlgorithm::MLDSA87_Ed448 |
164 a @ PublicKeyAlgorithm::SLHDSA128s |
165 a @ PublicKeyAlgorithm::SLHDSA128f |
166 a @ PublicKeyAlgorithm::SLHDSA256s |
167 a @ PublicKeyAlgorithm::Private(_) |
168 a @ PublicKeyAlgorithm::Unknown(_) =>
169 Err(Error::UnsupportedPublicKeyAlgorithm(a).into()),
170 }
171}
172
173
174impl packet::PKESK {
175 fn encrypt_common(algo: Option<SymmetricAlgorithm>,
176 session_key: &SessionKey,
177 recipient: &Key<key::UnspecifiedParts,
178 key::UnspecifiedRole>)
179 -> Result<Ciphertext>
180 {
181 let (checksummed, unencrypted_cipher_octet, encrypted_cipher_octet) =
182 classify_pk_algo(recipient.pk_algo(), algo.is_some())?;
183
184 let mut psk = Vec::with_capacity(
187 encrypted_cipher_octet.then(|| 1).unwrap_or(0)
188 + session_key.len()
189 + checksummed.then(|| 2).unwrap_or(0));
190 if let Some(algo) = algo {
191 if encrypted_cipher_octet {
192 psk.push(algo.into());
193 }
194 }
195 psk.extend_from_slice(session_key);
196
197 if checksummed {
198 let checksum = session_key
200 .iter()
201 .cloned()
202 .map(u16::from)
203 .fold(0u16, u16::wrapping_add);
204
205 psk.extend_from_slice(&checksum.to_be_bytes());
206 }
207
208 let psk: SessionKey = psk.into();
210 let mut esk = recipient.encrypt(&psk)?;
211
212 if let Some(algo) = algo {
213 if unencrypted_cipher_octet {
214 match esk {
215 Ciphertext::X25519 { ref mut key, .. } |
216 Ciphertext::X448 { ref mut key, .. } |
217 Ciphertext::MLKEM768_X25519 { esk: ref mut key, .. } => {
218 let mut new_key = Vec::with_capacity(1 + key.len());
219 new_key.push(algo.into());
220 new_key.extend_from_slice(key);
221 *key = new_key.into();
222 },
223 _ => unreachable!(
224 "We only prepend the cipher octet \
225 for X25519, X448 and ML-KEM-768+X25519"),
226 };
227 }
228 }
229
230 Ok(esk)
231 }
232
233 fn decrypt_common(ciphertext: &Ciphertext,
234 decryptor: &mut dyn Decryptor,
235 sym_algo_hint: Option<SymmetricAlgorithm>,
236 seipdv1: bool)
237 -> Result<(Option<SymmetricAlgorithm>, SessionKey)>
238 {
239 let (checksummed, unencrypted_cipher_octet, encrypted_cipher_octet) =
240 classify_pk_algo(decryptor.public().pk_algo(), seipdv1)?;
241
242 let mut sym_algo: Option<SymmetricAlgorithm> = None;
245 let modified_ciphertext;
246 let esk;
247 if unencrypted_cipher_octet {
248 match ciphertext {
249 Ciphertext::X25519 { e, key, } => {
250 sym_algo =
251 Some((*key.get(0).ok_or_else(
252 || Error::MalformedPacket("Short ESK".into()))?)
253 .into());
254 modified_ciphertext = Ciphertext::X25519 {
255 e: e.clone(),
256 key: key[1..].into(),
257 };
258 esk = &modified_ciphertext;
259 },
260 Ciphertext::X448 { e, key, } => {
261 sym_algo =
262 Some((*key.get(0).ok_or_else(
263 || Error::MalformedPacket("Short ESK".into()))?)
264 .into());
265 modified_ciphertext = Ciphertext::X448 {
266 e: e.clone(),
267 key: key[1..].into(),
268 };
269 esk = &modified_ciphertext;
270 },
271
272 Ciphertext::MLKEM768_X25519 { ecdh, mlkem, esk: key, } => {
273 sym_algo =
274 Some((*key.get(0).ok_or_else(
275 || Error::MalformedPacket("Short ESK".into()))?)
276 .into());
277 modified_ciphertext = Ciphertext::MLKEM768_X25519 {
278 ecdh: ecdh.clone(),
279 mlkem: mlkem.clone(),
280 esk: key[1..].into(),
281 };
282 esk = &modified_ciphertext;
283 },
284
285 Ciphertext::MLKEM1024_X448 { ecdh, mlkem, esk: key, } => {
286 sym_algo =
287 Some((*key.get(0).ok_or_else(
288 || Error::MalformedPacket("Short ESK".into()))?)
289 .into());
290 modified_ciphertext = Ciphertext::MLKEM1024_X448 {
291 ecdh: ecdh.clone(),
292 mlkem: mlkem.clone(),
293 esk: key[1..].into(),
294 };
295 esk = &modified_ciphertext;
296 },
297
298 _ => {
299 esk = ciphertext;
305 },
306 }
307 } else {
308 esk = ciphertext;
309 }
310
311 let plaintext_len = if let Some(s) = sym_algo_hint {
312 Some(encrypted_cipher_octet.then(|| 1).unwrap_or(0)
313 + s.key_size()?
314 + checksummed.then(|| 2).unwrap_or(0))
315 } else {
316 None
317 };
318 let plain = decryptor.decrypt(esk, plaintext_len)?;
319 let key_rgn = encrypted_cipher_octet.then(|| 1).unwrap_or(0)
320 ..plain.len().saturating_sub(checksummed.then(|| 2).unwrap_or(0));
321 if encrypted_cipher_octet {
322 sym_algo = Some(plain[0].into());
323 }
324 let sym_algo = sym_algo.or(sym_algo_hint);
325
326 if let Some(sym_algo) = sym_algo {
327 if key_rgn.len() != sym_algo.key_size()? {
328 return Err(Error::MalformedPacket(
329 format!("session key has the wrong size (got: {}, expected: {})",
330 key_rgn.len(), sym_algo.key_size()?)).into())
331 }
332 }
333
334 let mut key: SessionKey = vec![0u8; key_rgn.len()].into();
335 key.copy_from_slice(&plain[key_rgn]);
336
337 if checksummed {
338 let our_checksum
339 = key.iter().map(|&x| x as usize).sum::<usize>() & 0xffff;
340 let their_checksum = (plain[plain.len() - 2] as usize) << 8
341 | (plain[plain.len() - 1] as usize);
342
343 if their_checksum != our_checksum {
344 return Err(Error::MalformedPacket(
345 "key checksum wrong".to_string()).into());
346 }
347 }
348 Ok((sym_algo, key))
349 }
350}
351
352#[cfg(test)]
353impl Arbitrary for super::PKESK {
354 fn arbitrary(g: &mut Gen) -> Self {
355 if bool::arbitrary(g) {
356 PKESK3::arbitrary(g).into()
357 } else {
358 PKESK6::arbitrary(g).into()
359 }
360 }
361}