sequoia_openpgp/packet/
pkesk.rs1#[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 Ok((false, seipdv1, false)),
146
147 a @ PublicKeyAlgorithm::RSASign |
148 a @ PublicKeyAlgorithm::DSA |
149 a @ PublicKeyAlgorithm::ECDSA |
150 a @ PublicKeyAlgorithm::EdDSA |
151 a @ PublicKeyAlgorithm::Ed25519 |
152 a @ PublicKeyAlgorithm::Ed448 |
153 a @ PublicKeyAlgorithm::Private(_) |
154 a @ PublicKeyAlgorithm::Unknown(_) =>
155 Err(Error::UnsupportedPublicKeyAlgorithm(a).into()),
156 }
157}
158
159
160impl packet::PKESK {
161 fn encrypt_common(algo: Option<SymmetricAlgorithm>,
162 session_key: &SessionKey,
163 recipient: &Key<key::UnspecifiedParts,
164 key::UnspecifiedRole>)
165 -> Result<Ciphertext>
166 {
167 let (checksummed, unencrypted_cipher_octet, encrypted_cipher_octet) =
168 classify_pk_algo(recipient.pk_algo(), algo.is_some())?;
169
170 let mut psk = Vec::with_capacity(
173 encrypted_cipher_octet.then(|| 1).unwrap_or(0)
174 + session_key.len()
175 + checksummed.then(|| 2).unwrap_or(0));
176 if let Some(algo) = algo {
177 if encrypted_cipher_octet {
178 psk.push(algo.into());
179 }
180 }
181 psk.extend_from_slice(session_key);
182
183 if checksummed {
184 let checksum = session_key
186 .iter()
187 .cloned()
188 .map(u16::from)
189 .fold(0u16, u16::wrapping_add);
190
191 psk.extend_from_slice(&checksum.to_be_bytes());
192 }
193
194 let psk: SessionKey = psk.into();
196 let mut esk = recipient.encrypt(&psk)?;
197
198 if let Some(algo) = algo {
199 if unencrypted_cipher_octet {
200 match esk {
201 Ciphertext::X25519 { ref mut key, .. } |
202 Ciphertext::X448 { ref mut key, .. } => {
203 let mut new_key = Vec::with_capacity(1 + key.len());
204 new_key.push(algo.into());
205 new_key.extend_from_slice(key);
206 *key = new_key.into();
207 },
208 _ => unreachable!("We only prepend the cipher octet \
209 for X25519 and X448"),
210 };
211 }
212 }
213
214 Ok(esk)
215 }
216
217 fn decrypt_common(ciphertext: &Ciphertext,
218 decryptor: &mut dyn Decryptor,
219 sym_algo_hint: Option<SymmetricAlgorithm>,
220 seipdv1: bool)
221 -> Result<(Option<SymmetricAlgorithm>, SessionKey)>
222 {
223 let (checksummed, unencrypted_cipher_octet, encrypted_cipher_octet) =
224 classify_pk_algo(decryptor.public().pk_algo(), seipdv1)?;
225
226 let mut sym_algo: Option<SymmetricAlgorithm> = None;
229 let modified_ciphertext;
230 let esk;
231 if unencrypted_cipher_octet {
232 match ciphertext {
233 Ciphertext::X25519 { e, key, } => {
234 sym_algo =
235 Some((*key.get(0).ok_or_else(
236 || Error::MalformedPacket("Short ESK".into()))?)
237 .into());
238 modified_ciphertext = Ciphertext::X25519 {
239 e: e.clone(),
240 key: key[1..].into(),
241 };
242 esk = &modified_ciphertext;
243 },
244 Ciphertext::X448 { e, key, } => {
245 sym_algo =
246 Some((*key.get(0).ok_or_else(
247 || Error::MalformedPacket("Short ESK".into()))?)
248 .into());
249 modified_ciphertext = Ciphertext::X448 {
250 e: e.clone(),
251 key: key[1..].into(),
252 };
253 esk = &modified_ciphertext;
254 },
255
256 _ => {
257 esk = ciphertext;
263 },
264 }
265 } else {
266 esk = ciphertext;
267 }
268
269 let plaintext_len = if let Some(s) = sym_algo_hint {
270 Some(encrypted_cipher_octet.then(|| 1).unwrap_or(0)
271 + s.key_size()?
272 + checksummed.then(|| 2).unwrap_or(0))
273 } else {
274 None
275 };
276 let plain = decryptor.decrypt(esk, plaintext_len)?;
277 let key_rgn = encrypted_cipher_octet.then(|| 1).unwrap_or(0)
278 ..plain.len().saturating_sub(checksummed.then(|| 2).unwrap_or(0));
279 if encrypted_cipher_octet {
280 sym_algo = Some(plain[0].into());
281 }
282 let sym_algo = sym_algo.or(sym_algo_hint);
283
284 if let Some(sym_algo) = sym_algo {
285 if key_rgn.len() != sym_algo.key_size()? {
286 return Err(Error::MalformedPacket(
287 format!("session key has the wrong size (got: {}, expected: {})",
288 key_rgn.len(), sym_algo.key_size()?)).into())
289 }
290 }
291
292 let mut key: SessionKey = vec![0u8; key_rgn.len()].into();
293 key.copy_from_slice(&plain[key_rgn]);
294
295 if checksummed {
296 let our_checksum
297 = key.iter().map(|&x| x as usize).sum::<usize>() & 0xffff;
298 let their_checksum = (plain[plain.len() - 2] as usize) << 8
299 | (plain[plain.len() - 1] as usize);
300
301 if their_checksum != our_checksum {
302 return Err(Error::MalformedPacket(
303 "key checksum wrong".to_string()).into());
304 }
305 }
306 Ok((sym_algo, key))
307 }
308}
309
310#[cfg(test)]
311impl Arbitrary for super::PKESK {
312 fn arbitrary(g: &mut Gen) -> Self {
313 if bool::arbitrary(g) {
314 PKESK3::arbitrary(g).into()
315 } else {
316 PKESK6::arbitrary(g).into()
317 }
318 }
319}