1use std::pin::Pin;
2
3use bitwarden_encoding::B64;
4use generic_array::GenericArray;
5use rand::Rng;
6use typenum::U32;
7
8use crate::{
9 BitwardenLegacyKeyBytes, CryptoError, EncString, KeyDecryptable, SymmetricCryptoKey,
10 keys::utils::stretch_key,
11};
12
13#[derive(Clone)]
15pub struct KeyConnectorKey(pub(super) Pin<Box<GenericArray<u8, U32>>>);
16
17impl KeyConnectorKey {
18 pub fn make() -> Self {
20 let mut rng = rand::thread_rng();
21 let mut key = Box::pin(GenericArray::<u8, U32>::default());
22
23 rng.fill(key.as_mut_slice());
24 KeyConnectorKey(key)
25 }
26
27 pub fn encrypt_user_key(
29 &self,
30 user_key: &SymmetricCryptoKey,
31 ) -> crate::error::Result<EncString> {
32 let stretched_key = stretch_key(&self.0)?;
33 let user_key_bytes = user_key.to_encoded();
34 EncString::encrypt_aes256_hmac(user_key_bytes.as_ref(), &stretched_key)
35 }
36
37 pub fn decrypt_user_key(
39 &self,
40 user_key: EncString,
41 ) -> crate::error::Result<SymmetricCryptoKey> {
42 let dec: Vec<u8> = match user_key {
43 EncString::Aes256Cbc_B64 { iv, ref data } => {
47 let legacy_key = Box::pin(GenericArray::clone_from_slice(&self.0));
48 crate::aes::decrypt_aes256(&iv, data.clone(), &legacy_key)?
49 }
50 EncString::Aes256Cbc_HmacSha256_B64 { .. } => {
51 let stretched_key = SymmetricCryptoKey::Aes256CbcHmacKey(stretch_key(&self.0)?);
52 user_key.decrypt_with_key(&stretched_key)?
53 }
54 _ => {
55 return Err(CryptoError::OperationNotSupported(
56 crate::error::UnsupportedOperationError::EncryptionNotImplementedForKey,
57 ));
58 }
59 };
60
61 SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(dec))
62 }
63}
64
65impl std::fmt::Debug for KeyConnectorKey {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 f.debug_struct("KeyConnectorKey").finish()
68 }
69}
70
71impl From<KeyConnectorKey> for B64 {
72 fn from(key: KeyConnectorKey) -> Self {
73 B64::from(key.0.as_slice())
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use bitwarden_encoding::B64;
80 use coset::iana::KeyOperation;
81 use rand_chacha::rand_core::SeedableRng;
82
83 use super::KeyConnectorKey;
84 use crate::{BitwardenLegacyKeyBytes, EncString, SymmetricCryptoKey, UserKey};
85
86 const KEY_CONNECTOR_KEY_BYTES: [u8; 32] = [
87 31, 79, 104, 226, 150, 71, 177, 90, 194, 80, 172, 209, 17, 129, 132, 81, 138, 167, 69, 167,
88 254, 149, 2, 27, 39, 197, 64, 42, 22, 195, 86, 75,
89 ];
90
91 #[test]
92 fn test_make_two_different_keys() {
93 let key1 = KeyConnectorKey::make();
94 let key2 = KeyConnectorKey::make();
95 assert_ne!(key1.0.as_slice(), key2.0.as_slice());
96 }
97
98 #[test]
99 fn test_into_base64() {
100 let key: B64 = KeyConnectorKey(Box::pin(KEY_CONNECTOR_KEY_BYTES.into())).into();
101
102 assert_eq!(
103 "H09o4pZHsVrCUKzREYGEUYqnRaf+lQIbJ8VAKhbDVks=",
104 key.to_string()
105 );
106 }
107
108 #[test]
109 fn test_decrypt_user_key_aes256_cbc() {
110 let key_connector_key = "hvBMMb1t79YssFZkpetYsM3deyVuQv4r88Uj9gvYe08=".to_string();
111 let key_connector_key = SymmetricCryptoKey::try_from(key_connector_key).unwrap();
112 let SymmetricCryptoKey::Aes256CbcKey(key_connector_key) = &key_connector_key else {
113 panic!("Key Connector key is not an Aes256CbcKey");
114 };
115
116 let key_connector_key = KeyConnectorKey(key_connector_key.enc_key.clone());
117
118 let user_key: EncString = "0.tn/heK4HLbbEe+yEkC+kvw==|8QM94f7aVTtjm/bmvRdVxOxiLiiZtHYYO7+oBdjFCkilncesx0iVrXPl+tMKqW+Jo7+FtZdPNsTrL6RdoG7i5QbCRVwK+9010+xm7MTQY8s=".parse().unwrap();
119
120 let decrypted_user_key = key_connector_key.decrypt_user_key(user_key).unwrap();
121 let SymmetricCryptoKey::Aes256CbcHmacKey(user_key_unwrapped) = &decrypted_user_key else {
122 panic!("User key is not an Aes256CbcHmacKey");
123 };
124
125 assert_eq!(
126 user_key_unwrapped.enc_key.as_slice(),
127 [
128 116, 170, 187, 43, 80, 212, 193, 202, 234, 181, 57, 66, 151, 249, 59, 47, 70, 16,
129 57, 4, 170, 78, 85, 241, 152, 232, 91, 57, 9, 87, 209, 245,
130 ]
131 );
132 assert_eq!(
133 user_key_unwrapped.mac_key.as_slice(),
134 [
135 40, 245, 106, 140, 2, 225, 138, 213, 98, 223, 92, 168, 135, 208, 22, 194, 31, 21,
136 178, 252, 203, 198, 35, 174, 53, 218, 254, 151, 235, 57, 7, 98,
137 ]
138 );
139 }
140
141 #[test]
142 fn test_encrypt_decrypt_user_key_aes256_cbc_hmac() {
143 let rng = rand_chacha::ChaCha8Rng::from_seed([0u8; 32]);
144
145 let key_connector_key = KeyConnectorKey(Box::pin(KEY_CONNECTOR_KEY_BYTES.into()));
146
147 let user_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key_internal(rng);
148 let wrapped_user_key = key_connector_key.encrypt_user_key(&user_key).unwrap();
149 let user_key = UserKey::new(user_key);
150
151 let decrypted_user_key = key_connector_key
152 .decrypt_user_key(wrapped_user_key)
153 .unwrap();
154
155 let SymmetricCryptoKey::Aes256CbcHmacKey(user_key_unwrapped) = &decrypted_user_key else {
156 panic!("User key is not an Aes256CbcHmacKey");
157 };
158
159 assert_eq!(
160 user_key_unwrapped.enc_key.as_slice(),
161 [
162 62, 0, 239, 47, 137, 95, 64, 214, 127, 91, 184, 232, 31, 9, 165, 161, 44, 132, 14,
163 195, 206, 154, 127, 59, 24, 27, 225, 136, 239, 113, 26, 30
164 ]
165 );
166 assert_eq!(
167 user_key_unwrapped.mac_key.as_slice(),
168 [
169 152, 76, 225, 114, 185, 33, 111, 65, 159, 68, 83, 103, 69, 109, 86, 25, 49, 74, 66,
170 163, 218, 134, 176, 1, 56, 123, 253, 184, 14, 12, 254, 66
171 ]
172 );
173
174 assert_eq!(
175 decrypted_user_key, user_key.0,
176 "Decrypted key doesn't match user key"
177 );
178 }
179
180 #[test]
181 fn test_encrypt_decrypt_user_key_xchacha20_poly1305() {
182 let key_connector_key = KeyConnectorKey(Box::pin(KEY_CONNECTOR_KEY_BYTES.into()));
183
184 let user_key_b64: B64 = "pQEEAlDib+JxbqMBlcd3KTUesbufAzoAARFvBIQDBAUGIFggt79surJXmqhPhYuuqi9ZyPfieebmtw2OsmN5SDrb4yUB".parse()
185 .unwrap();
186 let user_key =
187 SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(&user_key_b64)).unwrap();
188 let wrapped_user_key = key_connector_key.encrypt_user_key(&user_key).unwrap();
189 let user_key = UserKey::new(user_key);
190
191 let decrypted_user_key = key_connector_key
192 .decrypt_user_key(wrapped_user_key)
193 .unwrap();
194
195 let SymmetricCryptoKey::XChaCha20Poly1305Key(user_key_unwrapped) = &decrypted_user_key
196 else {
197 panic!("User key is not an XChaCha20Poly1305Key");
198 };
199
200 assert_eq!(
201 user_key_unwrapped.enc_key.as_slice(),
202 [
203 183, 191, 108, 186, 178, 87, 154, 168, 79, 133, 139, 174, 170, 47, 89, 200, 247,
204 226, 121, 230, 230, 183, 13, 142, 178, 99, 121, 72, 58, 219, 227, 37
205 ]
206 );
207 assert_eq!(
208 user_key_unwrapped.key_id.as_slice(),
209 [
210 226, 111, 226, 113, 110, 163, 1, 149, 199, 119, 41, 53, 30, 177, 187, 159
211 ]
212 );
213 assert_eq!(
214 user_key_unwrapped.supported_operations,
215 [
216 KeyOperation::Encrypt,
217 KeyOperation::Decrypt,
218 KeyOperation::WrapKey,
219 KeyOperation::UnwrapKey
220 ]
221 );
222
223 assert_eq!(
224 decrypted_user_key, user_key.0,
225 "Decrypted key doesn't match user key"
226 );
227 }
228}