bitwarden_crypto/safe/
mod.rs1#![doc = include_str!("./README.md")]
2
3mod password_protected_key_envelope;
4pub use password_protected_key_envelope::*;
5mod high_entropy_secret;
6pub use high_entropy_secret::*;
7mod secret_protected_key_envelope;
8pub use secret_protected_key_envelope::*;
9mod symmetric_key_envelope;
10pub use symmetric_key_envelope::*;
11mod data_envelope;
12pub use data_envelope::*;
13mod helpers;
14
15use ciborium::Value;
16
17use crate::{
18 BitwardenLegacyKeyBytes, ContentFormat, CoseKeyBytes, EncodedSymmetricKey, KEY_ID_SIZE,
19 SymmetricCryptoKey,
20 cose::{CONTAINED_KEY_ID, extract_bytes},
21 keys::KeyId,
22 safe::helpers::set_header_value,
23};
24
25pub(super) enum DecodeSealedKeyError {
28 InvalidContentFormat,
30 UnsupportedContentFormat,
32 InvalidKey,
34}
35
36pub(super) fn decode_sealed_symmetric_key(
44 header: &coset::Header,
45 key_bytes: Vec<u8>,
46) -> Result<SymmetricCryptoKey, DecodeSealedKeyError> {
47 let encoded_key = match ContentFormat::try_from(header)
48 .map_err(|_| DecodeSealedKeyError::InvalidContentFormat)?
49 {
50 ContentFormat::BitwardenLegacyKey => {
51 EncodedSymmetricKey::BitwardenLegacyKey(BitwardenLegacyKeyBytes::from(key_bytes))
52 }
53 ContentFormat::CoseKey => EncodedSymmetricKey::CoseKey(CoseKeyBytes::from(key_bytes)),
54 _ => return Err(DecodeSealedKeyError::UnsupportedContentFormat),
55 };
56 SymmetricCryptoKey::try_from(encoded_key).map_err(|_| DecodeSealedKeyError::InvalidKey)
57}
58
59pub(super) fn extract_single_recipient(
64 cose_encrypt: &coset::CoseEncrypt,
65) -> Result<&coset::CoseRecipient, ()> {
66 match cose_encrypt.recipients.as_slice() {
67 [recipient] => Ok(recipient),
68 _ => Err(()),
69 }
70}
71
72pub(super) fn extract_key_id(header: &coset::Header) -> Result<Option<KeyId>, ()> {
75 let key_id_bytes = extract_bytes(header, CONTAINED_KEY_ID, "key id");
76
77 if let Ok(bytes) = key_id_bytes {
78 let key_id_array: [u8; KEY_ID_SIZE] = bytes.as_slice().try_into().map_err(|_| ())?;
79 Ok(Some(KeyId::from(key_id_array)))
80 } else {
81 Ok(None)
82 }
83}
84
85pub(super) fn set_contained_key_id(header: &mut coset::Header, key_id: Option<KeyId>) {
87 if let Some(key_id) = key_id {
88 set_header_value(header, CONTAINED_KEY_ID, Value::from(Vec::from(&key_id)));
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn set_contained_key_id_round_trips_through_extract_key_id() {
98 let key_id = KeyId::from([7u8; KEY_ID_SIZE]);
99 let mut header = coset::HeaderBuilder::new().build();
100
101 set_contained_key_id(&mut header, Some(key_id.clone()));
102
103 assert_eq!(extract_key_id(&header), Ok(Some(key_id)));
104 }
105
106 #[test]
107 fn extract_key_id_returns_none_when_absent() {
108 let header = coset::HeaderBuilder::new().build();
109
110 assert_eq!(extract_key_id(&header), Ok(None));
111 }
112
113 #[test]
114 fn set_contained_key_id_with_none_leaves_header_without_key_id() {
115 let mut header = coset::HeaderBuilder::new().build();
116
117 set_contained_key_id(&mut header, None);
118
119 assert_eq!(extract_key_id(&header), Ok(None));
120 }
121}