cmail_rpgp/composed/message/
decrypt.rs

1use log::debug;
2use zeroize::{Zeroize, ZeroizeOnDrop};
3
4use crate::crypto::sym::SymmetricKeyAlgorithm;
5use crate::errors::Result;
6use crate::packet::SymKeyEncryptedSessionKey;
7use crate::types::{EskType, PkeskBytes, SecretKeyRepr, SecretKeyTrait, SkeskVersion};
8
9/// Decrypts session key using secret key.
10pub fn decrypt_session_key<F, L>(
11    locked_key: &L,
12    key_pw: F,
13    values: &PkeskBytes,
14    typ: EskType,
15) -> Result<PlainSessionKey>
16where
17    F: FnOnce() -> String,
18    L: SecretKeyTrait<Unlocked = SecretKeyRepr>,
19{
20    debug!("decrypt session key");
21
22    locked_key.unlock(key_pw, |priv_key| priv_key.decrypt(values, typ, locked_key))
23}
24
25/// Decrypted session key.
26///
27/// A v3/v4 session key can be used  v1 SEIPD (and historically with SED packets).
28/// A v6 session key can only be used with a v2 SEIPD.
29///
30/// <https://www.rfc-editor.org/rfc/rfc9580.html#name-packet-versions-in-encrypte>
31///
32/// (Note that SED packets are malleable. They are historical and considered dangerous!
33/// They MUST NOT be produced and decryption is also discouraged:
34/// <https://www.rfc-editor.org/rfc/rfc9580.html#sed>)
35#[derive(derive_more::Debug, Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
36pub enum PlainSessionKey {
37    /// A session key from a v3 PKESK or a v4 SKESK
38    ///
39    /// (Note: for historical reasons, the OpenPGP format doesn't specify a v4 PKESK or a V3 SKESK)
40    V3_4 {
41        sym_alg: SymmetricKeyAlgorithm,
42        #[debug("..")]
43        key: Vec<u8>,
44    },
45
46    V5 {
47        #[debug("..")]
48        key: Vec<u8>,
49    },
50
51    /// A session key from a v6 PKESK or a v6 SKESK
52    V6 {
53        #[debug("..")]
54        key: Vec<u8>,
55    },
56}
57
58/// Decrypts session key from SKESK packet.
59///
60/// Returns decrypted or derived session key
61/// and symmetric algorithm of the key.
62pub fn decrypt_session_key_with_password<F>(
63    packet: &SymKeyEncryptedSessionKey,
64    msg_pw: F,
65) -> Result<PlainSessionKey>
66where
67    F: FnOnce() -> String,
68{
69    debug!("decrypt session key with password");
70
71    let packet_algorithm = packet.sym_algorithm();
72    ensure!(
73        packet_algorithm != SymmetricKeyAlgorithm::Plaintext,
74        "SKESK packet encryption algorithm cannot be plaintext"
75    );
76
77    // Implementations MUST NOT decrypt a secret using MD5, SHA-1, or RIPEMD-160 as a hash function
78    // in an S2K KDF in a version 6 (or later) packet.
79    //
80    // (See https://www.rfc-editor.org/rfc/rfc9580.html#section-9.5-3)
81    if packet.version() == SkeskVersion::V6 {
82        ensure!(
83            !packet.s2k().known_weak_hash_algo(),
84            "Weak hash algorithm in S2K not allowed for v6 {:?}",
85            packet.s2k()
86        )
87    }
88
89    let key = packet
90        .s2k()
91        .derive_key(&msg_pw(), packet_algorithm.key_size())?;
92
93    debug!("derived key: {}", hex::encode(&key));
94    if packet.encrypted_key().is_none() {
95        // There is no encrypted session key.
96        //
97        // S2K-derived key is the session key.
98        return Ok(PlainSessionKey::V3_4 {
99            key,
100            sym_alg: packet_algorithm,
101        });
102    }
103
104    let decrypted_key = packet.decrypt(&key)?;
105
106    Ok(decrypted_key)
107}