1use std::pin::Pin;
2
3use bitwarden_encoding::B64;
4use coset::{CborSerializable, RegisteredLabelWithPrivate, iana::KeyOperation};
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::{KEY_ID_SIZE, KeyId},
20};
21use crate::{BitwardenLegacyKeyBytes, ContentFormat, CoseKeyBytes, CryptoError, cose};
22
23#[derive(ZeroizeOnDrop, Clone)]
27pub struct Aes256CbcKey {
28 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#[derive(ZeroizeOnDrop, Clone)]
47pub struct Aes256CbcHmacKey {
48 pub(crate) enc_key: Pin<Box<GenericArray<u8, U32>>>,
50 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#[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 #[zeroize(skip)]
79 pub(crate) supported_operations: Vec<KeyOperation>,
80}
81
82impl XChaCha20Poly1305Key {
83 pub fn make() -> Self {
85 let mut rng = rand::thread_rng();
86 let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
87 rng.fill(enc_key.as_mut_slice());
88 let mut key_id = [0u8; KEY_ID_SIZE];
89 rng.fill(&mut key_id);
90
91 Self {
92 enc_key,
93 key_id,
94 supported_operations: vec![
95 KeyOperation::Decrypt,
96 KeyOperation::Encrypt,
97 KeyOperation::WrapKey,
98 KeyOperation::UnwrapKey,
99 ],
100 }
101 }
102
103 pub(crate) fn disable_key_operation(&mut self, op: KeyOperation) -> &mut Self {
104 self.supported_operations.retain(|k| *k != op);
105 self
106 }
107}
108
109impl ConstantTimeEq for XChaCha20Poly1305Key {
110 fn ct_eq(&self, other: &Self) -> Choice {
111 self.enc_key.ct_eq(&other.enc_key) & self.key_id.ct_eq(&other.key_id)
112 }
113}
114
115impl PartialEq for XChaCha20Poly1305Key {
116 fn eq(&self, other: &Self) -> bool {
117 self.ct_eq(other).into()
118 }
119}
120
121#[derive(ZeroizeOnDrop, Clone)]
123pub enum SymmetricCryptoKey {
124 #[allow(missing_docs)]
125 Aes256CbcKey(Aes256CbcKey),
126 #[allow(missing_docs)]
127 Aes256CbcHmacKey(Aes256CbcHmacKey),
128 XChaCha20Poly1305Key(XChaCha20Poly1305Key),
131}
132
133impl SymmetricCryptoKey {
134 const AES256_CBC_KEY_LEN: usize = 32;
136 const AES256_CBC_HMAC_KEY_LEN: usize = 64;
138
139 pub(crate) fn make_aes256_cbc_hmac_key_internal(
145 mut rng: impl rand::RngCore + rand::CryptoRng,
146 ) -> Self {
147 let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
148 let mut mac_key = Box::pin(GenericArray::<u8, U32>::default());
149
150 rng.fill(enc_key.as_mut_slice());
151 rng.fill(mac_key.as_mut_slice());
152
153 Self::Aes256CbcHmacKey(Aes256CbcHmacKey { enc_key, mac_key })
154 }
155
156 pub fn make_aes256_cbc_hmac_key() -> Self {
158 let rng = rand::thread_rng();
159 Self::make_aes256_cbc_hmac_key_internal(rng)
160 }
161
162 pub fn make_xchacha20_poly1305_key() -> Self {
164 let mut rng = rand::thread_rng();
165 let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
166 rng.fill(enc_key.as_mut_slice());
167 Self::XChaCha20Poly1305Key(XChaCha20Poly1305Key {
168 enc_key,
169 key_id: KeyId::make().into(),
170 supported_operations: vec![
171 KeyOperation::Decrypt,
172 KeyOperation::Encrypt,
173 KeyOperation::WrapKey,
174 KeyOperation::UnwrapKey,
175 ],
176 })
177 }
178
179 pub fn to_encoded(&self) -> BitwardenLegacyKeyBytes {
188 let encoded_key = self.to_encoded_raw();
189 match encoded_key {
190 EncodedSymmetricKey::BitwardenLegacyKey(_) => {
191 let encoded_key: Vec<u8> = encoded_key.into();
192 BitwardenLegacyKeyBytes::from(encoded_key)
193 }
194 EncodedSymmetricKey::CoseKey(_) => {
195 let mut encoded_key: Vec<u8> = encoded_key.into();
196 pad_key(&mut encoded_key, (Self::AES256_CBC_HMAC_KEY_LEN + 1) as u8); BitwardenLegacyKeyBytes::from(encoded_key)
198 }
199 }
200 }
201
202 #[cfg(test)]
205 pub fn generate_seeded_for_unit_tests(seed: &str) -> Self {
206 let mut seeded_rng = ChaChaRng::from_seed(sha2::Sha256::digest(seed.as_bytes()).into());
208 let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
209 let mut mac_key = Box::pin(GenericArray::<u8, U32>::default());
210
211 seeded_rng.fill(enc_key.as_mut_slice());
212 seeded_rng.fill(mac_key.as_mut_slice());
213
214 SymmetricCryptoKey::Aes256CbcHmacKey(Aes256CbcHmacKey { enc_key, mac_key })
215 }
216
217 pub(crate) fn to_encoded_raw(&self) -> EncodedSymmetricKey {
229 match self {
230 Self::Aes256CbcKey(key) => {
231 EncodedSymmetricKey::BitwardenLegacyKey(key.enc_key.to_vec().into())
232 }
233 Self::Aes256CbcHmacKey(key) => {
234 let mut buf = Vec::with_capacity(64);
235 buf.extend_from_slice(&key.enc_key);
236 buf.extend_from_slice(&key.mac_key);
237 EncodedSymmetricKey::BitwardenLegacyKey(buf.into())
238 }
239 Self::XChaCha20Poly1305Key(key) => {
240 let builder = coset::CoseKeyBuilder::new_symmetric_key(key.enc_key.to_vec());
241 let mut cose_key = builder.key_id(key.key_id.to_vec());
242 for op in &key.supported_operations {
243 cose_key = cose_key.add_key_op(*op);
244 }
245 let mut cose_key = cose_key.build();
246 cose_key.alg = Some(RegisteredLabelWithPrivate::PrivateUse(
247 cose::XCHACHA20_POLY1305,
248 ));
249 EncodedSymmetricKey::CoseKey(
250 cose_key
251 .to_vec()
252 .expect("cose key serialization should not fail")
253 .into(),
254 )
255 }
256 }
257 }
258
259 pub(crate) fn try_from_cose(serialized_key: &[u8]) -> Result<Self, CryptoError> {
260 let cose_key =
261 coset::CoseKey::from_slice(serialized_key).map_err(|_| CryptoError::InvalidKey)?;
262 let key = SymmetricCryptoKey::try_from(&cose_key)?;
263 Ok(key)
264 }
265
266 #[allow(missing_docs)]
267 pub fn to_base64(&self) -> B64 {
268 B64::from(self.to_encoded().as_ref())
269 }
270}
271
272impl ConstantTimeEq for SymmetricCryptoKey {
273 fn ct_eq(&self, other: &SymmetricCryptoKey) -> Choice {
277 use SymmetricCryptoKey::*;
278 match (self, other) {
279 (Aes256CbcKey(a), Aes256CbcKey(b)) => a.ct_eq(b),
280 (Aes256CbcKey(_), _) => Choice::from(0),
281
282 (Aes256CbcHmacKey(a), Aes256CbcHmacKey(b)) => a.ct_eq(b),
283 (Aes256CbcHmacKey(_), _) => Choice::from(0),
284
285 (XChaCha20Poly1305Key(a), XChaCha20Poly1305Key(b)) => a.ct_eq(b),
286 (XChaCha20Poly1305Key(_), _) => Choice::from(0),
287 }
288 }
289}
290
291impl PartialEq for SymmetricCryptoKey {
292 fn eq(&self, other: &Self) -> bool {
293 self.ct_eq(other).into()
294 }
295}
296
297impl TryFrom<String> for SymmetricCryptoKey {
298 type Error = CryptoError;
299
300 fn try_from(value: String) -> Result<Self, Self::Error> {
301 let bytes = B64::try_from(value).map_err(|_| CryptoError::InvalidKey)?;
302 Self::try_from(bytes)
303 }
304}
305
306impl TryFrom<B64> for SymmetricCryptoKey {
307 type Error = CryptoError;
308
309 fn try_from(value: B64) -> Result<Self, Self::Error> {
310 Self::try_from(&BitwardenLegacyKeyBytes::from(&value))
311 }
312}
313
314impl TryFrom<&BitwardenLegacyKeyBytes> for SymmetricCryptoKey {
315 type Error = CryptoError;
316
317 fn try_from(value: &BitwardenLegacyKeyBytes) -> Result<Self, Self::Error> {
318 let slice = value.as_ref();
319
320 if slice.len() == Self::AES256_CBC_HMAC_KEY_LEN || slice.len() == Self::AES256_CBC_KEY_LEN {
326 Self::try_from(EncodedSymmetricKey::BitwardenLegacyKey(value.clone()))
327 } else if slice.len() > Self::AES256_CBC_HMAC_KEY_LEN {
328 let unpadded_value = unpad_key(slice)?;
329 Ok(Self::try_from_cose(unpadded_value)?)
330 } else {
331 Err(CryptoError::InvalidKeyLen)
332 }
333 }
334}
335
336impl TryFrom<EncodedSymmetricKey> for SymmetricCryptoKey {
337 type Error = CryptoError;
338
339 fn try_from(value: EncodedSymmetricKey) -> Result<Self, Self::Error> {
340 match value {
341 EncodedSymmetricKey::BitwardenLegacyKey(key)
342 if key.as_ref().len() == Self::AES256_CBC_KEY_LEN =>
343 {
344 let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
345 enc_key.copy_from_slice(&key.as_ref()[..Self::AES256_CBC_KEY_LEN]);
346 Ok(Self::Aes256CbcKey(Aes256CbcKey { enc_key }))
347 }
348 EncodedSymmetricKey::BitwardenLegacyKey(key)
349 if key.as_ref().len() == Self::AES256_CBC_HMAC_KEY_LEN =>
350 {
351 let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
352 enc_key.copy_from_slice(&key.as_ref()[..32]);
353
354 let mut mac_key = Box::pin(GenericArray::<u8, U32>::default());
355 mac_key.copy_from_slice(&key.as_ref()[32..]);
356
357 Ok(Self::Aes256CbcHmacKey(Aes256CbcHmacKey {
358 enc_key,
359 mac_key,
360 }))
361 }
362 EncodedSymmetricKey::CoseKey(key) => Self::try_from_cose(key.as_ref()),
363 _ => Err(CryptoError::InvalidKey),
364 }
365 }
366}
367
368impl CryptoKey for SymmetricCryptoKey {}
369
370impl std::fmt::Debug for SymmetricCryptoKey {
372 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
373 f.debug_struct("SymmetricCryptoKey")
374 .field(
375 "inner_type",
376 match self {
377 SymmetricCryptoKey::Aes256CbcKey(key) => key,
378 SymmetricCryptoKey::Aes256CbcHmacKey(key) => key,
379 SymmetricCryptoKey::XChaCha20Poly1305Key(key) => key,
380 },
381 )
382 .finish()
383 }
384}
385
386impl std::fmt::Debug for Aes256CbcKey {
387 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
388 f.debug_struct("Aes256CbcKey").finish()
389 }
390}
391
392impl std::fmt::Debug for Aes256CbcHmacKey {
393 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
394 f.debug_struct("Aes256CbcHmacKey").finish()
395 }
396}
397
398impl std::fmt::Debug for XChaCha20Poly1305Key {
399 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
400 f.debug_struct("XChaCha20Poly1305Key")
401 .field("key_id", &self.key_id)
402 .finish()
403 }
404}
405
406fn pad_key(key_bytes: &mut Vec<u8>, min_length: u8) {
417 crate::keys::utils::pad_bytes(key_bytes, min_length as usize)
418 .expect("Padding cannot fail since the min_length is < 255")
419}
420
421fn unpad_key(key_bytes: &[u8]) -> Result<&[u8], CryptoError> {
432 crate::keys::utils::unpad_bytes(key_bytes).map_err(|_| CryptoError::InvalidKey)
433}
434
435pub enum EncodedSymmetricKey {
437 BitwardenLegacyKey(BitwardenLegacyKeyBytes),
439 CoseKey(CoseKeyBytes),
441}
442impl From<EncodedSymmetricKey> for Vec<u8> {
443 fn from(val: EncodedSymmetricKey) -> Self {
444 match val {
445 EncodedSymmetricKey::BitwardenLegacyKey(key) => key.to_vec(),
446 EncodedSymmetricKey::CoseKey(key) => key.to_vec(),
447 }
448 }
449}
450impl EncodedSymmetricKey {
451 #[allow(private_interfaces)]
453 pub fn content_format(&self) -> ContentFormat {
454 match self {
455 EncodedSymmetricKey::BitwardenLegacyKey(_) => ContentFormat::BitwardenLegacyKey,
456 EncodedSymmetricKey::CoseKey(_) => ContentFormat::CoseKey,
457 }
458 }
459}
460
461#[cfg(test)]
463pub fn derive_symmetric_key(name: &str) -> Aes256CbcHmacKey {
464 use zeroize::Zeroizing;
465
466 use crate::{derive_shareable_key, generate_random_bytes};
467
468 let secret: Zeroizing<[u8; 16]> = generate_random_bytes();
469 derive_shareable_key(secret, name, None)
470}
471
472#[cfg(test)]
473mod tests {
474 use bitwarden_encoding::B64;
475 use coset::iana::KeyOperation;
476 use generic_array::GenericArray;
477 use typenum::U32;
478
479 use super::{SymmetricCryptoKey, derive_symmetric_key};
480 use crate::{
481 Aes256CbcHmacKey, Aes256CbcKey, BitwardenLegacyKeyBytes, XChaCha20Poly1305Key,
482 keys::symmetric_crypto_key::{pad_key, unpad_key},
483 };
484
485 #[test]
486 fn test_symmetric_crypto_key() {
487 let key = SymmetricCryptoKey::Aes256CbcHmacKey(derive_symmetric_key("test"));
488 let key2 = SymmetricCryptoKey::try_from(key.to_base64()).unwrap();
489
490 assert_eq!(key, key2);
491
492 let key = "UY4B5N4DA4UisCNClgZtRr6VLy9ZF5BXXC7cDZRqourKi4ghEMgISbCsubvgCkHf5DZctQjVot11/vVvN9NNHQ==".to_string();
493 let key2 = SymmetricCryptoKey::try_from(key.clone()).unwrap();
494 assert_eq!(key, key2.to_base64().to_string());
495 }
496
497 #[test]
498 fn test_encode_decode_old_symmetric_crypto_key() {
499 let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
500 let encoded = key.to_encoded();
501 let decoded = SymmetricCryptoKey::try_from(&encoded).unwrap();
502 assert_eq!(key, decoded);
503 }
504
505 #[test]
506 fn test_decode_new_symmetric_crypto_key() {
507 let key: B64 = ("pQEEAlDib+JxbqMBlcd3KTUesbufAzoAARFvBIQDBAUGIFggt79surJXmqhPhYuuqi9ZyPfieebmtw2OsmN5SDrb4yUB").parse()
508 .unwrap();
509 let key = BitwardenLegacyKeyBytes::from(&key);
510 let key = SymmetricCryptoKey::try_from(&key).unwrap();
511 match key {
512 SymmetricCryptoKey::XChaCha20Poly1305Key(_) => (),
513 _ => panic!("Invalid key type"),
514 }
515 }
516
517 #[test]
518 fn test_encode_xchacha20_poly1305_key() {
519 let key = SymmetricCryptoKey::make_xchacha20_poly1305_key();
520 let encoded = key.to_encoded();
521 let decoded = SymmetricCryptoKey::try_from(&encoded).unwrap();
522 assert_eq!(key, decoded);
523 }
524
525 #[test]
526 fn test_pad_unpad_key_63() {
527 let original_key = vec![1u8; 63];
528 let mut key_bytes = original_key.clone();
529 let mut encoded_bytes = vec![1u8; 65];
530 encoded_bytes[63] = 2;
531 encoded_bytes[64] = 2;
532 pad_key(&mut key_bytes, 65);
533 assert_eq!(encoded_bytes, key_bytes);
534 let unpadded_key = unpad_key(&key_bytes).unwrap();
535 assert_eq!(original_key, unpadded_key);
536 }
537
538 #[test]
539 fn test_pad_unpad_key_64() {
540 let original_key = vec![1u8; 64];
541 let mut key_bytes = original_key.clone();
542 let mut encoded_bytes = vec![1u8; 65];
543 encoded_bytes[64] = 1;
544 pad_key(&mut key_bytes, 65);
545 assert_eq!(encoded_bytes, key_bytes);
546 let unpadded_key = unpad_key(&key_bytes).unwrap();
547 assert_eq!(original_key, unpadded_key);
548 }
549
550 #[test]
551 fn test_pad_unpad_key_65() {
552 let original_key = vec![1u8; 65];
553 let mut key_bytes = original_key.clone();
554 let mut encoded_bytes = vec![1u8; 66];
555 encoded_bytes[65] = 1;
556 pad_key(&mut key_bytes, 65);
557 assert_eq!(encoded_bytes, key_bytes);
558 let unpadded_key = unpad_key(&key_bytes).unwrap();
559 assert_eq!(original_key, unpadded_key);
560 }
561
562 #[test]
563 fn test_eq_aes_cbc_hmac() {
564 let key1 = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
565 let key2 = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
566 assert_ne!(key1, key2);
567 let key3 = SymmetricCryptoKey::try_from(key1.to_base64()).unwrap();
568 assert_eq!(key1, key3);
569 }
570
571 #[test]
572 fn test_eq_aes_cbc() {
573 let key1 =
574 SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(vec![1u8; 32])).unwrap();
575 let key2 =
576 SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(vec![2u8; 32])).unwrap();
577 assert_ne!(key1, key2);
578 let key3 = SymmetricCryptoKey::try_from(key1.to_base64()).unwrap();
579 assert_eq!(key1, key3);
580 }
581
582 #[test]
583 fn test_eq_xchacha20_poly1305() {
584 let key1 = SymmetricCryptoKey::make_xchacha20_poly1305_key();
585 let key2 = SymmetricCryptoKey::make_xchacha20_poly1305_key();
586 assert_ne!(key1, key2);
587 let key3 = SymmetricCryptoKey::try_from(key1.to_base64()).unwrap();
588 assert_eq!(key1, key3);
589 }
590
591 #[test]
592 fn test_neq_different_key_types() {
593 let key1 = SymmetricCryptoKey::Aes256CbcKey(Aes256CbcKey {
594 enc_key: Box::pin(GenericArray::<u8, U32>::default()),
595 });
596 let key2 = SymmetricCryptoKey::XChaCha20Poly1305Key(XChaCha20Poly1305Key {
597 enc_key: Box::pin(GenericArray::<u8, U32>::default()),
598 key_id: [0; 16],
599 supported_operations: vec![
600 KeyOperation::Decrypt,
601 KeyOperation::Encrypt,
602 KeyOperation::WrapKey,
603 KeyOperation::UnwrapKey,
604 ],
605 });
606 assert_ne!(key1, key2);
607 }
608
609 #[test]
610 fn test_eq_variant_aes256_cbc() {
611 let key1 = Aes256CbcKey {
612 enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
613 vec![1u8; 32].as_slice(),
614 )),
615 };
616 let key2 = Aes256CbcKey {
617 enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
618 vec![1u8; 32].as_slice(),
619 )),
620 };
621 let key3 = Aes256CbcKey {
622 enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
623 vec![2u8; 32].as_slice(),
624 )),
625 };
626 assert_eq!(key1, key2);
627 assert_ne!(key1, key3);
628 }
629
630 #[test]
631 fn test_eq_variant_aes256_cbc_hmac() {
632 let key1 = Aes256CbcHmacKey {
633 enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
634 vec![1u8; 32].as_slice(),
635 )),
636 mac_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
637 vec![2u8; 32].as_slice(),
638 )),
639 };
640 let key2 = Aes256CbcHmacKey {
641 enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
642 vec![1u8; 32].as_slice(),
643 )),
644 mac_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
645 vec![2u8; 32].as_slice(),
646 )),
647 };
648 let key3 = Aes256CbcHmacKey {
649 enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
650 vec![3u8; 32].as_slice(),
651 )),
652 mac_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
653 vec![4u8; 32].as_slice(),
654 )),
655 };
656 assert_eq!(key1, key2);
657 assert_ne!(key1, key3);
658 }
659
660 #[test]
661 fn test_eq_variant_xchacha20_poly1305() {
662 let key1 = XChaCha20Poly1305Key {
663 enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
664 vec![1u8; 32].as_slice(),
665 )),
666 key_id: [0; 16],
667 supported_operations: vec![
668 KeyOperation::Decrypt,
669 KeyOperation::Encrypt,
670 KeyOperation::WrapKey,
671 KeyOperation::UnwrapKey,
672 ],
673 };
674 let key2 = XChaCha20Poly1305Key {
675 enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
676 vec![1u8; 32].as_slice(),
677 )),
678 key_id: [0; 16],
679 supported_operations: vec![
680 KeyOperation::Decrypt,
681 KeyOperation::Encrypt,
682 KeyOperation::WrapKey,
683 KeyOperation::UnwrapKey,
684 ],
685 };
686 let key3 = XChaCha20Poly1305Key {
687 enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
688 vec![2u8; 32].as_slice(),
689 )),
690 key_id: [1; 16],
691 supported_operations: vec![
692 KeyOperation::Decrypt,
693 KeyOperation::Encrypt,
694 KeyOperation::WrapKey,
695 KeyOperation::UnwrapKey,
696 ],
697 };
698 assert_eq!(key1, key2);
699 assert_ne!(key1, key3);
700 }
701
702 #[test]
703 fn test_neq_different_key_id() {
704 let key1 = XChaCha20Poly1305Key {
705 enc_key: Box::pin(GenericArray::<u8, U32>::default()),
706 key_id: [0; 16],
707 supported_operations: vec![
708 KeyOperation::Decrypt,
709 KeyOperation::Encrypt,
710 KeyOperation::WrapKey,
711 KeyOperation::UnwrapKey,
712 ],
713 };
714 let key2 = XChaCha20Poly1305Key {
715 enc_key: Box::pin(GenericArray::<u8, U32>::default()),
716 key_id: [1; 16],
717 supported_operations: vec![
718 KeyOperation::Decrypt,
719 KeyOperation::Encrypt,
720 KeyOperation::WrapKey,
721 KeyOperation::UnwrapKey,
722 ],
723 };
724 assert_ne!(key1, key2);
725
726 let key1 = SymmetricCryptoKey::XChaCha20Poly1305Key(key1);
727 let key2 = SymmetricCryptoKey::XChaCha20Poly1305Key(key2);
728 assert_ne!(key1, key2);
729 }
730}