vpgp/packet/
public_key_encrypted_session_key.rs

1use std::io;
2
3use byteorder::WriteBytesExt;
4use nom::bytes::streaming::take;
5use nom::combinator::{map, map_res};
6use nom::number::streaming::be_u8;
7use nom::sequence::pair;
8use rand::{CryptoRng, Rng};
9use zeroize::Zeroizing;
10
11use crate::crypto::checksum;
12use crate::crypto::public_key::PublicKeyAlgorithm;
13use crate::crypto::sym::SymmetricKeyAlgorithm;
14use crate::errors::{IResult, Result};
15use crate::packet::PacketTrait;
16use crate::ser::Serialize;
17use crate::types::{
18    mpi, EskType, Fingerprint, KeyId, KeyVersion, PkeskBytes, PkeskVersion, PublicKeyTrait,
19    PublicParams, Tag, Version,
20};
21
22/// Public Key Encrypted Session Key Packet (PKESK)
23/// <https://www.rfc-editor.org/rfc/rfc9580.html#name-public-key-encrypted-sessio>
24///
25/// A PKESK contains an encrypted session key that has been encrypted to a specific public key.
26/// PKESK are used in combination with a symmetric encryption container:
27///
28/// - V3 PKESK are used in combination with [version 1 Symmetrically Encrypted and Integrity
29///   Protected Data Packets](https://www.rfc-editor.org/rfc/rfc9580.html#name-version-1-symmetrically-enc).
30/// - V6 PKESK are used in combination with [version 2 Symmetrically Encrypted and Integrity
31///   Protected Data Packets](https://www.rfc-editor.org/rfc/rfc9580.html#name-version-2-symmetrically-enc).
32#[derive(Debug, Clone, PartialEq, Eq)]
33pub enum PublicKeyEncryptedSessionKey {
34    V3 {
35        packet_version: Version,
36        id: KeyId,
37        pk_algo: PublicKeyAlgorithm,
38        values: PkeskBytes,
39    },
40
41    V6 {
42        packet_version: Version,
43        fingerprint: Option<Fingerprint>,
44        pk_algo: PublicKeyAlgorithm,
45        values: PkeskBytes,
46    },
47
48    Other {
49        packet_version: Version,
50        version: u8,
51    },
52}
53
54impl PublicKeyEncryptedSessionKey {
55    /// Parses a `PublicKeyEncryptedSessionKey` packet from the given slice.
56    pub fn from_slice(version: Version, input: &[u8]) -> Result<Self> {
57        let (_, pk) = parse(version)(input)?;
58
59        Ok(pk)
60    }
61
62    /// Prepare the session key data for encryption in a PKESK.
63    ///
64    /// See <https://www.rfc-editor.org/rfc/rfc9580.html#name-public-key-encrypted-sessio>
65    fn prepare_session_key_for_encryption(
66        alg: Option<SymmetricKeyAlgorithm>, // set for pkesk v3
67        sk: &[u8],
68        pp: &PublicParams,
69    ) -> Zeroizing<Vec<u8>> {
70        let mut data = Zeroizing::new(Vec::with_capacity(1 + sk.len() + 2)); // max required capacity (for v3 and not-x22519/449)
71
72        // Prefix session key with symmetric key algorithm (for v3 PKESK)
73        if let Some(alg) = alg {
74            data.push(u8::from(alg));
75        }
76
77        // Add the raw session key data
78        data.extend_from_slice(sk);
79
80        // Appended a checksum of the session key (except for X25519 and X448)
81        match pp {
82            PublicParams::X25519 { .. } | PublicParams::X448 { .. } => {}
83            _ => data.extend_from_slice(&checksum::calculate_simple(sk).to_be_bytes()),
84        }
85
86        data
87    }
88
89    /// Encrypts the given session key to `pkey` as a v3 pkesk.
90    pub fn from_session_key_v3<R: CryptoRng + Rng>(
91        rng: R,
92        session_key: &[u8],
93        alg: SymmetricKeyAlgorithm,
94        pkey: &impl PublicKeyTrait,
95    ) -> Result<Self> {
96        // the symmetric key algorithm, the session key, and a checksum (for some algorithms)
97        let data =
98            Self::prepare_session_key_for_encryption(Some(alg), session_key, pkey.public_params());
99
100        let values = pkey.encrypt(rng, &data, EskType::V3_4)?;
101
102        Ok(PublicKeyEncryptedSessionKey::V3 {
103            packet_version: Default::default(),
104            id: pkey.key_id(),
105            pk_algo: pkey.algorithm(),
106            values,
107        })
108    }
109
110    /// Encrypts the given session key to `pkey` as a v6 pkesk.
111    pub fn from_session_key_v6<R: CryptoRng + Rng>(
112        rng: R,
113        session_key: &[u8],
114        pkey: &impl PublicKeyTrait,
115    ) -> Result<Self> {
116        // "An implementation MUST NOT generate ElGamal v6 PKESK packets."
117        // (See https://www.rfc-editor.org/rfc/rfc9580.html#section-5.1.4-6)
118        if pkey.algorithm() == PublicKeyAlgorithm::Elgamal {
119            bail!("ElGamal is not a legal encryption mechanism for v6 PKESK");
120        }
121
122        // the session key, and a checksum (for some algorithms)
123        let data =
124            Self::prepare_session_key_for_encryption(None, session_key, pkey.public_params());
125
126        let values = pkey.encrypt(rng, &data, EskType::V6)?;
127
128        Ok(PublicKeyEncryptedSessionKey::V6 {
129            packet_version: Default::default(),
130            fingerprint: Some(pkey.fingerprint()),
131            pk_algo: pkey.algorithm(),
132            values,
133        })
134    }
135
136    /// Check if a Key matches with this PKESK's target
137    /// - for v3: is PKESK key id the wildcard, or does it match the key id of `pkey`?
138    /// - for v6: is PKESK fingerprint the wildcard (represented as `None`), or does it match the fingerprint of `pkey`?
139    pub fn match_identity(&self, pkey: &impl PublicKeyTrait) -> bool {
140        match self {
141            Self::V3 { id, .. } => id.is_wildcard() || (id == &pkey.key_id()),
142            Self::V6 { fingerprint, .. } => {
143                if let Some(fingerprint) = fingerprint {
144                    fingerprint == &pkey.fingerprint()
145                } else {
146                    true // wildcard always matches
147                }
148            }
149            _ => false,
150        }
151    }
152
153    /// The Key ID in this PKESK. Only available for v3 PKESK (the function returns an Error for v6 PKESK).
154    ///
155    /// The Key ID may consist of all Zero-Bytes (if the PKESK was created for an anonymous recipient).
156    ///
157    /// See <https://www.rfc-editor.org/rfc/rfc9580.html#section-5.1.1-3.2>
158    pub fn id(&self) -> Result<&KeyId> {
159        match self {
160            Self::V3 { id, .. } => Ok(id),
161            _ => bail!("KeyID is only available for V3 PKESK"),
162        }
163    }
164
165    /// The Fingerprint in this PKESK. Only available for v6 PKESK (the function returns an Error for v3 PKESK).
166    ///
167    /// Additionally, the Fingerprint may be `None` (if the PKESK was created for an anonymous recipient).
168    ///
169    /// See <https://www.rfc-editor.org/rfc/rfc9580.html#section-5.1.2-3.2>
170    pub fn fingerprint(&self) -> Result<Option<&Fingerprint>> {
171        match self {
172            Self::V6 { fingerprint, .. } => Ok(fingerprint.as_ref()),
173            _ => bail!("Fingerprint is only available for V6 PKESK"),
174        }
175    }
176
177    /// The raw encrypted session key data inside this PKESK.
178    /// This data is algorithm specific.
179    ///
180    /// See <https://www.rfc-editor.org/rfc/rfc9580.html#name-algorithm-specific-fields-f>
181    /// and the following sections.
182    pub fn values(&self) -> Result<&PkeskBytes> {
183        match self {
184            Self::V3 { values, .. } | Self::V6 { values, .. } => Ok(values),
185            Self::Other { version, .. } => bail!("Unsupported PKESK version {}", version),
186        }
187    }
188
189    /// The public key algorithm used in this PKESK.
190    ///
191    /// An error is returned for unsupported PKESK versions (any version except "3" and "6").
192    pub fn algorithm(&self) -> Result<PublicKeyAlgorithm> {
193        match self {
194            Self::V3 { pk_algo, .. } | Self::V6 { pk_algo, .. } => Ok(*pk_algo),
195            _ => bail!("PublicKeyAlgorithm unknown for {:?}", self),
196        }
197    }
198
199    /// The version of this PKESK (currently "3" and "6" are expected values)
200    pub fn version(&self) -> PkeskVersion {
201        match self {
202            Self::V3 { .. } => PkeskVersion::V3,
203            Self::V6 { .. } => PkeskVersion::V6,
204            Self::Other { version, .. } => PkeskVersion::Other(*version),
205        }
206    }
207}
208
209fn parse_esk<'i>(
210    alg: &PublicKeyAlgorithm,
211    i: &'i [u8],
212    version: u8,
213) -> IResult<&'i [u8], PkeskBytes> {
214    match alg {
215        PublicKeyAlgorithm::RSA | PublicKeyAlgorithm::RSASign | PublicKeyAlgorithm::RSAEncrypt => {
216            map(mpi, |v| PkeskBytes::Rsa { mpi: v.to_owned() })(i)
217        }
218        PublicKeyAlgorithm::Elgamal | PublicKeyAlgorithm::ElgamalSign => {
219            map(pair(mpi, mpi), |(first, second)| PkeskBytes::Elgamal {
220                first: first.to_owned(),
221                second: second.to_owned(),
222            })(i)
223        }
224        PublicKeyAlgorithm::ECDSA | PublicKeyAlgorithm::DSA | PublicKeyAlgorithm::DiffieHellman => {
225            Ok((i, PkeskBytes::Other))
226        }
227        PublicKeyAlgorithm::ECDH => {
228            let (i, a) = mpi(i)?;
229            let (i, blen) = be_u8(i)?;
230            let (i, b) = take(blen)(i)?;
231
232            Ok((
233                i,
234                PkeskBytes::Ecdh {
235                    public_point: a.to_owned(),
236                    encrypted_session_key: b.into(),
237                },
238            ))
239        }
240        PublicKeyAlgorithm::X25519 => {
241            // 32 octets representing an ephemeral X25519 public key.
242            let (i, ephemeral_public) = nom::bytes::complete::take(32u8)(i)?;
243
244            // A one-octet size of the following fields.
245            let (i, len) = be_u8(i)?;
246            if len == 0 {
247                return Err(nom::Err::Error(crate::errors::Error::InvalidInput));
248            }
249
250            // The one-octet algorithm identifier, if it was passed (in the case of a v3 PKESK packet).
251            let (i, sym_alg) = if version == 3 {
252                map(be_u8, SymmetricKeyAlgorithm::from)(i).map(|(i, alg)| (i, Some(alg)))?
253            } else {
254                (i, None)
255            };
256
257            let skey_len = if version == 3 { len - 1 } else { len };
258
259            // The encrypted session key.
260            let (i, esk) = nom::bytes::complete::take(skey_len)(i)?;
261
262            Ok((
263                i,
264                PkeskBytes::X25519 {
265                    ephemeral: ephemeral_public.try_into().expect("32"),
266                    sym_alg,
267                    session_key: esk.to_vec(),
268                },
269            ))
270        }
271        PublicKeyAlgorithm::X448 => {
272            // 56 octets representing an ephemeral X448 public key.
273            let (i, ephemeral_public) = nom::bytes::complete::take(56u8)(i)?;
274
275            // A one-octet size of the following fields.
276            let (i, len) = be_u8(i)?;
277            if len == 0 {
278                return Err(nom::Err::Error(crate::errors::Error::InvalidInput));
279            }
280
281            // The one-octet algorithm identifier, if it was passed (in the case of a v3 PKESK packet).
282            let (i, sym_alg) = if version == 3 {
283                map(be_u8, SymmetricKeyAlgorithm::from)(i).map(|(i, alg)| (i, Some(alg)))?
284            } else {
285                (i, None)
286            };
287
288            let skey_len = if version == 3 { len - 1 } else { len };
289
290            // The encrypted session key.
291            let (i, esk) = nom::bytes::complete::take(skey_len)(i)?;
292
293            Ok((
294                i,
295                PkeskBytes::X448 {
296                    ephemeral: ephemeral_public.try_into().expect("56"),
297                    sym_alg,
298                    session_key: esk.to_vec(),
299                },
300            ))
301        }
302        PublicKeyAlgorithm::Unknown(_) => Ok((i, PkeskBytes::Other)), // we don't know the format of this data
303        _ => Err(nom::Err::Error(crate::errors::Error::ParsingError(
304            nom::error::ErrorKind::Switch,
305        ))),
306    }
307}
308
309/// Parses a Public-Key Encrypted Session Key Packets.
310fn parse(
311    packet_version: Version,
312) -> impl Fn(&[u8]) -> IResult<&[u8], PublicKeyEncryptedSessionKey> {
313    move |i: &[u8]| {
314        // version, 3 and 6 are allowed
315        let (i, version) = be_u8(i)?;
316
317        if version == 3 {
318            // the key id this maps to
319            let (i, id) = map_res(take(8u8), KeyId::from_slice)(i)?;
320            // the public key algorithm
321            let (i, pk_algo) = map(be_u8, PublicKeyAlgorithm::from)(i)?;
322
323            // key algorithm specific data
324            let (i, values) = parse_esk(&pk_algo, i, version)?;
325
326            Ok((
327                i,
328                PublicKeyEncryptedSessionKey::V3 {
329                    packet_version,
330                    id,
331                    pk_algo,
332                    values,
333                },
334            ))
335        } else if version == 6 {
336            // A one-octet size of the following two fields. This size may be zero,
337            // if the key version number field and the fingerprint field are omitted
338            // for an "anonymous recipient" (see Section 5.1.8).
339            let (i, len) = be_u8(i)?;
340
341            let (i, fingerprint) = match len {
342                0 => (i, None),
343                _ => {
344                    // A one octet key version number.
345                    let (i, v) = map(be_u8, KeyVersion::from)(i)?;
346
347                    // The fingerprint of the public key or subkey to which the session key is encrypted.
348                    // Note that the length N of the fingerprint for a version 4 key is 20 octets;
349                    // for a version 6 key N is 32.
350                    let (i, fp) = nom::bytes::complete::take(len - 1)(i)?;
351
352                    let fp = Fingerprint::new(v, fp)?;
353
354                    (i, Some(fp))
355                }
356            };
357
358            // A one-octet number giving the public-key algorithm used.
359            let (i, pk_algo) = map(be_u8, PublicKeyAlgorithm::from)(i)?;
360
361            // A series of values comprising the encrypted session key. This is algorithm-specific and described below.
362            let (i, values) = parse_esk(&pk_algo, i, version)?;
363
364            Ok((
365                i,
366                PublicKeyEncryptedSessionKey::V6 {
367                    packet_version,
368                    fingerprint,
369                    pk_algo,
370                    values,
371                },
372            ))
373        } else {
374            Ok((
375                i,
376                PublicKeyEncryptedSessionKey::Other {
377                    packet_version,
378                    version,
379                },
380            ))
381        }
382    }
383}
384
385impl Serialize for PublicKeyEncryptedSessionKey {
386    fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
387        writer.write_u8(self.version().into())?;
388
389        match self {
390            PublicKeyEncryptedSessionKey::V3 { id, .. } => writer.write_all(id.as_ref())?,
391            PublicKeyEncryptedSessionKey::V6 { fingerprint, .. } => {
392                // A one-octet size of the following two fields.
393                // This size may be zero, if the key version number field and the fingerprint field
394                // are omitted for an "anonymous recipient" (see Section 5.1.8).
395                match fingerprint {
396                    Some(fingerprint) => {
397                        let len = fingerprint.len() + 1;
398                        writer.write_u8(len.try_into()?)?;
399
400                        // A one octet key version number.
401                        match fingerprint.version() {
402                            Some(version) => writer.write_u8(version.into())?,
403                            None => {
404                                bail!("Fingerprint without version information {:?}", fingerprint)
405                            }
406                        }
407
408                        // The fingerprint of the public key or subkey to which the session key is encrypted.
409                        // Note that the length N of the fingerprint for a version 4 key is 20 octets;
410                        // for a version 6 key N is 32.
411                        writer.write_all(fingerprint.as_bytes())?;
412                    }
413                    _ => writer.write_u8(0)?,
414                }
415            }
416            PublicKeyEncryptedSessionKey::Other { .. } => todo!(),
417        }
418
419        let algorithm = self.algorithm()?;
420        writer.write_u8(algorithm.into())?;
421
422        match (algorithm, self.values()?) {
423            (
424                PublicKeyAlgorithm::RSA
425                | PublicKeyAlgorithm::RSASign
426                | PublicKeyAlgorithm::RSAEncrypt,
427                PkeskBytes::Rsa { mpi },
428            ) => {
429                mpi.to_writer(writer)?;
430            }
431            (
432                PublicKeyAlgorithm::Elgamal | PublicKeyAlgorithm::ElgamalSign,
433                PkeskBytes::Elgamal { first, second },
434            ) => {
435                first.to_writer(writer)?;
436                second.to_writer(writer)?;
437            }
438            (
439                PublicKeyAlgorithm::ECDH,
440                PkeskBytes::Ecdh {
441                    public_point,
442                    encrypted_session_key,
443                },
444            ) => {
445                public_point.to_writer(writer)?;
446
447                // length of session key as one octet
448                writer.write_u8(encrypted_session_key.len().try_into()?)?;
449
450                writer.write_all(encrypted_session_key)?;
451            }
452            (
453                PublicKeyAlgorithm::X25519,
454                PkeskBytes::X25519 {
455                    ephemeral,
456                    sym_alg,
457                    session_key,
458                },
459            ) => {
460                writer.write_all(ephemeral)?;
461
462                // Unlike the other public-key algorithms, in the case of a v3 PKESK packet,
463                // the symmetric algorithm ID is not encrypted [for X25519].
464                //
465                // https://www.rfc-editor.org/rfc/rfc9580.html#name-algorithm-specific-fields-for-
466                if let Some(sym_alg) = sym_alg {
467                    ensure!(
468                        matches!(self, PublicKeyEncryptedSessionKey::V3 { .. }),
469                        "Inconsistent: X25519 SymmetricKeyAlgorithm is set for {:?} PKESK",
470                        self.version()
471                    );
472
473                    // len: algo octet + session_key len
474                    writer.write_u8((session_key.len() + 1).try_into()?)?;
475
476                    writer.write_u8((*sym_alg).into())?;
477                } else {
478                    ensure!(
479                        matches!(self, PublicKeyEncryptedSessionKey::V6 { .. }),
480                        "Inconsistent: X25519 SymmetricKeyAlgorithm is unset for {:?} PKESK",
481                        self.version()
482                    );
483
484                    // len: esk len
485                    writer.write_u8(session_key.len().try_into()?)?;
486
487                    // For v6 PKESK, sym_alg is None, and the algorithm is not written here
488                }
489
490                writer.write_all(session_key)?; // encrypted session key
491            }
492            (
493                PublicKeyAlgorithm::X448,
494                PkeskBytes::X448 {
495                    ephemeral,
496                    sym_alg,
497                    session_key,
498                },
499            ) => {
500                writer.write_all(ephemeral)?;
501
502                // Unlike the other public-key algorithms, in the case of a v3 PKESK packet,
503                // the symmetric algorithm ID is not encrypted [for X448].
504                //
505                // https://www.rfc-editor.org/rfc/rfc9580.html#name-algorithm-specific-fields-for-x
506                if let Some(sym_alg) = sym_alg {
507                    ensure!(
508                        matches!(self, PublicKeyEncryptedSessionKey::V3 { .. }),
509                        "Inconsistent: X448 SymmetricKeyAlgorithm is set for {:?} PKESK",
510                        self.version()
511                    );
512
513                    // len: algo + esk len
514                    writer.write_u8((session_key.len() + 1).try_into()?)?;
515
516                    writer.write_u8((*sym_alg).into())?;
517                } else {
518                    ensure!(
519                        matches!(self, PublicKeyEncryptedSessionKey::V6 { .. }),
520                        "Inconsistent: X448 SymmetricKeyAlgorithm is unset for {:?} PKESK",
521                        self.version()
522                    );
523
524                    // len: algo octet + session_key len
525                    writer.write_u8(session_key.len().try_into()?)?;
526
527                    // For v6 PKESK, sym_alg is None, and the algorithm is not written here
528                }
529
530                writer.write_all(session_key)?; // encrypted session key
531            }
532            (alg, _) => {
533                bail!("failed to write EskBytes for {:?}", alg);
534            }
535        }
536
537        Ok(())
538    }
539}
540
541impl PacketTrait for PublicKeyEncryptedSessionKey {
542    fn packet_version(&self) -> Version {
543        match self {
544            Self::V3 { packet_version, .. }
545            | Self::V6 { packet_version, .. }
546            | Self::Other { packet_version, .. } => *packet_version,
547        }
548    }
549    fn tag(&self) -> Tag {
550        Tag::PublicKeyEncryptedSessionKey
551    }
552}