bitwarden_crypto/keys/
symmetric_crypto_key.rs

1use std::{cmp::max, pin::Pin};
2
3use base64::{engine::general_purpose::STANDARD, Engine};
4use coset::{iana::KeyOperation, CborSerializable, RegisteredLabelWithPrivate};
5use generic_array::GenericArray;
6use rand::Rng;
7#[cfg(test)]
8use rand::SeedableRng;
9#[cfg(test)]
10use rand_chacha::ChaChaRng;
11#[cfg(test)]
12use sha2::Digest;
13use subtle::{Choice, ConstantTimeEq};
14use typenum::U32;
15use zeroize::{Zeroize, ZeroizeOnDrop};
16
17use super::{
18    key_encryptable::CryptoKey,
19    key_id::{KeyId, KEY_ID_SIZE},
20};
21use crate::{cose, CryptoError};
22
23/// [Aes256CbcKey] is a symmetric encryption key, consisting of one 256-bit key,
24/// used to decrypt legacy type 0 enc strings. The data is not authenticated
25/// so this should be used with caution, and removed where possible.
26#[derive(ZeroizeOnDrop, Clone)]
27pub struct Aes256CbcKey {
28    /// Uses a pinned heap data structure, as noted in [Pinned heap data][crate#pinned-heap-data]
29    pub(crate) enc_key: Pin<Box<GenericArray<u8, U32>>>,
30}
31
32impl ConstantTimeEq for Aes256CbcKey {
33    fn ct_eq(&self, other: &Self) -> Choice {
34        self.enc_key.ct_eq(&other.enc_key)
35    }
36}
37
38impl PartialEq for Aes256CbcKey {
39    fn eq(&self, other: &Self) -> bool {
40        self.ct_eq(other).into()
41    }
42}
43
44/// [Aes256CbcHmacKey] is a symmetric encryption key consisting
45/// of two 256-bit keys, one for encryption and one for MAC
46#[derive(ZeroizeOnDrop, Clone)]
47pub struct Aes256CbcHmacKey {
48    /// Uses a pinned heap data structure, as noted in [Pinned heap data][crate#pinned-heap-data]
49    pub(crate) enc_key: Pin<Box<GenericArray<u8, U32>>>,
50    /// Uses a pinned heap data structure, as noted in [Pinned heap data][crate#pinned-heap-data]
51    pub(crate) mac_key: Pin<Box<GenericArray<u8, U32>>>,
52}
53
54impl ConstantTimeEq for Aes256CbcHmacKey {
55    fn ct_eq(&self, other: &Self) -> Choice {
56        self.enc_key.ct_eq(&other.enc_key) & self.mac_key.ct_eq(&other.mac_key)
57    }
58}
59
60impl PartialEq for Aes256CbcHmacKey {
61    fn eq(&self, other: &Self) -> bool {
62        self.ct_eq(other).into()
63    }
64}
65
66/// [XChaCha20Poly1305Key] is a symmetric encryption key consisting
67/// of one 256-bit key, and contains a key id. In contrast to the
68/// [Aes256CbcKey] and [Aes256CbcHmacKey], this key type is used to create
69/// CoseEncrypt0 messages.
70#[derive(Zeroize, Clone)]
71pub struct XChaCha20Poly1305Key {
72    pub(crate) key_id: [u8; KEY_ID_SIZE],
73    pub(crate) enc_key: Pin<Box<GenericArray<u8, U32>>>,
74}
75
76impl ConstantTimeEq for XChaCha20Poly1305Key {
77    fn ct_eq(&self, other: &Self) -> Choice {
78        self.enc_key.ct_eq(&other.enc_key) & self.key_id.ct_eq(&other.key_id)
79    }
80}
81
82impl PartialEq for XChaCha20Poly1305Key {
83    fn eq(&self, other: &Self) -> bool {
84        self.ct_eq(other).into()
85    }
86}
87
88/// A symmetric encryption key. Used to encrypt and decrypt [`EncString`](crate::EncString)
89#[derive(ZeroizeOnDrop, Clone)]
90pub enum SymmetricCryptoKey {
91    Aes256CbcKey(Aes256CbcKey),
92    Aes256CbcHmacKey(Aes256CbcHmacKey),
93    /// Data encrypted by XChaCha20Poly1305Key keys has type
94    /// [`Cose_Encrypt0_B64`](crate::EncString::Cose_Encrypt0_B64)
95    XChaCha20Poly1305Key(XChaCha20Poly1305Key),
96}
97
98impl SymmetricCryptoKey {
99    // enc type 0 old static format
100    const AES256_CBC_KEY_LEN: usize = 32;
101    // enc type 2 old static format
102    const AES256_CBC_HMAC_KEY_LEN: usize = 64;
103
104    /// Generate a new random AES256_CBC [SymmetricCryptoKey]
105    ///
106    /// WARNING: This function should only be used with a proper cryptographic RNG. If you do not
107    /// have a good reason for using this function, use
108    /// [SymmetricCryptoKey::make_aes256_cbc_hmac_key] instead.
109    pub(crate) fn make_aes256_cbc_hmac_key_internal(
110        mut rng: impl rand::RngCore + rand::CryptoRng,
111    ) -> Self {
112        let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
113        let mut mac_key = Box::pin(GenericArray::<u8, U32>::default());
114
115        rng.fill(enc_key.as_mut_slice());
116        rng.fill(mac_key.as_mut_slice());
117
118        Self::Aes256CbcHmacKey(Aes256CbcHmacKey { enc_key, mac_key })
119    }
120
121    /// Generate a new random AES256_CBC_HMAC [SymmetricCryptoKey]
122    pub fn make_aes256_cbc_hmac_key() -> Self {
123        let rng = rand::thread_rng();
124        Self::make_aes256_cbc_hmac_key_internal(rng)
125    }
126
127    /// Generate a new random XChaCha20Poly1305 [SymmetricCryptoKey]
128    pub fn make_xchacha20_poly1305_key() -> Self {
129        let mut rng = rand::thread_rng();
130        let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
131        rng.fill(enc_key.as_mut_slice());
132        Self::XChaCha20Poly1305Key(XChaCha20Poly1305Key {
133            enc_key,
134            key_id: KeyId::make().into(),
135        })
136    }
137
138    /// Encodes the key to a byte array representation, that is separated by size.
139    /// [SymmetricCryptoKey::Aes256CbcHmacKey] and [SymmetricCryptoKey::Aes256CbcKey] are
140    /// encoded as 64 and 32 bytes respectively. [SymmetricCryptoKey::XChaCha20Poly1305Key]
141    /// is encoded as at least 65 bytes, using padding.
142    ///
143    /// This can be used for storage and transmission in the old byte array format.
144    /// When the wrapping key is a COSE key, and the wrapped key is a COSE key, then this should
145    /// not use the byte representation but instead use the COSE key representation.
146    pub fn to_encoded(&self) -> Vec<u8> {
147        let mut encoded_key = self.to_encoded_raw();
148        match self {
149            Self::Aes256CbcKey(_) | Self::Aes256CbcHmacKey(_) => encoded_key,
150            Self::XChaCha20Poly1305Key(_) => {
151                pad_key(&mut encoded_key, Self::AES256_CBC_HMAC_KEY_LEN + 1);
152                encoded_key
153            }
154        }
155    }
156
157    /// Generate a new random [SymmetricCryptoKey] for unit tests. Note: DO NOT USE THIS
158    /// IN PRODUCTION CODE.
159    #[cfg(test)]
160    pub fn generate_seeded_for_unit_tests(seed: &str) -> Self {
161        // Keep this separate from the other generate function to not break test vectors.
162        let mut seeded_rng = ChaChaRng::from_seed(sha2::Sha256::digest(seed.as_bytes()).into());
163        let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
164        let mut mac_key = Box::pin(GenericArray::<u8, U32>::default());
165
166        seeded_rng.fill(enc_key.as_mut_slice());
167        seeded_rng.fill(mac_key.as_mut_slice());
168
169        SymmetricCryptoKey::Aes256CbcHmacKey(Aes256CbcHmacKey { enc_key, mac_key })
170    }
171
172    /// Creates the byte representation of the key, without any padding. This should not
173    /// be used directly for creating serialized key representations, instead,
174    /// [SymmetricCryptoKey::to_encoded] should be used.
175    ///
176    /// [SymmetricCryptoKey::Aes256CbcHmacKey] and [SymmetricCryptoKey::Aes256CbcKey] are
177    /// encoded as 64 and 32 byte arrays respectively, representing the key bytes directly.
178    /// [SymmetricCryptoKey::XChaCha20Poly1305Key] is encoded as a COSE key, serialized to a byte
179    /// array. The COSE key can be either directly encrypted using COSE, where the content
180    /// format hints an the key type, or can be represented as a byte array, if padded to be
181    /// larger than the byte array representation of the other key types using the
182    /// aforementioned [SymmetricCryptoKey::to_encoded] function.
183    pub(crate) fn to_encoded_raw(&self) -> Vec<u8> {
184        match self {
185            Self::Aes256CbcKey(key) => key.enc_key.to_vec(),
186            Self::Aes256CbcHmacKey(key) => {
187                let mut buf = Vec::with_capacity(64);
188                buf.extend_from_slice(&key.enc_key);
189                buf.extend_from_slice(&key.mac_key);
190                buf
191            }
192            Self::XChaCha20Poly1305Key(key) => {
193                let builder = coset::CoseKeyBuilder::new_symmetric_key(key.enc_key.to_vec());
194                let mut cose_key = builder
195                    .key_id(key.key_id.to_vec())
196                    .add_key_op(KeyOperation::Decrypt)
197                    .add_key_op(KeyOperation::Encrypt)
198                    .add_key_op(KeyOperation::WrapKey)
199                    .add_key_op(KeyOperation::UnwrapKey)
200                    .build();
201                cose_key.alg = Some(RegisteredLabelWithPrivate::PrivateUse(
202                    cose::XCHACHA20_POLY1305,
203                ));
204                cose_key
205                    .to_vec()
206                    .expect("cose key serialization should not fail")
207            }
208        }
209    }
210
211    pub fn to_base64(&self) -> String {
212        STANDARD.encode(self.to_encoded())
213    }
214}
215
216impl ConstantTimeEq for SymmetricCryptoKey {
217    /// Note: This is constant time with respect to comparing two keys of the same type, but not
218    /// constant type with respect to the fact that different keys are compared. If two types of
219    /// different keys are compared, then this does have different timing.
220    fn ct_eq(&self, other: &SymmetricCryptoKey) -> Choice {
221        use SymmetricCryptoKey::*;
222        match (self, other) {
223            (Aes256CbcKey(a), Aes256CbcKey(b)) => a.ct_eq(b),
224            (Aes256CbcKey(_), _) => Choice::from(0),
225
226            (Aes256CbcHmacKey(a), Aes256CbcHmacKey(b)) => a.ct_eq(b),
227            (Aes256CbcHmacKey(_), _) => Choice::from(0),
228
229            (XChaCha20Poly1305Key(a), XChaCha20Poly1305Key(b)) => a.ct_eq(b),
230            (XChaCha20Poly1305Key(_), _) => Choice::from(0),
231        }
232    }
233}
234
235impl PartialEq for SymmetricCryptoKey {
236    fn eq(&self, other: &Self) -> bool {
237        self.ct_eq(other).into()
238    }
239}
240
241impl TryFrom<String> for SymmetricCryptoKey {
242    type Error = CryptoError;
243
244    fn try_from(value: String) -> Result<Self, Self::Error> {
245        let b = STANDARD
246            .decode(value)
247            .map_err(|_| CryptoError::InvalidKey)?;
248        Self::try_from(b)
249    }
250}
251
252impl TryFrom<Vec<u8>> for SymmetricCryptoKey {
253    type Error = CryptoError;
254
255    fn try_from(mut value: Vec<u8>) -> Result<Self, Self::Error> {
256        Self::try_from(value.as_mut_slice())
257    }
258}
259
260impl TryFrom<&mut [u8]> for SymmetricCryptoKey {
261    type Error = CryptoError;
262
263    /// Note: This function takes the byte slice by mutable reference and will zero out all
264    /// the data in it. This is to prevent the key from being left in memory.
265    fn try_from(value: &mut [u8]) -> Result<Self, Self::Error> {
266        // Raw byte serialized keys are either 32, 64, or more bytes long. If they are 32/64, they
267        // are the raw serializations of the AES256-CBC, and AES256-CBC-HMAC keys. If they
268        // are longer, they are COSE keys. The COSE keys are padded to the minimum length of
269        // 65 bytes, when serialized to raw byte arrays.
270        let result = if value.len() == Self::AES256_CBC_HMAC_KEY_LEN {
271            let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
272            let mut mac_key = Box::pin(GenericArray::<u8, U32>::default());
273
274            enc_key.copy_from_slice(&value[..32]);
275            mac_key.copy_from_slice(&value[32..]);
276
277            Ok(Self::Aes256CbcHmacKey(Aes256CbcHmacKey {
278                enc_key,
279                mac_key,
280            }))
281        } else if value.len() == Self::AES256_CBC_KEY_LEN {
282            let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
283
284            enc_key.copy_from_slice(&value[..Self::AES256_CBC_KEY_LEN]);
285
286            Ok(Self::Aes256CbcKey(Aes256CbcKey { enc_key }))
287        } else if value.len() > Self::AES256_CBC_HMAC_KEY_LEN {
288            let unpadded_value = unpad_key(value)?;
289            let cose_key =
290                coset::CoseKey::from_slice(unpadded_value).map_err(|_| CryptoError::InvalidKey)?;
291            SymmetricCryptoKey::try_from(&cose_key)
292        } else {
293            Err(CryptoError::InvalidKeyLen)
294        };
295
296        value.zeroize();
297        result
298    }
299}
300
301impl CryptoKey for SymmetricCryptoKey {}
302
303// We manually implement these to make sure we don't print any sensitive data
304impl std::fmt::Debug for SymmetricCryptoKey {
305    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
306        f.debug_struct("SymmetricCryptoKey")
307            .field(
308                "inner_type",
309                match self {
310                    SymmetricCryptoKey::Aes256CbcKey(key) => key,
311                    SymmetricCryptoKey::Aes256CbcHmacKey(key) => key,
312                    SymmetricCryptoKey::XChaCha20Poly1305Key(key) => key,
313                },
314            )
315            .finish()
316    }
317}
318
319impl std::fmt::Debug for Aes256CbcKey {
320    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
321        f.debug_struct("Aes256CbcKey").finish()
322    }
323}
324
325impl std::fmt::Debug for Aes256CbcHmacKey {
326    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
327        f.debug_struct("Aes256CbcHmacKey").finish()
328    }
329}
330
331impl std::fmt::Debug for XChaCha20Poly1305Key {
332    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
333        f.debug_struct("XChaCha20Poly1305Key")
334            .field("key_id", &self.key_id)
335            .finish()
336    }
337}
338
339/// Pad a key to a minimum length using PKCS7-like padding.
340/// The last N bytes of the padded bytes all have the value N.
341/// For example, padded to size 4, the value 0,0 becomes 0,0,2,2.
342///
343/// Keys that have the type [SymmetricCryptoKey::XChaCha20Poly1305Key] must be distinguishable
344/// from [SymmetricCryptoKey::Aes256CbcHmacKey] keys, when both are encoded as byte arrays
345/// with no additional content format included in the encoding message. For this reason, the
346/// padding is used to make sure that the byte representation uniquely separates the keys by
347/// size of the byte array. The previous key types [SymmetricCryptoKey::Aes256CbcHmacKey] and
348/// [SymmetricCryptoKey::Aes256CbcKey] are 64 and 32 bytes long respectively.
349fn pad_key(key_bytes: &mut Vec<u8>, min_length: usize) {
350    // at least 1 byte of padding is required
351    let pad_bytes = min_length.saturating_sub(key_bytes.len()).max(1);
352    let padded_length = max(min_length, key_bytes.len() + 1);
353    key_bytes.resize(padded_length, pad_bytes as u8);
354}
355
356/// Unpad a key that is padded using the PKCS7-like padding defined by [pad_key].
357/// The last N bytes of the padded bytes all have the value N.
358/// For example, padded to size 4, the value 0,0 becomes 0,0,2,2.
359///
360/// Keys that have the type [SymmetricCryptoKey::XChaCha20Poly1305Key] must be distinguishable
361/// from [SymmetricCryptoKey::Aes256CbcHmacKey] keys, when both are encoded as byte arrays
362/// with no additional content format included in the encoding message. For this reason, the
363/// padding is used to make sure that the byte representation uniquely separates the keys by
364/// size of the byte array the previous key types [SymmetricCryptoKey::Aes256CbcHmacKey] and
365/// [SymmetricCryptoKey::Aes256CbcKey] are 64 and 32 bytes long respectively.
366fn unpad_key(key_bytes: &[u8]) -> Result<&[u8], CryptoError> {
367    let pad_len = *key_bytes.last().ok_or(CryptoError::InvalidKey)? as usize;
368    if pad_len >= key_bytes.len() {
369        return Err(CryptoError::InvalidKey);
370    }
371    Ok(key_bytes[..key_bytes.len() - pad_len].as_ref())
372}
373
374#[cfg(test)]
375pub fn derive_symmetric_key(name: &str) -> Aes256CbcHmacKey {
376    use zeroize::Zeroizing;
377
378    use crate::{derive_shareable_key, generate_random_bytes};
379
380    let secret: Zeroizing<[u8; 16]> = generate_random_bytes();
381    derive_shareable_key(secret, name, None)
382}
383
384#[cfg(test)]
385mod tests {
386    use base64::{engine::general_purpose::STANDARD, Engine};
387    use generic_array::GenericArray;
388    use typenum::U32;
389
390    use super::{derive_symmetric_key, SymmetricCryptoKey};
391    use crate::{
392        keys::symmetric_crypto_key::{pad_key, unpad_key},
393        Aes256CbcHmacKey, Aes256CbcKey, XChaCha20Poly1305Key,
394    };
395
396    #[test]
397    fn test_symmetric_crypto_key() {
398        let key = SymmetricCryptoKey::Aes256CbcHmacKey(derive_symmetric_key("test"));
399        let key2 = SymmetricCryptoKey::try_from(key.to_base64()).unwrap();
400
401        assert_eq!(key, key2);
402
403        let key = "UY4B5N4DA4UisCNClgZtRr6VLy9ZF5BXXC7cDZRqourKi4ghEMgISbCsubvgCkHf5DZctQjVot11/vVvN9NNHQ==".to_string();
404        let key2 = SymmetricCryptoKey::try_from(key.clone()).unwrap();
405        assert_eq!(key, key2.to_base64());
406    }
407
408    #[test]
409    fn test_encode_decode_old_symmetric_crypto_key() {
410        let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
411        let encoded = key.to_encoded();
412        let decoded = SymmetricCryptoKey::try_from(encoded).unwrap();
413        assert_eq!(key, decoded);
414    }
415
416    #[test]
417    fn test_decode_new_symmetric_crypto_key() {
418        let key_b64 = STANDARD.decode("pQEEAlDib+JxbqMBlcd3KTUesbufAzoAARFvBIQDBAUGIFggt79surJXmqhPhYuuqi9ZyPfieebmtw2OsmN5SDrb4yUB").unwrap();
419        let key = SymmetricCryptoKey::try_from(key_b64).unwrap();
420        match key {
421            SymmetricCryptoKey::XChaCha20Poly1305Key(_) => (),
422            _ => panic!("Invalid key type"),
423        }
424    }
425
426    #[test]
427    fn test_encode_xchacha20_poly1305_key() {
428        let key = SymmetricCryptoKey::make_xchacha20_poly1305_key();
429        let encoded = key.to_encoded();
430        let decoded = SymmetricCryptoKey::try_from(encoded).unwrap();
431        assert_eq!(key, decoded);
432    }
433
434    #[test]
435    fn test_pad_unpad_key_63() {
436        let original_key = vec![1u8; 63];
437        let mut key_bytes = original_key.clone();
438        let mut encoded_bytes = vec![1u8; 65];
439        encoded_bytes[63] = 2;
440        encoded_bytes[64] = 2;
441        pad_key(&mut key_bytes, 65);
442        assert_eq!(encoded_bytes, key_bytes);
443        let unpadded_key = unpad_key(&key_bytes).unwrap();
444        assert_eq!(original_key, unpadded_key);
445    }
446
447    #[test]
448    fn test_pad_unpad_key_64() {
449        let original_key = vec![1u8; 64];
450        let mut key_bytes = original_key.clone();
451        let mut encoded_bytes = vec![1u8; 65];
452        encoded_bytes[64] = 1;
453        pad_key(&mut key_bytes, 65);
454        assert_eq!(encoded_bytes, key_bytes);
455        let unpadded_key = unpad_key(&key_bytes).unwrap();
456        assert_eq!(original_key, unpadded_key);
457    }
458
459    #[test]
460    fn test_pad_unpad_key_65() {
461        let original_key = vec![1u8; 65];
462        let mut key_bytes = original_key.clone();
463        let mut encoded_bytes = vec![1u8; 66];
464        encoded_bytes[65] = 1;
465        pad_key(&mut key_bytes, 65);
466        assert_eq!(encoded_bytes, key_bytes);
467        let unpadded_key = unpad_key(&key_bytes).unwrap();
468        assert_eq!(original_key, unpadded_key);
469    }
470
471    #[test]
472    fn test_eq_aes_cbc_hmac() {
473        let key1 = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
474        let key2 = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
475        assert_ne!(key1, key2);
476        let key3 = SymmetricCryptoKey::try_from(key1.to_base64()).unwrap();
477        assert_eq!(key1, key3);
478    }
479
480    #[test]
481    fn test_eq_aes_cbc() {
482        let key1 = SymmetricCryptoKey::try_from(vec![1u8; 32]).unwrap();
483        let key2 = SymmetricCryptoKey::try_from(vec![2u8; 32]).unwrap();
484        assert_ne!(key1, key2);
485        let key3 = SymmetricCryptoKey::try_from(key1.to_base64()).unwrap();
486        assert_eq!(key1, key3);
487    }
488
489    #[test]
490    fn test_eq_xchacha20_poly1305() {
491        let key1 = SymmetricCryptoKey::make_xchacha20_poly1305_key();
492        let key2 = SymmetricCryptoKey::make_xchacha20_poly1305_key();
493        assert_ne!(key1, key2);
494        let key3 = SymmetricCryptoKey::try_from(key1.to_base64()).unwrap();
495        assert_eq!(key1, key3);
496    }
497
498    #[test]
499    fn test_neq_different_key_types() {
500        let key1 = SymmetricCryptoKey::Aes256CbcKey(Aes256CbcKey {
501            enc_key: Box::pin(GenericArray::<u8, U32>::default()),
502        });
503        let key2 = SymmetricCryptoKey::XChaCha20Poly1305Key(XChaCha20Poly1305Key {
504            enc_key: Box::pin(GenericArray::<u8, U32>::default()),
505            key_id: [0; 16],
506        });
507        assert_ne!(key1, key2);
508    }
509
510    #[test]
511    fn test_eq_variant_aes256_cbc() {
512        let key1 = Aes256CbcKey {
513            enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
514                vec![1u8; 32].as_slice(),
515            )),
516        };
517        let key2 = Aes256CbcKey {
518            enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
519                vec![1u8; 32].as_slice(),
520            )),
521        };
522        let key3 = Aes256CbcKey {
523            enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
524                vec![2u8; 32].as_slice(),
525            )),
526        };
527        assert_eq!(key1, key2);
528        assert_ne!(key1, key3);
529    }
530
531    #[test]
532    fn test_eq_variant_aes256_cbc_hmac() {
533        let key1 = Aes256CbcHmacKey {
534            enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
535                vec![1u8; 32].as_slice(),
536            )),
537            mac_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
538                vec![2u8; 32].as_slice(),
539            )),
540        };
541        let key2 = Aes256CbcHmacKey {
542            enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
543                vec![1u8; 32].as_slice(),
544            )),
545            mac_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
546                vec![2u8; 32].as_slice(),
547            )),
548        };
549        let key3 = Aes256CbcHmacKey {
550            enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
551                vec![3u8; 32].as_slice(),
552            )),
553            mac_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
554                vec![4u8; 32].as_slice(),
555            )),
556        };
557        assert_eq!(key1, key2);
558        assert_ne!(key1, key3);
559    }
560
561    #[test]
562    fn test_eq_variant_xchacha20_poly1305() {
563        let key1 = XChaCha20Poly1305Key {
564            enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
565                vec![1u8; 32].as_slice(),
566            )),
567            key_id: [0; 16],
568        };
569        let key2 = XChaCha20Poly1305Key {
570            enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
571                vec![1u8; 32].as_slice(),
572            )),
573            key_id: [0; 16],
574        };
575        let key3 = XChaCha20Poly1305Key {
576            enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
577                vec![2u8; 32].as_slice(),
578            )),
579            key_id: [1; 16],
580        };
581        assert_eq!(key1, key2);
582        assert_ne!(key1, key3);
583    }
584
585    #[test]
586    fn test_neq_different_key_id() {
587        let key1 = XChaCha20Poly1305Key {
588            enc_key: Box::pin(GenericArray::<u8, U32>::default()),
589            key_id: [0; 16],
590        };
591        let key2 = XChaCha20Poly1305Key {
592            enc_key: Box::pin(GenericArray::<u8, U32>::default()),
593            key_id: [1; 16],
594        };
595        assert_ne!(key1, key2);
596
597        let key1 = SymmetricCryptoKey::XChaCha20Poly1305Key(key1);
598        let key2 = SymmetricCryptoKey::XChaCha20Poly1305Key(key2);
599        assert_ne!(key1, key2);
600    }
601}