Skip to main content

bitwarden_crypto/safe/key_hierarchy/
content_encryption_key.rs

1use crate::{KeySlotIds, KeyStoreContext, SymmetricKeyAlgorithm};
2
3/// A content-encryption-key (CEK) - alternatively data-encryption-key (DEK) - a single-use
4/// symmetric key that encrypts content directly. It SHALL NOT be re-used for multiple encrypt
5/// operations.
6///
7/// See the `key_hierarchy` module documentation for its place in the key hierarchy.
8pub struct ContentEncryptionKey;
9
10impl ContentEncryptionKey {
11    /// Generates a fresh content-encryption-key, stores it in the key store context, and returns
12    /// its key id.
13    pub fn make<Ids: KeySlotIds>(ctx: &mut KeyStoreContext<Ids>) -> Ids::Symmetric {
14        // AES-256-GCM is used because a CEK is never reused: a new one is generated for each piece
15        // of content, and it only ever performs a single encryption before being used solely to
16        // decrypt that content. Because the key is unique per encryption, the 96-bit AES-256-GCM
17        // nonce never risks a collision under it, so the extended-nonce variant a reused key
18        // requires is unnecessary here.
19        ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256Gcm)
20    }
21
22    /// Returns whether the symmetric key `key_id` refers to uses an algorithm permitted for a
23    /// content-encryption-key. Returns `false` if the key is missing or uses an unsupported
24    /// algorithm.
25    #[allow(unused)]
26    pub(crate) fn is_key_algorithm_valid<Ids: KeySlotIds>(
27        ctx: &KeyStoreContext<Ids>,
28        key_id: Ids::Symmetric,
29    ) -> bool {
30        let Ok(algorithm) = ctx.get_symmetric_key_algorithm(key_id) else {
31            return false;
32        };
33        matches!(algorithm, SymmetricKeyAlgorithm::Aes256Gcm)
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40    use crate::{KeyStore, SymmetricCryptoKey, traits::tests::TestIds};
41
42    #[test]
43    fn make_generates_aes256_gcm_key_in_context() {
44        let key_store = KeyStore::<TestIds>::default();
45        let mut ctx = key_store.context_mut();
46
47        let cek_id = ContentEncryptionKey::make(&mut ctx);
48
49        let key = ctx.get_symmetric_key(cek_id).expect("CEK be stored");
50        assert!(matches!(key, SymmetricCryptoKey::Aes256GcmKey(_)));
51    }
52
53    #[test]
54    fn make_generates_distinct_keys() {
55        let key_store = KeyStore::<TestIds>::default();
56        let mut ctx = key_store.context_mut();
57
58        let first = ContentEncryptionKey::make(&mut ctx);
59        let second = ContentEncryptionKey::make(&mut ctx);
60
61        ctx.assert_symmetric_keys_not_equal(first, second);
62    }
63
64    #[test]
65    fn is_key_algorithm_valid_accepts_dek_algorithms() {
66        let key_store = KeyStore::<TestIds>::default();
67        let mut ctx = key_store.context_mut();
68
69        // We will add support for AES-256-CBC-HMAC in the future, to migrate to safe quicker.
70        #[allow(clippy::single_element_loop)]
71        for algorithm in [SymmetricKeyAlgorithm::Aes256Gcm] {
72            let key_id = ctx.make_symmetric_key(algorithm);
73            assert!(ContentEncryptionKey::is_key_algorithm_valid(&ctx, key_id));
74        }
75    }
76
77    #[test]
78    fn is_key_algorithm_valid_rejects_non_dek_algorithms() {
79        let key_store = KeyStore::<TestIds>::default();
80        let mut ctx = key_store.context_mut();
81
82        for algorithm in [
83            SymmetricKeyAlgorithm::XChaCha20Poly1305,
84            SymmetricKeyAlgorithm::XAes256Gcm,
85            SymmetricKeyAlgorithm::Aes256CbcHmac,
86        ] {
87            let key_id = ctx.make_symmetric_key(algorithm);
88            assert!(!ContentEncryptionKey::is_key_algorithm_valid(&ctx, key_id));
89        }
90    }
91}