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