1use std::{pin::Pin, str::FromStr};
2
3use bitwarden_encoding::{B64, FromStrVisitor};
4use coset::{CborSerializable, RegisteredLabelWithPrivate, iana::KeyOperation};
5use hybrid_array::Array;
6use rand::RngExt;
7#[cfg(test)]
8use rand::SeedableRng;
9#[cfg(test)]
10use rand_chacha::ChaChaRng;
11use serde::{Deserialize, Serialize};
12#[cfg(test)]
13use sha2::Digest;
14use subtle::{Choice, ConstantTimeEq};
15use typenum::U32;
16#[cfg(feature = "wasm")]
17use wasm_bindgen::convert::{FromWasmAbi, IntoWasmAbi, OptionFromWasmAbi};
18use zeroize::{Zeroize, ZeroizeOnDrop};
19
20use super::{key_encryptable::CryptoKey, key_id::KeyId};
21use crate::{BitwardenLegacyKeyBytes, ContentFormat, CoseKeyBytes, CryptoError, cose};
22
23#[cfg(feature = "wasm")]
24#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
25const TS_CUSTOM_TYPES: &'static str = r#"
26export type SymmetricKey = Tagged<string, "SymmetricKey">;
27"#;
28
29#[cfg(feature = "wasm")]
30impl wasm_bindgen::describe::WasmDescribe for SymmetricCryptoKey {
31 fn describe() {
32 <String as wasm_bindgen::describe::WasmDescribe>::describe();
33 }
34}
35
36#[cfg(feature = "wasm")]
37impl FromWasmAbi for SymmetricCryptoKey {
38 type Abi = <String as FromWasmAbi>::Abi;
39
40 unsafe fn from_abi(abi: Self::Abi) -> Self {
41 use wasm_bindgen::UnwrapThrowExt;
42 let string = unsafe { String::from_abi(abi) };
43 let b64 = B64::try_from(string).unwrap_throw();
44 SymmetricCryptoKey::try_from(b64).unwrap_throw()
45 }
46}
47
48#[cfg(feature = "wasm")]
49impl OptionFromWasmAbi for SymmetricCryptoKey {
50 fn is_none(abi: &Self::Abi) -> bool {
51 <String as OptionFromWasmAbi>::is_none(abi)
52 }
53}
54
55#[cfg(feature = "wasm")]
56impl IntoWasmAbi for SymmetricCryptoKey {
57 type Abi = <String as IntoWasmAbi>::Abi;
58
59 fn into_abi(self) -> Self::Abi {
60 let string: String = self.to_base64().to_string();
61 string.into_abi()
62 }
63}
64
65#[cfg(feature = "wasm")]
66impl TryFrom<wasm_bindgen::JsValue> for SymmetricCryptoKey {
67 type Error = CryptoError;
68
69 fn try_from(value: wasm_bindgen::JsValue) -> Result<Self, Self::Error> {
70 let string = value.as_string().ok_or(CryptoError::InvalidKey)?;
71 Self::try_from(string)
72 }
73}
74
75#[derive(Debug, PartialEq)]
77pub enum SymmetricKeyAlgorithm {
78 Aes256CbcHmac,
80 XChaCha20Poly1305,
82}
83
84#[derive(ZeroizeOnDrop, Clone)]
88pub struct Aes256CbcKey {
89 pub(crate) enc_key: Pin<Box<Array<u8, U32>>>,
91}
92
93impl ConstantTimeEq for Aes256CbcKey {
94 fn ct_eq(&self, other: &Self) -> Choice {
95 self.enc_key.ct_eq(&other.enc_key)
96 }
97}
98
99impl PartialEq for Aes256CbcKey {
100 fn eq(&self, other: &Self) -> bool {
101 self.ct_eq(other).into()
102 }
103}
104
105#[derive(ZeroizeOnDrop, Clone)]
108pub struct Aes256CbcHmacKey {
109 pub(crate) enc_key: Pin<Box<Array<u8, U32>>>,
111 pub(crate) mac_key: Pin<Box<Array<u8, U32>>>,
113}
114
115impl ConstantTimeEq for Aes256CbcHmacKey {
116 fn ct_eq(&self, other: &Self) -> Choice {
117 self.enc_key.ct_eq(&other.enc_key) & self.mac_key.ct_eq(&other.mac_key)
118 }
119}
120
121impl PartialEq for Aes256CbcHmacKey {
122 fn eq(&self, other: &Self) -> bool {
123 self.ct_eq(other).into()
124 }
125}
126
127#[derive(Zeroize, Clone)]
132pub struct XChaCha20Poly1305Key {
133 pub(crate) key_id: KeyId,
134 pub(crate) enc_key: Pin<Box<Array<u8, U32>>>,
135 #[zeroize(skip)]
140 pub(crate) supported_operations: Vec<KeyOperation>,
141}
142
143impl XChaCha20Poly1305Key {
144 pub fn make() -> Self {
146 let mut rng = rand::rng();
147 let mut enc_key = Box::pin(Array::<u8, U32>::default());
148 rng.fill(enc_key.as_mut_slice());
149 let key_id = KeyId::make();
150
151 Self {
152 enc_key,
153 key_id,
154 supported_operations: vec![
155 KeyOperation::Decrypt,
156 KeyOperation::Encrypt,
157 KeyOperation::WrapKey,
158 KeyOperation::UnwrapKey,
159 ],
160 }
161 }
162
163 pub(crate) fn disable_key_operation(&mut self, op: KeyOperation) -> &mut Self {
164 self.supported_operations.retain(|k| *k != op);
165 self
166 }
167}
168
169impl ConstantTimeEq for XChaCha20Poly1305Key {
170 fn ct_eq(&self, other: &Self) -> Choice {
171 self.enc_key.ct_eq(&other.enc_key) & self.key_id.ct_eq(&other.key_id)
172 }
173}
174
175impl PartialEq for XChaCha20Poly1305Key {
176 fn eq(&self, other: &Self) -> bool {
177 self.ct_eq(other).into()
178 }
179}
180
181#[derive(ZeroizeOnDrop, Clone)]
183pub enum SymmetricCryptoKey {
184 #[allow(missing_docs)]
185 Aes256CbcKey(Aes256CbcKey),
186 #[allow(missing_docs)]
187 Aes256CbcHmacKey(Aes256CbcHmacKey),
188 XChaCha20Poly1305Key(XChaCha20Poly1305Key),
191}
192
193impl SymmetricCryptoKey {
194 const AES256_CBC_KEY_LEN: usize = 32;
196 const AES256_CBC_HMAC_KEY_LEN: usize = 64;
198
199 pub(crate) fn make_aes256_cbc_hmac_key_internal(mut rng: impl rand::CryptoRng) -> Self {
205 let mut enc_key = Box::pin(Array::<u8, U32>::default());
206 let mut mac_key = Box::pin(Array::<u8, U32>::default());
207
208 rng.fill(enc_key.as_mut_slice());
209 rng.fill(mac_key.as_mut_slice());
210
211 Self::Aes256CbcHmacKey(Aes256CbcHmacKey { enc_key, mac_key })
212 }
213
214 pub fn make(algorithm: SymmetricKeyAlgorithm) -> Self {
216 match algorithm {
217 SymmetricKeyAlgorithm::Aes256CbcHmac => Self::make_aes256_cbc_hmac_key(),
218 SymmetricKeyAlgorithm::XChaCha20Poly1305 => Self::make_xchacha20_poly1305_key(),
219 }
220 }
221
222 pub fn make_aes256_cbc_hmac_key() -> Self {
224 let rng = rand::rng();
225 Self::make_aes256_cbc_hmac_key_internal(rng)
226 }
227
228 pub fn make_xchacha20_poly1305_key() -> Self {
230 let mut rng = rand::rng();
231 let mut enc_key = Box::pin(Array::<u8, U32>::default());
232 rng.fill(enc_key.as_mut_slice());
233 Self::XChaCha20Poly1305Key(XChaCha20Poly1305Key {
234 enc_key,
235 key_id: KeyId::make(),
236 supported_operations: vec![
237 KeyOperation::Decrypt,
238 KeyOperation::Encrypt,
239 KeyOperation::WrapKey,
240 KeyOperation::UnwrapKey,
241 ],
242 })
243 }
244
245 pub fn to_encoded(&self) -> BitwardenLegacyKeyBytes {
254 let encoded_key = self.to_encoded_raw();
255 match encoded_key {
256 EncodedSymmetricKey::BitwardenLegacyKey(_) => {
257 let encoded_key: Vec<u8> = encoded_key.into();
258 BitwardenLegacyKeyBytes::from(encoded_key)
259 }
260 EncodedSymmetricKey::CoseKey(_) => {
261 let mut encoded_key: Vec<u8> = encoded_key.into();
262 pad_key(&mut encoded_key, (Self::AES256_CBC_HMAC_KEY_LEN + 1) as u8); BitwardenLegacyKeyBytes::from(encoded_key)
264 }
265 }
266 }
267
268 #[cfg(test)]
271 pub fn generate_seeded_for_unit_tests(seed: &str) -> Self {
272 let mut seeded_rng = ChaChaRng::from_seed(sha2::Sha256::digest(seed.as_bytes()).into());
274 let mut enc_key = Box::pin(Array::<u8, U32>::default());
275 let mut mac_key = Box::pin(Array::<u8, U32>::default());
276
277 seeded_rng.fill(enc_key.as_mut_slice());
278 seeded_rng.fill(mac_key.as_mut_slice());
279
280 SymmetricCryptoKey::Aes256CbcHmacKey(Aes256CbcHmacKey { enc_key, mac_key })
281 }
282
283 pub(crate) fn to_encoded_raw(&self) -> EncodedSymmetricKey {
295 match self {
296 Self::Aes256CbcKey(key) => {
297 EncodedSymmetricKey::BitwardenLegacyKey(key.enc_key.to_vec().into())
298 }
299 Self::Aes256CbcHmacKey(key) => {
300 let mut buf = Vec::with_capacity(64);
301 buf.extend_from_slice(&key.enc_key);
302 buf.extend_from_slice(&key.mac_key);
303 EncodedSymmetricKey::BitwardenLegacyKey(buf.into())
304 }
305 Self::XChaCha20Poly1305Key(key) => {
306 let builder = coset::CoseKeyBuilder::new_symmetric_key(key.enc_key.to_vec());
307 let mut cose_key = builder.key_id((&key.key_id).into());
308 for op in &key.supported_operations {
309 cose_key = cose_key.add_key_op(*op);
310 }
311 let mut cose_key = cose_key.build();
312 cose_key.alg = Some(RegisteredLabelWithPrivate::PrivateUse(
313 cose::XCHACHA20_POLY1305,
314 ));
315 EncodedSymmetricKey::CoseKey(
316 cose_key
317 .to_vec()
318 .expect("cose key serialization should not fail")
319 .into(),
320 )
321 }
322 }
323 }
324
325 pub(crate) fn try_from_cose(serialized_key: &[u8]) -> Result<Self, CryptoError> {
326 let cose_key =
327 coset::CoseKey::from_slice(serialized_key).map_err(|_| CryptoError::InvalidKey)?;
328 let key = SymmetricCryptoKey::try_from(&cose_key)?;
329 Ok(key)
330 }
331
332 #[allow(missing_docs)]
333 pub fn to_base64(&self) -> B64 {
334 B64::from(self.to_encoded().as_ref())
335 }
336
337 pub fn key_id(&self) -> Option<KeyId> {
340 match self {
341 Self::Aes256CbcKey(_) => None,
342 Self::Aes256CbcHmacKey(_) => None,
343 Self::XChaCha20Poly1305Key(key) => Some(key.key_id.clone()),
344 }
345 }
346}
347
348impl ConstantTimeEq for SymmetricCryptoKey {
349 fn ct_eq(&self, other: &SymmetricCryptoKey) -> Choice {
353 use SymmetricCryptoKey::*;
354 match (self, other) {
355 (Aes256CbcKey(a), Aes256CbcKey(b)) => a.ct_eq(b),
356 (Aes256CbcKey(_), _) => Choice::from(0),
357
358 (Aes256CbcHmacKey(a), Aes256CbcHmacKey(b)) => a.ct_eq(b),
359 (Aes256CbcHmacKey(_), _) => Choice::from(0),
360
361 (XChaCha20Poly1305Key(a), XChaCha20Poly1305Key(b)) => a.ct_eq(b),
362 (XChaCha20Poly1305Key(_), _) => Choice::from(0),
363 }
364 }
365}
366
367impl PartialEq for SymmetricCryptoKey {
368 fn eq(&self, other: &Self) -> bool {
369 self.ct_eq(other).into()
370 }
371}
372
373impl TryFrom<String> for SymmetricCryptoKey {
374 type Error = CryptoError;
375
376 fn try_from(value: String) -> Result<Self, Self::Error> {
377 let bytes = B64::try_from(value).map_err(|_| CryptoError::InvalidKey)?;
378 Self::try_from(bytes)
379 }
380}
381
382impl TryFrom<B64> for SymmetricCryptoKey {
383 type Error = CryptoError;
384
385 fn try_from(value: B64) -> Result<Self, Self::Error> {
386 Self::try_from(&BitwardenLegacyKeyBytes::from(&value))
387 }
388}
389
390impl TryFrom<&BitwardenLegacyKeyBytes> for SymmetricCryptoKey {
391 type Error = CryptoError;
392
393 fn try_from(value: &BitwardenLegacyKeyBytes) -> Result<Self, Self::Error> {
394 let slice = value.as_ref();
395
396 if slice.len() == Self::AES256_CBC_HMAC_KEY_LEN || slice.len() == Self::AES256_CBC_KEY_LEN {
402 Self::try_from(EncodedSymmetricKey::BitwardenLegacyKey(value.clone()))
403 } else if slice.len() > Self::AES256_CBC_HMAC_KEY_LEN {
404 let unpadded_value = unpad_key(slice)?;
405 Ok(Self::try_from_cose(unpadded_value)?)
406 } else {
407 Err(CryptoError::InvalidKeyLen)
408 }
409 }
410}
411
412impl TryFrom<EncodedSymmetricKey> for SymmetricCryptoKey {
413 type Error = CryptoError;
414
415 fn try_from(value: EncodedSymmetricKey) -> Result<Self, Self::Error> {
416 match value {
417 EncodedSymmetricKey::BitwardenLegacyKey(key)
418 if key.as_ref().len() == Self::AES256_CBC_KEY_LEN =>
419 {
420 let mut enc_key = Box::pin(Array::<u8, U32>::default());
421 enc_key.copy_from_slice(&key.as_ref()[..Self::AES256_CBC_KEY_LEN]);
422 Ok(Self::Aes256CbcKey(Aes256CbcKey { enc_key }))
423 }
424 EncodedSymmetricKey::BitwardenLegacyKey(key)
425 if key.as_ref().len() == Self::AES256_CBC_HMAC_KEY_LEN =>
426 {
427 let mut enc_key = Box::pin(Array::<u8, U32>::default());
428 enc_key.copy_from_slice(&key.as_ref()[..32]);
429
430 let mut mac_key = Box::pin(Array::<u8, U32>::default());
431 mac_key.copy_from_slice(&key.as_ref()[32..]);
432
433 Ok(Self::Aes256CbcHmacKey(Aes256CbcHmacKey {
434 enc_key,
435 mac_key,
436 }))
437 }
438 EncodedSymmetricKey::CoseKey(key) => Self::try_from_cose(key.as_ref()),
439 _ => Err(CryptoError::InvalidKey),
440 }
441 }
442}
443
444impl CryptoKey for SymmetricCryptoKey {}
445
446impl std::fmt::Debug for SymmetricCryptoKey {
448 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
449 match self {
450 SymmetricCryptoKey::Aes256CbcKey(key) => key.fmt(f),
451 SymmetricCryptoKey::Aes256CbcHmacKey(key) => key.fmt(f),
452 SymmetricCryptoKey::XChaCha20Poly1305Key(key) => key.fmt(f),
453 }
454 }
455}
456
457impl std::fmt::Debug for Aes256CbcKey {
458 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
459 let mut debug_struct = f.debug_struct("SymmetricKey::Aes256Cbc");
460 #[cfg(feature = "dangerous-crypto-debug")]
461 debug_struct.field("key", &hex::encode(self.enc_key.as_slice()));
462 debug_struct.finish()
463 }
464}
465
466impl std::fmt::Debug for Aes256CbcHmacKey {
467 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
468 let mut debug_struct = f.debug_struct("SymmetricKey::Aes256CbcHmac");
469 #[cfg(feature = "dangerous-crypto-debug")]
470 debug_struct
471 .field("enc_key", &hex::encode(self.enc_key.as_slice()))
472 .field("mac_key", &hex::encode(self.mac_key.as_slice()));
473 debug_struct.finish()
474 }
475}
476
477impl std::fmt::Debug for XChaCha20Poly1305Key {
478 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
479 let mut debug_struct = f.debug_struct("SymmetricKey::XChaCha20Poly1305");
480 debug_struct.field("key_id", &self.key_id);
481 debug_struct.field(
482 "supported_operations",
483 &self
484 .supported_operations
485 .iter()
486 .map(|key_operation: &KeyOperation| cose::debug_key_operation(*key_operation))
487 .collect::<Vec<_>>(),
488 );
489 #[cfg(feature = "dangerous-crypto-debug")]
490 debug_struct.field("key", &hex::encode(self.enc_key.as_slice()));
491 debug_struct.finish()
492 }
493}
494
495fn pad_key(key_bytes: &mut Vec<u8>, min_length: u8) {
506 crate::keys::utils::pad_bytes(key_bytes, min_length as usize)
507 .expect("Padding cannot fail since the min_length is < 255")
508}
509
510fn unpad_key(key_bytes: &[u8]) -> Result<&[u8], CryptoError> {
521 crate::keys::utils::unpad_bytes(key_bytes).map_err(|_| CryptoError::InvalidKey)
522}
523
524pub enum EncodedSymmetricKey {
526 BitwardenLegacyKey(BitwardenLegacyKeyBytes),
528 CoseKey(CoseKeyBytes),
530}
531impl From<EncodedSymmetricKey> for Vec<u8> {
532 fn from(val: EncodedSymmetricKey) -> Self {
533 match val {
534 EncodedSymmetricKey::BitwardenLegacyKey(key) => key.to_vec(),
535 EncodedSymmetricKey::CoseKey(key) => key.to_vec(),
536 }
537 }
538}
539impl EncodedSymmetricKey {
540 #[allow(private_interfaces)]
542 pub fn content_format(&self) -> ContentFormat {
543 match self {
544 EncodedSymmetricKey::BitwardenLegacyKey(_) => ContentFormat::BitwardenLegacyKey,
545 EncodedSymmetricKey::CoseKey(_) => ContentFormat::CoseKey,
546 }
547 }
548}
549
550impl<'de> Deserialize<'de> for SymmetricCryptoKey {
555 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
556 where
557 D: serde::Deserializer<'de>,
558 {
559 deserializer.deserialize_str(FromStrVisitor::new())
560 }
561}
562
563impl FromStr for SymmetricCryptoKey {
564 type Err = CryptoError;
565
566 fn from_str(s: &str) -> Result<Self, Self::Err> {
567 let bytes = B64::try_from(s.to_string()).map_err(|_| CryptoError::InvalidKey)?;
568 Self::try_from(bytes).map_err(|_| CryptoError::InvalidKey)
569 }
570}
571
572impl Serialize for SymmetricCryptoKey {
573 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
574 where
575 S: serde::Serializer,
576 {
577 serializer.serialize_str(&self.to_base64().to_string())
578 }
579}
580
581#[cfg(test)]
583pub fn derive_symmetric_key(name: &str) -> Aes256CbcHmacKey {
584 use zeroize::Zeroizing;
585
586 use crate::{derive_shareable_key, generate_random_bytes};
587
588 let secret: Zeroizing<[u8; 16]> = generate_random_bytes();
589 derive_shareable_key(secret, name, None)
590}
591
592#[cfg(test)]
593mod tests {
594 use bitwarden_encoding::B64;
595 use coset::iana::KeyOperation;
596 use hybrid_array::Array;
597 use typenum::U32;
598
599 use super::{SymmetricCryptoKey, derive_symmetric_key};
600 use crate::{
601 Aes256CbcHmacKey, Aes256CbcKey, BitwardenLegacyKeyBytes, XChaCha20Poly1305Key,
602 keys::{
603 KeyId,
604 symmetric_crypto_key::{pad_key, unpad_key},
605 },
606 };
607
608 #[test]
609 #[ignore = "Manual test to verify debug format"]
610 fn test_key_debug() {
611 let aes_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
612 println!("{:?}", aes_key);
613 let xchacha_key = SymmetricCryptoKey::make_xchacha20_poly1305_key();
614 println!("{:?}", xchacha_key);
615 }
616
617 #[test]
618 fn test_serialize_deserialize_symmetric_crypto_key() {
619 let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
620 let serialized = serde_json::to_string(&key).unwrap();
621 let deserialized: SymmetricCryptoKey = serde_json::from_str(&serialized).unwrap();
622 assert_eq!(key, deserialized);
623 }
624
625 #[test]
626 fn test_symmetric_crypto_key() {
627 let key = SymmetricCryptoKey::Aes256CbcHmacKey(derive_symmetric_key("test"));
628 let key2 = SymmetricCryptoKey::try_from(key.to_base64()).unwrap();
629
630 assert_eq!(key, key2);
631
632 let key = "UY4B5N4DA4UisCNClgZtRr6VLy9ZF5BXXC7cDZRqourKi4ghEMgISbCsubvgCkHf5DZctQjVot11/vVvN9NNHQ==".to_string();
633 let key2 = SymmetricCryptoKey::try_from(key.clone()).unwrap();
634 assert_eq!(key, key2.to_base64().to_string());
635 }
636
637 #[test]
638 fn test_encode_decode_old_symmetric_crypto_key() {
639 let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
640 let encoded = key.to_encoded();
641 let decoded = SymmetricCryptoKey::try_from(&encoded).unwrap();
642 assert_eq!(key, decoded);
643 }
644
645 #[test]
646 fn test_decode_new_symmetric_crypto_key() {
647 let key: B64 = ("pQEEAlDib+JxbqMBlcd3KTUesbufAzoAARFvBIQDBAUGIFggt79surJXmqhPhYuuqi9ZyPfieebmtw2OsmN5SDrb4yUB").parse()
648 .unwrap();
649 let key = BitwardenLegacyKeyBytes::from(&key);
650 let key = SymmetricCryptoKey::try_from(&key).unwrap();
651 match key {
652 SymmetricCryptoKey::XChaCha20Poly1305Key(_) => (),
653 _ => panic!("Invalid key type"),
654 }
655 }
656
657 #[test]
658 fn test_encode_xchacha20_poly1305_key() {
659 let key = SymmetricCryptoKey::make_xchacha20_poly1305_key();
660 let encoded = key.to_encoded();
661 let decoded = SymmetricCryptoKey::try_from(&encoded).unwrap();
662 assert_eq!(key, decoded);
663 }
664
665 #[test]
666 fn test_pad_unpad_key_63() {
667 let original_key = vec![1u8; 63];
668 let mut key_bytes = original_key.clone();
669 let mut encoded_bytes = vec![1u8; 65];
670 encoded_bytes[63] = 2;
671 encoded_bytes[64] = 2;
672 pad_key(&mut key_bytes, 65);
673 assert_eq!(encoded_bytes, key_bytes);
674 let unpadded_key = unpad_key(&key_bytes).unwrap();
675 assert_eq!(original_key, unpadded_key);
676 }
677
678 #[test]
679 fn test_pad_unpad_key_64() {
680 let original_key = vec![1u8; 64];
681 let mut key_bytes = original_key.clone();
682 let mut encoded_bytes = vec![1u8; 65];
683 encoded_bytes[64] = 1;
684 pad_key(&mut key_bytes, 65);
685 assert_eq!(encoded_bytes, key_bytes);
686 let unpadded_key = unpad_key(&key_bytes).unwrap();
687 assert_eq!(original_key, unpadded_key);
688 }
689
690 #[test]
691 fn test_pad_unpad_key_65() {
692 let original_key = vec![1u8; 65];
693 let mut key_bytes = original_key.clone();
694 let mut encoded_bytes = vec![1u8; 66];
695 encoded_bytes[65] = 1;
696 pad_key(&mut key_bytes, 65);
697 assert_eq!(encoded_bytes, key_bytes);
698 let unpadded_key = unpad_key(&key_bytes).unwrap();
699 assert_eq!(original_key, unpadded_key);
700 }
701
702 #[test]
703 fn test_eq_aes_cbc_hmac() {
704 let key1 = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
705 let key2 = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
706 assert_ne!(key1, key2);
707 let key3 = SymmetricCryptoKey::try_from(key1.to_base64()).unwrap();
708 assert_eq!(key1, key3);
709 }
710
711 #[test]
712 fn test_eq_aes_cbc() {
713 let key1 =
714 SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(vec![1u8; 32])).unwrap();
715 let key2 =
716 SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(vec![2u8; 32])).unwrap();
717 assert_ne!(key1, key2);
718 let key3 = SymmetricCryptoKey::try_from(key1.to_base64()).unwrap();
719 assert_eq!(key1, key3);
720 }
721
722 #[test]
723 fn test_eq_xchacha20_poly1305() {
724 let key1 = SymmetricCryptoKey::make_xchacha20_poly1305_key();
725 let key2 = SymmetricCryptoKey::make_xchacha20_poly1305_key();
726 assert_ne!(key1, key2);
727 let key3 = SymmetricCryptoKey::try_from(key1.to_base64()).unwrap();
728 assert_eq!(key1, key3);
729 }
730
731 #[test]
732 fn test_neq_different_key_types() {
733 let key1 = SymmetricCryptoKey::Aes256CbcKey(Aes256CbcKey {
734 enc_key: Box::pin(Array::<u8, U32>::default()),
735 });
736 let key2 = SymmetricCryptoKey::XChaCha20Poly1305Key(XChaCha20Poly1305Key {
737 enc_key: Box::pin(Array::<u8, U32>::default()),
738 key_id: KeyId::from([0; 16]),
739 supported_operations: vec![
740 KeyOperation::Decrypt,
741 KeyOperation::Encrypt,
742 KeyOperation::WrapKey,
743 KeyOperation::UnwrapKey,
744 ],
745 });
746 assert_ne!(key1, key2);
747 }
748
749 #[test]
750 fn test_eq_variant_aes256_cbc() {
751 let key1 = Aes256CbcKey {
752 enc_key: Box::pin(Array::from([1u8; 32])),
753 };
754 let key2 = Aes256CbcKey {
755 enc_key: Box::pin(Array::from([1u8; 32])),
756 };
757 let key3 = Aes256CbcKey {
758 enc_key: Box::pin(Array::from([2u8; 32])),
759 };
760 assert_eq!(key1, key2);
761 assert_ne!(key1, key3);
762 }
763
764 #[test]
765 fn test_eq_variant_aes256_cbc_hmac() {
766 let key1 = Aes256CbcHmacKey {
767 enc_key: Box::pin(Array::from([1u8; 32])),
768 mac_key: Box::pin(Array::from([2u8; 32])),
769 };
770 let key2 = Aes256CbcHmacKey {
771 enc_key: Box::pin(Array::from([1u8; 32])),
772 mac_key: Box::pin(Array::from([2u8; 32])),
773 };
774 let key3 = Aes256CbcHmacKey {
775 enc_key: Box::pin(Array::from([3u8; 32])),
776 mac_key: Box::pin(Array::from([4u8; 32])),
777 };
778 assert_eq!(key1, key2);
779 assert_ne!(key1, key3);
780 }
781
782 #[test]
783 fn test_eq_variant_xchacha20_poly1305() {
784 let key1 = XChaCha20Poly1305Key {
785 enc_key: Box::pin(Array::from([1u8; 32])),
786 key_id: KeyId::from([0; 16]),
787 supported_operations: vec![
788 KeyOperation::Decrypt,
789 KeyOperation::Encrypt,
790 KeyOperation::WrapKey,
791 KeyOperation::UnwrapKey,
792 ],
793 };
794 let key2 = XChaCha20Poly1305Key {
795 enc_key: Box::pin(Array::from([1u8; 32])),
796 key_id: KeyId::from([0; 16]),
797 supported_operations: vec![
798 KeyOperation::Decrypt,
799 KeyOperation::Encrypt,
800 KeyOperation::WrapKey,
801 KeyOperation::UnwrapKey,
802 ],
803 };
804 let key3 = XChaCha20Poly1305Key {
805 enc_key: Box::pin(Array::from([2u8; 32])),
806 key_id: KeyId::from([1; 16]),
807 supported_operations: vec![
808 KeyOperation::Decrypt,
809 KeyOperation::Encrypt,
810 KeyOperation::WrapKey,
811 KeyOperation::UnwrapKey,
812 ],
813 };
814 assert_eq!(key1, key2);
815 assert_ne!(key1, key3);
816 }
817
818 #[test]
819 fn test_neq_different_key_id() {
820 let key1 = XChaCha20Poly1305Key {
821 enc_key: Box::pin(Array::<u8, U32>::default()),
822 key_id: KeyId::from([0; 16]),
823 supported_operations: vec![
824 KeyOperation::Decrypt,
825 KeyOperation::Encrypt,
826 KeyOperation::WrapKey,
827 KeyOperation::UnwrapKey,
828 ],
829 };
830 let key2 = XChaCha20Poly1305Key {
831 enc_key: Box::pin(Array::<u8, U32>::default()),
832 key_id: KeyId::from([1; 16]),
833 supported_operations: vec![
834 KeyOperation::Decrypt,
835 KeyOperation::Encrypt,
836 KeyOperation::WrapKey,
837 KeyOperation::UnwrapKey,
838 ],
839 };
840 assert_ne!(key1, key2);
841
842 let key1 = SymmetricCryptoKey::XChaCha20Poly1305Key(key1);
843 let key2 = SymmetricCryptoKey::XChaCha20Poly1305Key(key2);
844 assert_ne!(key1, key2);
845 }
846}