|
1 | 1 | use std::str::FromStr;
|
2 | 2 |
|
| 3 | +use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; |
3 | 4 | use bitwarden_crypto::{
|
4 |
| - CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey, SymmetricCryptoKey, |
| 5 | + AsymmetricCryptoKey, AsymmetricPublicCryptoKey, CryptoError, Decryptable, EncString, |
| 6 | + Encryptable, Kdf, KeyDecryptable, KeyEncryptable, KeyStore, MasterKey, SymmetricCryptoKey, |
| 7 | + UnsignedSharedKey, |
5 | 8 | };
|
6 | 9 | use wasm_bindgen::prelude::*;
|
7 | 10 |
|
@@ -107,6 +110,144 @@ impl PureCrypto {
|
107 | 110 | pub fn generate_user_key_xchacha20_poly1305() -> Vec<u8> {
|
108 | 111 | SymmetricCryptoKey::make_xchacha20_poly1305_key().to_encoded()
|
109 | 112 | }
|
| 113 | + |
| 114 | + // Key wrap |
| 115 | + pub fn wrap_symmetric_key( |
| 116 | + key_to_be_wrapped: Vec<u8>, |
| 117 | + wrapping_key: Vec<u8>, |
| 118 | + ) -> Result<String, CryptoError> { |
| 119 | + let tmp_store: KeyStore<KeyIds> = KeyStore::default(); |
| 120 | + let mut context = tmp_store.context(); |
| 121 | + #[allow(deprecated)] |
| 122 | + context.set_symmetric_key( |
| 123 | + SymmetricKeyId::Local("wrapping_key"), |
| 124 | + SymmetricCryptoKey::try_from(wrapping_key)?, |
| 125 | + )?; |
| 126 | + #[allow(deprecated)] |
| 127 | + context.set_symmetric_key( |
| 128 | + SymmetricKeyId::Local("key_to_wrap"), |
| 129 | + SymmetricCryptoKey::try_from(key_to_be_wrapped)?, |
| 130 | + )?; |
| 131 | + // Note: The order of arguments is different here, and should probably be refactored |
| 132 | + Ok(context |
| 133 | + .wrap_symmetric_key( |
| 134 | + SymmetricKeyId::Local("wrapping_key"), |
| 135 | + SymmetricKeyId::Local("key_to_wrap"), |
| 136 | + )? |
| 137 | + .to_string()) |
| 138 | + } |
| 139 | + |
| 140 | + pub fn unwrap_symmetric_key( |
| 141 | + wrapped_key: String, |
| 142 | + wrapping_key: Vec<u8>, |
| 143 | + ) -> Result<Vec<u8>, CryptoError> { |
| 144 | + let tmp_store: KeyStore<KeyIds> = KeyStore::default(); |
| 145 | + let mut context = tmp_store.context(); |
| 146 | + #[allow(deprecated)] |
| 147 | + context.set_symmetric_key( |
| 148 | + SymmetricKeyId::Local("wrapping_key"), |
| 149 | + SymmetricCryptoKey::try_from(wrapping_key)?, |
| 150 | + )?; |
| 151 | + // Note: The order of arguments is different here, and should probably be refactored |
| 152 | + context.unwrap_symmetric_key( |
| 153 | + SymmetricKeyId::Local("wrapping_key"), |
| 154 | + SymmetricKeyId::Local("wrapped_key"), |
| 155 | + &EncString::from_str(wrapped_key.as_str())?, |
| 156 | + )?; |
| 157 | + #[allow(deprecated)] |
| 158 | + let key = context.dangerous_get_symmetric_key(SymmetricKeyId::Local("wrapped_key"))?; |
| 159 | + Ok(key.to_encoded()) |
| 160 | + } |
| 161 | + |
| 162 | + pub fn wrap_encapsulation_key( |
| 163 | + encapsulation_key: Vec<u8>, |
| 164 | + wrapping_key: Vec<u8>, |
| 165 | + ) -> Result<String, CryptoError> { |
| 166 | + let tmp_store: KeyStore<KeyIds> = KeyStore::default(); |
| 167 | + let mut context = tmp_store.context(); |
| 168 | + #[allow(deprecated)] |
| 169 | + context.set_symmetric_key( |
| 170 | + SymmetricKeyId::Local("wrapping_key"), |
| 171 | + SymmetricCryptoKey::try_from(wrapping_key)?, |
| 172 | + )?; |
| 173 | + // Note: The order of arguments is different here, and should probably be refactored |
| 174 | + Ok(encapsulation_key |
| 175 | + .encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))? |
| 176 | + .to_string()) |
| 177 | + } |
| 178 | + |
| 179 | + pub fn unwrap_encapsulation_key( |
| 180 | + wrapped_key: String, |
| 181 | + wrapping_key: Vec<u8>, |
| 182 | + ) -> Result<Vec<u8>, CryptoError> { |
| 183 | + let tmp_store: KeyStore<KeyIds> = KeyStore::default(); |
| 184 | + let mut context = tmp_store.context(); |
| 185 | + #[allow(deprecated)] |
| 186 | + context.set_symmetric_key( |
| 187 | + SymmetricKeyId::Local("wrapping_key"), |
| 188 | + SymmetricCryptoKey::try_from(wrapping_key)?, |
| 189 | + )?; |
| 190 | + // Note: The order of arguments is different here, and should probably be refactored |
| 191 | + EncString::from_str(wrapped_key.as_str())? |
| 192 | + .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key")) |
| 193 | + } |
| 194 | + |
| 195 | + pub fn wrap_decapsulation_key( |
| 196 | + decapsulation_key: Vec<u8>, |
| 197 | + wrapping_key: Vec<u8>, |
| 198 | + ) -> Result<String, CryptoError> { |
| 199 | + let tmp_store: KeyStore<KeyIds> = KeyStore::default(); |
| 200 | + let mut context = tmp_store.context(); |
| 201 | + #[allow(deprecated)] |
| 202 | + context.set_symmetric_key( |
| 203 | + SymmetricKeyId::Local("wrapping_key"), |
| 204 | + SymmetricCryptoKey::try_from(wrapping_key)?, |
| 205 | + )?; |
| 206 | + // Note: The order of arguments is different here, and should probably be refactored |
| 207 | + Ok(decapsulation_key |
| 208 | + .encrypt(&mut context, SymmetricKeyId::Local("wrapping_key"))? |
| 209 | + .to_string()) |
| 210 | + } |
| 211 | + |
| 212 | + pub fn unwrap_decapsulation_key( |
| 213 | + wrapped_key: String, |
| 214 | + wrapping_key: Vec<u8>, |
| 215 | + ) -> Result<Vec<u8>, CryptoError> { |
| 216 | + let tmp_store: KeyStore<KeyIds> = KeyStore::default(); |
| 217 | + let mut context = tmp_store.context(); |
| 218 | + #[allow(deprecated)] |
| 219 | + context.set_symmetric_key( |
| 220 | + SymmetricKeyId::Local("wrapping_key"), |
| 221 | + SymmetricCryptoKey::try_from(wrapping_key)?, |
| 222 | + )?; |
| 223 | + // Note: The order of arguments is different here, and should probably be refactored |
| 224 | + EncString::from_str(wrapped_key.as_str())? |
| 225 | + .decrypt(&mut context, SymmetricKeyId::Local("wrapping_key")) |
| 226 | + } |
| 227 | + |
| 228 | + // Key encapsulation |
| 229 | + pub fn encapsulate_key_unsigned( |
| 230 | + shared_key: Vec<u8>, |
| 231 | + encapsulation_key: Vec<u8>, |
| 232 | + ) -> Result<String, CryptoError> { |
| 233 | + let encapsulation_key = AsymmetricPublicCryptoKey::from_der(encapsulation_key.as_slice())?; |
| 234 | + Ok(UnsignedSharedKey::encapsulate_key_unsigned( |
| 235 | + &SymmetricCryptoKey::try_from(shared_key)?, |
| 236 | + &encapsulation_key, |
| 237 | + )? |
| 238 | + .to_string()) |
| 239 | + } |
| 240 | + |
| 241 | + pub fn decapsulate_key_unsigned( |
| 242 | + encapsulated_key: String, |
| 243 | + decapsulation_key: Vec<u8>, |
| 244 | + ) -> Result<Vec<u8>, CryptoError> { |
| 245 | + Ok(UnsignedSharedKey::from_str(encapsulated_key.as_str())? |
| 246 | + .decapsulate_key_unsigned(&AsymmetricCryptoKey::from_der( |
| 247 | + decapsulation_key.as_slice(), |
| 248 | + )?)? |
| 249 | + .to_encoded()) |
| 250 | + } |
110 | 251 | }
|
111 | 252 |
|
112 | 253 | #[cfg(test)]
|
@@ -134,6 +275,35 @@ mod tests {
|
134 | 275 | 120, 104, 144, 4, 76, 3,
|
135 | 276 | ];
|
136 | 277 |
|
| 278 | + const PEM_KEY: &str = "-----BEGIN PRIVATE KEY----- |
| 279 | +MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDiTQVuzhdygFz5 |
| 280 | +qv14i+XFDGTnDravzUQT1hPKPGUZOUSZ1gwdNgkWqOIaOnR65BHEnL0sp4bnuiYc |
| 281 | +afeK2JAW5Sc8Z7IxBNSuAwhQmuKx3RochMIiuCkI2/p+JvUQoJu6FBNm8OoJ4Cwm |
| 282 | +qqHGZESMfnpQDCuDrB3JdJEdXhtmnl0C48sGjOk3WaBMcgGqn8LbJDUlyu1zdqyv |
| 283 | +b0waJf0iV4PJm2fkUl7+57D/2TkpbCqURVnZK1FFIEg8mr6FzSN1F2pOfktkNYZw |
| 284 | +P7MSNR7o81CkRSCMr7EkIVa+MZYMBx106BMK7FXgWB7nbSpsWKxBk7ZDHkID2fam |
| 285 | +rEcVtrzDAgMBAAECggEBAKwq9OssGGKgjhvUnyrLJHAZ0dqIMyzk+dotkLjX4gKi |
| 286 | +szJmyqiep6N5sStLNbsZMPtoU/RZMCW0VbJgXFhiEp2YkZU/Py5UAoqw++53J+kx |
| 287 | +0d/IkPphKbb3xUec0+1mg5O6GljDCQuiZXS1dIa/WfeZcezclW6Dz9WovY6ePjJ+ |
| 288 | +8vEBR1icbNKzyeINd6MtPtpcgQPHtDwHvhPyUDbKDYGbLvjh9nui8h4+ZUlXKuVR |
| 289 | +jB0ChxiKV1xJRjkrEVoulOOicd5r597WfB2ghax3pvRZ4MdXemCXm3gQYqPVKach |
| 290 | +vGU+1cPQR/MBJZpxT+EZA97xwtFS3gqwbxJaNFcoE8ECgYEA9OaeYZhQPDo485tI |
| 291 | +1u/Z7L/3PNape9hBQIXoW7+MgcQ5NiWqYh8Jnj43EIYa0wM/ECQINr1Za8Q5e6KR |
| 292 | +J30FcU+kfyjuQ0jeXdNELGU/fx5XXNg/vV8GevHwxRlwzqZTCg6UExUZzbYEQqd7 |
| 293 | +l+wPyETGeua5xCEywA1nX/D101kCgYEA7I6aMFjhEjO71RmzNhqjKJt6DOghoOfQ |
| 294 | +TjhaaanNEhLYSbenFz1mlb21mW67ulmz162saKdIYLxQNJIP8ZPmxh4ummOJI8w9 |
| 295 | +ClHfo8WuCI2hCjJ19xbQJocSbTA5aJg6lA1IDVZMDbQwsnAByPRGpaLHBT/Q9Bye |
| 296 | +KvCMB+9amXsCgYEAx65yXSkP4sumPBrVHUub6MntERIGRxBgw/drKcPZEMWp0FiN |
| 297 | +wEuGUBxyUWrG3F69QK/gcqGZE6F/LSu0JvptQaKqgXQiMYJsrRvhbkFvsHpQyUcZ |
| 298 | +UZL1ebFjm5HOxPAgrQaN/bEqxOwwNRjSUWEMzUImg3c06JIZCzbinvudtKECgYEA |
| 299 | +kY3JF/iIPI/yglP27lKDlCfeeHSYxI3+oTKRhzSAxx8rUGidenJAXeDGDauR/T7W |
| 300 | +pt3pGNfddBBK9Z3uC4Iq3DqUCFE4f/taj7ADAJ1Q0Vh7/28/IJM77ojr8J1cpZwN |
| 301 | +Zy2o6PPxhfkagaDjqEeN9Lrs5LD4nEvDkr5CG1vOjmMCgYEAvIBFKRm31NyF8jLi |
| 302 | +CVuPwC5PzrW5iThDmsWTaXFpB3esUsbICO2pEz872oeQS+Em4GO5vXUlpbbFPzup |
| 303 | +PFhA8iMJ8TAvemhvc7oM0OZqpU6p3K4seHf6BkwLxumoA3vDJfovu9RuXVcJVOnf |
| 304 | +DnqOsltgPomWZ7xVfMkm9niL2OA= |
| 305 | +-----END PRIVATE KEY-----"; |
| 306 | + |
137 | 307 | #[test]
|
138 | 308 | fn test_symmetric_decrypt() {
|
139 | 309 | let enc_string = EncString::from_str(ENCRYPTED).unwrap();
|
@@ -228,4 +398,58 @@ mod tests {
|
228 | 398 | .unwrap();
|
229 | 399 | assert_eq!(user_key, decrypted_user_key);
|
230 | 400 | }
|
| 401 | + |
| 402 | + #[test] |
| 403 | + fn test_wrap_unwrap_symmetric_key() { |
| 404 | + let key_to_be_wrapped = PureCrypto::generate_user_key_aes256_cbc_hmac(); |
| 405 | + let wrapping_key = PureCrypto::generate_user_key_aes256_cbc_hmac(); |
| 406 | + let wrapped_key = |
| 407 | + PureCrypto::wrap_symmetric_key(key_to_be_wrapped.clone(), wrapping_key.clone()) |
| 408 | + .unwrap(); |
| 409 | + let unwrapped_key = PureCrypto::unwrap_symmetric_key(wrapped_key, wrapping_key).unwrap(); |
| 410 | + assert_eq!(key_to_be_wrapped, unwrapped_key); |
| 411 | + } |
| 412 | + |
| 413 | + #[test] |
| 414 | + fn test_wrap_encapsulation_key() { |
| 415 | + let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap(); |
| 416 | + let encapsulation_key = decapsulation_key.to_public_der().unwrap(); |
| 417 | + let wrapping_key = PureCrypto::generate_user_key_aes256_cbc_hmac(); |
| 418 | + let wrapped_key = |
| 419 | + PureCrypto::wrap_encapsulation_key(encapsulation_key.clone(), wrapping_key.clone()) |
| 420 | + .unwrap(); |
| 421 | + let unwrapped_key = |
| 422 | + PureCrypto::unwrap_encapsulation_key(wrapped_key, wrapping_key).unwrap(); |
| 423 | + assert_eq!(encapsulation_key, unwrapped_key); |
| 424 | + } |
| 425 | + |
| 426 | + #[test] |
| 427 | + fn test_wrap_decapsulation_key() { |
| 428 | + let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap(); |
| 429 | + let wrapping_key = PureCrypto::generate_user_key_aes256_cbc_hmac(); |
| 430 | + let wrapped_key = PureCrypto::wrap_decapsulation_key( |
| 431 | + decapsulation_key.to_der().unwrap(), |
| 432 | + wrapping_key.clone(), |
| 433 | + ) |
| 434 | + .unwrap(); |
| 435 | + let unwrapped_key = |
| 436 | + PureCrypto::unwrap_decapsulation_key(wrapped_key, wrapping_key).unwrap(); |
| 437 | + assert_eq!(decapsulation_key.to_der().unwrap(), unwrapped_key); |
| 438 | + } |
| 439 | + |
| 440 | + #[test] |
| 441 | + fn test_encapsulate_key_unsigned() { |
| 442 | + let shared_key = PureCrypto::generate_user_key_aes256_cbc_hmac(); |
| 443 | + let decapsulation_key = AsymmetricCryptoKey::from_pem(PEM_KEY).unwrap(); |
| 444 | + let encapsulation_key = decapsulation_key.to_public_der().unwrap(); |
| 445 | + let encapsulated_key = |
| 446 | + PureCrypto::encapsulate_key_unsigned(shared_key.clone(), encapsulation_key.clone()) |
| 447 | + .unwrap(); |
| 448 | + let unwrapped_key = PureCrypto::decapsulate_key_unsigned( |
| 449 | + encapsulated_key, |
| 450 | + decapsulation_key.to_der().unwrap(), |
| 451 | + ) |
| 452 | + .unwrap(); |
| 453 | + assert_eq!(shared_key, unwrapped_key); |
| 454 | + } |
231 | 455 | }
|
0 commit comments