diff --git a/crates/bitwarden-core/src/auth/auth_client.rs b/crates/bitwarden-core/src/auth/auth_client.rs index 559a5dd9b..224f69f82 100644 --- a/crates/bitwarden-core/src/auth/auth_client.rs +++ b/crates/bitwarden-core/src/auth/auth_client.rs @@ -1,7 +1,6 @@ +use bitwarden_crypto::WrappedSymmetricKey; #[cfg(feature = "internal")] -use bitwarden_crypto::{ - CryptoError, DeviceKey, EncString, Kdf, TrustDeviceResponse, UnsignedSharedKey, -}; +use bitwarden_crypto::{CryptoError, DeviceKey, Kdf, TrustDeviceResponse, UnsignedSharedKey}; #[cfg(feature = "secrets")] use crate::auth::login::{login_access_token, AccessTokenLoginRequest, AccessTokenLoginResponse}; @@ -135,7 +134,7 @@ impl AuthClient { pub fn validate_password_user_key( &self, password: String, - encrypted_user_key: String, + encrypted_user_key: WrappedSymmetricKey, ) -> Result { validate_password_user_key(&self.client, password, encrypted_user_key) } @@ -143,7 +142,7 @@ impl AuthClient { pub fn validate_pin( &self, pin: String, - pin_protected_user_key: EncString, + pin_protected_user_key: WrappedSymmetricKey, ) -> Result { validate_pin(&self.client, pin, pin_protected_user_key) } diff --git a/crates/bitwarden-core/src/auth/auth_request.rs b/crates/bitwarden-core/src/auth/auth_request.rs index c265584f4..7a2a7a186 100644 --- a/crates/bitwarden-core/src/auth/auth_request.rs +++ b/crates/bitwarden-core/src/auth/auth_request.rs @@ -72,7 +72,7 @@ pub(crate) fn auth_request_decrypt_master_key( let master_key: SymmetricCryptoKey = master_key.decapsulate_key_unsigned(&key)?; let master_key = MasterKey::try_from(&master_key)?; - Ok(master_key.decrypt_user_key(user_key)?) + Ok(master_key.decrypt_user_key(user_key.into())?) } #[allow(missing_docs)] @@ -137,7 +137,7 @@ fn test_auth_request() { mod tests { use std::num::NonZeroU32; - use bitwarden_crypto::{Kdf, MasterKey}; + use bitwarden_crypto::{EncString, Kdf, MasterKey}; use super::*; use crate::{ @@ -158,11 +158,11 @@ mod tests { ) .unwrap(); - let user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(); + let user_key: EncString = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(); let private_key ="2.yN7l00BOlUE0Sb0M//Q53w==|EwKG/BduQRQ33Izqc/ogoBROIoI5dmgrxSo82sgzgAMIBt3A2FZ9vPRMY+GWT85JiqytDitGR3TqwnFUBhKUpRRAq4x7rA6A1arHrFp5Tp1p21O3SfjtvB3quiOKbqWk6ZaU1Np9HwqwAecddFcB0YyBEiRX3VwF2pgpAdiPbSMuvo2qIgyob0CUoC/h4Bz1be7Qa7B0Xw9/fMKkB1LpOm925lzqosyMQM62YpMGkjMsbZz0uPopu32fxzDWSPr+kekNNyLt9InGhTpxLmq1go/pXR2uw5dfpXc5yuta7DB0EGBwnQ8Vl5HPdDooqOTD9I1jE0mRyuBpWTTI3FRnu3JUh3rIyGBJhUmHqGZvw2CKdqHCIrQeQkkEYqOeJRJVdBjhv5KGJifqT3BFRwX/YFJIChAQpebNQKXe/0kPivWokHWwXlDB7S7mBZzhaAPidZvnuIhalE2qmTypDwHy22FyqV58T8MGGMchcASDi/QXI6kcdpJzPXSeU9o+NC68QDlOIrMVxKFeE7w7PvVmAaxEo0YwmuAzzKy9QpdlK0aab/xEi8V4iXj4hGepqAvHkXIQd+r3FNeiLfllkb61p6WTjr5urcmDQMR94/wYoilpG5OlybHdbhsYHvIzYoLrC7fzl630gcO6t4nM24vdB6Ymg9BVpEgKRAxSbE62Tqacxqnz9AcmgItb48NiR/He3n3ydGjPYuKk/ihZMgEwAEZvSlNxYONSbYrIGDtOY+8Nbt6KiH3l06wjZW8tcmFeVlWv+tWotnTY9IqlAfvNVTjtsobqtQnvsiDjdEVtNy/s2ci5TH+NdZluca2OVEr91Wayxh70kpM6ib4UGbfdmGgCo74gtKvKSJU0rTHakQ5L9JlaSDD5FamBRyI0qfL43Ad9qOUZ8DaffDCyuaVyuqk7cz9HwmEmvWU3VQ+5t06n/5kRDXttcw8w+3qClEEdGo1KeENcnXCB32dQe3tDTFpuAIMLqwXs6FhpawfZ5kPYvLPczGWaqftIs/RXJ/EltGc0ugw2dmTLpoQhCqrcKEBDoYVk0LDZKsnzitOGdi9mOWse7Se8798ib1UsHFUjGzISEt6upestxOeupSTOh0v4+AjXbDzRUyogHww3V+Bqg71bkcMxtB+WM+pn1XNbVTyl9NR040nhP7KEf6e9ruXAtmrBC2ah5cFEpLIot77VFZ9ilLuitSz+7T8n1yAh1IEG6xxXxninAZIzi2qGbH69O5RSpOJuJTv17zTLJQIIc781JwQ2TTwTGnx5wZLbffhCasowJKd2EVcyMJyhz6ru0PvXWJ4hUdkARJs3Xu8dus9a86N8Xk6aAPzBDqzYb1vyFIfBxP0oO8xFHgd30Cgmz8UrSE3qeWRrF8ftrI6xQnFjHBGWD/JWSvd6YMcQED0aVuQkuNW9ST/DzQThPzRfPUoiL10yAmV7Ytu4fR3x2sF0Yfi87YhHFuCMpV/DsqxmUizyiJuD938eRcH8hzR/VO53Qo3UIsqOLcyXtTv6THjSlTopQ+JOLOnHm1w8dzYbLN44OG44rRsbihMUQp+wUZ6bsI8rrOnm9WErzkbQFbrfAINdoCiNa6cimYIjvvnMTaFWNymqY1vZxGztQiMiHiHYwTfwHTXrb9j0uPM=|09J28iXv9oWzYtzK2LBT6Yht4IT4MijEkk0fwFdrVQ4=".parse().unwrap(); client .internal - .initialize_user_crypto_master_key(master_key, user_key, private_key) + .initialize_user_crypto_master_key(master_key, user_key.into(), private_key) .unwrap(); let public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvyLRDUwXB4BfQ507D4meFPmwn5zwy3IqTPJO4plrrhnclWahXa240BzyFW9gHgYu+Jrgms5xBfRTBMcEsqqNm7+JpB6C1B6yvnik0DpJgWQw1rwvy4SUYidpR/AWbQi47n/hvnmzI/sQxGddVfvWu1iTKOlf5blbKYAXnUE5DZBGnrWfacNXwRRdtP06tFB0LwDgw+91CeLSJ9py6dm1qX5JIxoO8StJOQl65goLCdrTWlox+0Jh4xFUfCkb+s3px+OhSCzJbvG/hlrSRcUz5GnwlCEyF3v5lfUtV96MJD+78d8pmH6CfFAp2wxKRAbGdk+JccJYO6y6oIXd3Fm7twIDAQAB"; @@ -196,7 +196,7 @@ mod tests { let private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzLtEUdxfcLxDj84yaGFsVF5hZ8Hjlb08NMQDy1RnBma06I3ZESshLYzVz4r/gegMn9OOltfV/Yxlyvida8oW6qdlfJ7AVz6Oa8pV7BiL40C7b76+oqraQpyYw2HChANB1AhXL9SqWngKmLZwjA7qiCrmcc0kZHeOb4KnKtp9iVvPVs+8veFvKgYO4ba2AAOHKFdR0W55/agXfAy+fWUAkC8mc9ikyJdQWaPV6OZvC2XFkOseBQm9Rynudh3BQpoWiL6w620efe7t5k+02/EyOFJL9f/XEEjM/+Yo0t3LAfkuhHGeKiRST59Xc9hTEmyJTeVXROtz+0fjqOp3xkaObAgMBAAECggEACs4xhnO0HaZhh1/iH7zORMIRXKeyxP2LQiTR8xwN5JJ9wRWmGAR9VasS7EZFTDidIGVME2u/h4s5EqXnhxfO+0gGksVvgNXJ/qw87E8K2216g6ZNo6vSGA7H1GH2voWwejJ4/k/cJug6dz2S402rRAKh2Wong1arYHSkVlQp3diiMa5FHAOSE+Cy09O2ZsaF9IXQYUtlW6AVXFrBEPYH2kvkaPXchh8VETMijo6tbvoKLnUHe+wTaDMls7hy8exjtVyI59r3DNzjy1lNGaGb5QSnFMXR+eHhPZc844Wv02MxC15zKABADrl58gpJyjTl6XpDdHCYGsmGpVGH3X9TQQKBgQDz/9beFjzq59ve6rGwn+EtnQfSsyYT+jr7GN8lNEXb3YOFXBgPhfFIcHRh2R00Vm9w2ApfAx2cd8xm2I6HuvQ1Os7g26LWazvuWY0Qzb+KaCLQTEGH1RnTq6CCG+BTRq/a3J8M4t38GV5TWlzv8wr9U4dl6FR4efjb65HXs1GQ4QKBgQC7/uHfrOTEHrLeIeqEuSl0vWNqEotFKdKLV6xpOvNuxDGbgW4/r/zaxDqt0YBOXmRbQYSEhmO3oy9J6XfE1SUln0gbavZeW0HESCAmUIC88bDnspUwS9RxauqT5aF8ODKN/bNCWCnBM1xyonPOs1oT1nyparJVdQoG//Y7vkB3+wKBgBqLqPq8fKAp3XfhHLfUjREDVoiLyQa/YI9U42IOz9LdxKNLo6p8rgVthpvmnRDGnpUuS+KOWjhdqDVANjF6G3t3DG7WNl8Rh5Gk2H4NhFswfSkgQrjebFLlBy9gjQVCWXt8KSmjvPbiY6q52Aaa8IUjA0YJAregvXxfopxO+/7BAoGARicvEtDp7WWnSc1OPoj6N14VIxgYcI7SyrzE0d/1x3ffKzB5e7qomNpxKzvqrVP8DzG7ydh8jaKPmv1MfF8tpYRy3AhmN3/GYwCnPqT75YYrhcrWcVdax5gmQVqHkFtIQkRSCIftzPLlpMGKha/YBV8c1fvC4LD0NPh/Ynv0gtECgYEAyOZg95/kte0jpgUEgwuMrzkhY/AaUJULFuR5MkyvReEbtSBQwV5tx60+T95PHNiFooWWVXiLMsAgyI2IbkxVR1Pzdri3gWK5CTfqb7kLuaj/B7SGvBa2Sxo478KS5K8tBBBWkITqo+wLC0mn3uZi1dyMWO1zopTA+KtEGF2dtGQ="; let enc_master_key = "4.dxbd5OMwi/Avy7DQxvLV+Z7kDJgHBtg/jAbgYNO7QU0Zii4rLFNco2lS5aS9z42LTZHc2p5HYwn2ZwkZNfHsQ6//d5q40MDgGYJMKBXOZP62ZHhct1XsvYBmtcUtIOm5j2HSjt2pjEuGAc1LbyGIWRJJQ3Lp1ULbL2m71I+P23GF36JyOM8SUWvpvxE/3+qqVhRFPG2VqMCYa2kLLxwVfUmpV+KKjX1TXsrq6pfJIwHNwHw4h7MSfD8xTy2bx4MiBt638Z9Vt1pGsSQkh9RgPvCbnhuCpZQloUgJ8ByLVEcrlKx3yaaxiQXvte+ZhuOI7rGdjmoVoOzisooje4JgYw==".parse().unwrap(); - let enc_user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(); + let enc_user_key: EncString = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(); let dec = auth_request_decrypt_master_key(private_key.to_owned(), enc_master_key, enc_user_key) .unwrap(); @@ -219,7 +219,7 @@ mod tests { }; let email = "test@bitwarden.com"; - let user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(); + let user_key: EncString = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(); let private_key = "2.yN7l00BOlUE0Sb0M//Q53w==|EwKG/BduQRQ33Izqc/ogoBROIoI5dmgrxSo82sgzgAMIBt3A2FZ9vPRMY+GWT85JiqytDitGR3TqwnFUBhKUpRRAq4x7rA6A1arHrFp5Tp1p21O3SfjtvB3quiOKbqWk6ZaU1Np9HwqwAecddFcB0YyBEiRX3VwF2pgpAdiPbSMuvo2qIgyob0CUoC/h4Bz1be7Qa7B0Xw9/fMKkB1LpOm925lzqosyMQM62YpMGkjMsbZz0uPopu32fxzDWSPr+kekNNyLt9InGhTpxLmq1go/pXR2uw5dfpXc5yuta7DB0EGBwnQ8Vl5HPdDooqOTD9I1jE0mRyuBpWTTI3FRnu3JUh3rIyGBJhUmHqGZvw2CKdqHCIrQeQkkEYqOeJRJVdBjhv5KGJifqT3BFRwX/YFJIChAQpebNQKXe/0kPivWokHWwXlDB7S7mBZzhaAPidZvnuIhalE2qmTypDwHy22FyqV58T8MGGMchcASDi/QXI6kcdpJzPXSeU9o+NC68QDlOIrMVxKFeE7w7PvVmAaxEo0YwmuAzzKy9QpdlK0aab/xEi8V4iXj4hGepqAvHkXIQd+r3FNeiLfllkb61p6WTjr5urcmDQMR94/wYoilpG5OlybHdbhsYHvIzYoLrC7fzl630gcO6t4nM24vdB6Ymg9BVpEgKRAxSbE62Tqacxqnz9AcmgItb48NiR/He3n3ydGjPYuKk/ihZMgEwAEZvSlNxYONSbYrIGDtOY+8Nbt6KiH3l06wjZW8tcmFeVlWv+tWotnTY9IqlAfvNVTjtsobqtQnvsiDjdEVtNy/s2ci5TH+NdZluca2OVEr91Wayxh70kpM6ib4UGbfdmGgCo74gtKvKSJU0rTHakQ5L9JlaSDD5FamBRyI0qfL43Ad9qOUZ8DaffDCyuaVyuqk7cz9HwmEmvWU3VQ+5t06n/5kRDXttcw8w+3qClEEdGo1KeENcnXCB32dQe3tDTFpuAIMLqwXs6FhpawfZ5kPYvLPczGWaqftIs/RXJ/EltGc0ugw2dmTLpoQhCqrcKEBDoYVk0LDZKsnzitOGdi9mOWse7Se8798ib1UsHFUjGzISEt6upestxOeupSTOh0v4+AjXbDzRUyogHww3V+Bqg71bkcMxtB+WM+pn1XNbVTyl9NR040nhP7KEf6e9ruXAtmrBC2ah5cFEpLIot77VFZ9ilLuitSz+7T8n1yAh1IEG6xxXxninAZIzi2qGbH69O5RSpOJuJTv17zTLJQIIc781JwQ2TTwTGnx5wZLbffhCasowJKd2EVcyMJyhz6ru0PvXWJ4hUdkARJs3Xu8dus9a86N8Xk6aAPzBDqzYb1vyFIfBxP0oO8xFHgd30Cgmz8UrSE3qeWRrF8ftrI6xQnFjHBGWD/JWSvd6YMcQED0aVuQkuNW9ST/DzQThPzRfPUoiL10yAmV7Ytu4fR3x2sF0Yfi87YhHFuCMpV/DsqxmUizyiJuD938eRcH8hzR/VO53Qo3UIsqOLcyXtTv6THjSlTopQ+JOLOnHm1w8dzYbLN44OG44rRsbihMUQp+wUZ6bsI8rrOnm9WErzkbQFbrfAINdoCiNa6cimYIjvvnMTaFWNymqY1vZxGztQiMiHiHYwTfwHTXrb9j0uPM=|09J28iXv9oWzYtzK2LBT6Yht4IT4MijEkk0fwFdrVQ4="; // Initialize an existing client which is unlocked @@ -229,7 +229,11 @@ mod tests { existing_device .internal - .initialize_user_crypto_master_key(master_key, user_key, private_key.parse().unwrap()) + .initialize_user_crypto_master_key( + master_key, + user_key.into(), + private_key.parse().unwrap(), + ) .unwrap(); // Initialize a new device which will request to be logged in diff --git a/crates/bitwarden-core/src/auth/key_connector.rs b/crates/bitwarden-core/src/auth/key_connector.rs index b4006b472..b7eb1b8a2 100644 --- a/crates/bitwarden-core/src/auth/key_connector.rs +++ b/crates/bitwarden-core/src/auth/key_connector.rs @@ -16,7 +16,7 @@ pub(super) fn make_key_connector_keys( Ok(KeyConnectorResponse { master_key: master_key.to_base64(), - encrypted_user_key: encrypted_user_key.to_string(), + encrypted_user_key: encrypted_user_key.into_inner().to_string(), keys, }) } diff --git a/crates/bitwarden-core/src/auth/login/api_key.rs b/crates/bitwarden-core/src/auth/login/api_key.rs index 649e448f8..56350252b 100644 --- a/crates/bitwarden-core/src/auth/login/api_key.rs +++ b/crates/bitwarden-core/src/auth/login/api_key.rs @@ -51,9 +51,11 @@ pub(crate) async fn login_api_key( let user_key: EncString = require!(r.key.as_deref()).parse()?; let private_key: EncString = require!(r.private_key.as_deref()).parse()?; - client - .internal - .initialize_user_crypto_master_key(master_key, user_key, private_key)?; + client.internal.initialize_user_crypto_master_key( + master_key, + user_key.into(), + private_key, + )?; } Ok(ApiKeyLoginResponse::process_response(response)) diff --git a/crates/bitwarden-core/src/auth/login/password.rs b/crates/bitwarden-core/src/auth/login/password.rs index 9d9390b85..9552c008c 100644 --- a/crates/bitwarden-core/src/auth/login/password.rs +++ b/crates/bitwarden-core/src/auth/login/password.rs @@ -53,9 +53,11 @@ pub(crate) async fn login_password( let user_key: EncString = require!(r.key.as_deref()).parse()?; let private_key: EncString = require!(r.private_key.as_deref()).parse()?; - client - .internal - .initialize_user_crypto_master_key(master_key, user_key, private_key)?; + client.internal.initialize_user_crypto_master_key( + master_key, + user_key.into(), + private_key, + )?; } Ok(PasswordLoginResponse::process_response(response)) diff --git a/crates/bitwarden-core/src/auth/password/validate.rs b/crates/bitwarden-core/src/auth/password/validate.rs index 39abee276..5a3320874 100644 --- a/crates/bitwarden-core/src/auth/password/validate.rs +++ b/crates/bitwarden-core/src/auth/password/validate.rs @@ -1,4 +1,4 @@ -use bitwarden_crypto::{HashPurpose, MasterKey}; +use bitwarden_crypto::{HashPurpose, MasterKey, WrappedSymmetricKey}; use crate::{ auth::{password::determine_password_hash, AuthValidateError}, @@ -40,7 +40,7 @@ pub(crate) fn validate_password( pub(crate) fn validate_password_user_key( client: &Client, password: String, - encrypted_user_key: String, + encrypted_user_key: WrappedSymmetricKey, ) -> Result { use crate::key_management::SymmetricKeyId; @@ -56,7 +56,7 @@ pub(crate) fn validate_password_user_key( | UserLoginMethod::ApiKey { email, kdf, .. } => { let master_key = MasterKey::derive(&password, email, kdf)?; let user_key = master_key - .decrypt_user_key(encrypted_user_key.parse()?) + .decrypt_user_key(encrypted_user_key) .map_err(|_| WrongPasswordError)?; let key_store = client.internal.get_key_store(); @@ -80,7 +80,7 @@ pub(crate) fn validate_password_user_key( #[cfg(test)] mod tests { - use bitwarden_crypto::Kdf; + use bitwarden_crypto::{EncString, Kdf}; use crate::auth::password::{validate::validate_password_user_key, validate_password}; @@ -135,16 +135,16 @@ mod tests { let master_key = MasterKey::derive(password, email, &kdf).unwrap(); - let user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE="; + let user_key: EncString = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(); let private_key = "2.yN7l00BOlUE0Sb0M//Q53w==|EwKG/BduQRQ33Izqc/ogoBROIoI5dmgrxSo82sgzgAMIBt3A2FZ9vPRMY+GWT85JiqytDitGR3TqwnFUBhKUpRRAq4x7rA6A1arHrFp5Tp1p21O3SfjtvB3quiOKbqWk6ZaU1Np9HwqwAecddFcB0YyBEiRX3VwF2pgpAdiPbSMuvo2qIgyob0CUoC/h4Bz1be7Qa7B0Xw9/fMKkB1LpOm925lzqosyMQM62YpMGkjMsbZz0uPopu32fxzDWSPr+kekNNyLt9InGhTpxLmq1go/pXR2uw5dfpXc5yuta7DB0EGBwnQ8Vl5HPdDooqOTD9I1jE0mRyuBpWTTI3FRnu3JUh3rIyGBJhUmHqGZvw2CKdqHCIrQeQkkEYqOeJRJVdBjhv5KGJifqT3BFRwX/YFJIChAQpebNQKXe/0kPivWokHWwXlDB7S7mBZzhaAPidZvnuIhalE2qmTypDwHy22FyqV58T8MGGMchcASDi/QXI6kcdpJzPXSeU9o+NC68QDlOIrMVxKFeE7w7PvVmAaxEo0YwmuAzzKy9QpdlK0aab/xEi8V4iXj4hGepqAvHkXIQd+r3FNeiLfllkb61p6WTjr5urcmDQMR94/wYoilpG5OlybHdbhsYHvIzYoLrC7fzl630gcO6t4nM24vdB6Ymg9BVpEgKRAxSbE62Tqacxqnz9AcmgItb48NiR/He3n3ydGjPYuKk/ihZMgEwAEZvSlNxYONSbYrIGDtOY+8Nbt6KiH3l06wjZW8tcmFeVlWv+tWotnTY9IqlAfvNVTjtsobqtQnvsiDjdEVtNy/s2ci5TH+NdZluca2OVEr91Wayxh70kpM6ib4UGbfdmGgCo74gtKvKSJU0rTHakQ5L9JlaSDD5FamBRyI0qfL43Ad9qOUZ8DaffDCyuaVyuqk7cz9HwmEmvWU3VQ+5t06n/5kRDXttcw8w+3qClEEdGo1KeENcnXCB32dQe3tDTFpuAIMLqwXs6FhpawfZ5kPYvLPczGWaqftIs/RXJ/EltGc0ugw2dmTLpoQhCqrcKEBDoYVk0LDZKsnzitOGdi9mOWse7Se8798ib1UsHFUjGzISEt6upestxOeupSTOh0v4+AjXbDzRUyogHww3V+Bqg71bkcMxtB+WM+pn1XNbVTyl9NR040nhP7KEf6e9ruXAtmrBC2ah5cFEpLIot77VFZ9ilLuitSz+7T8n1yAh1IEG6xxXxninAZIzi2qGbH69O5RSpOJuJTv17zTLJQIIc781JwQ2TTwTGnx5wZLbffhCasowJKd2EVcyMJyhz6ru0PvXWJ4hUdkARJs3Xu8dus9a86N8Xk6aAPzBDqzYb1vyFIfBxP0oO8xFHgd30Cgmz8UrSE3qeWRrF8ftrI6xQnFjHBGWD/JWSvd6YMcQED0aVuQkuNW9ST/DzQThPzRfPUoiL10yAmV7Ytu4fR3x2sF0Yfi87YhHFuCMpV/DsqxmUizyiJuD938eRcH8hzR/VO53Qo3UIsqOLcyXtTv6THjSlTopQ+JOLOnHm1w8dzYbLN44OG44rRsbihMUQp+wUZ6bsI8rrOnm9WErzkbQFbrfAINdoCiNa6cimYIjvvnMTaFWNymqY1vZxGztQiMiHiHYwTfwHTXrb9j0uPM=|09J28iXv9oWzYtzK2LBT6Yht4IT4MijEkk0fwFdrVQ4=".parse().unwrap(); client .internal - .initialize_user_crypto_master_key(master_key, user_key.parse().unwrap(), private_key) + .initialize_user_crypto_master_key(master_key, user_key.clone().into(), private_key) .unwrap(); let result = - validate_password_user_key(&client, "asdfasdfasdf".to_owned(), user_key.to_string()) + validate_password_user_key(&client, "asdfasdfasdf".to_owned(), user_key.into()) .unwrap(); assert_eq!(result, "aOvkBXFhSdgrBWR3hZCMRoML9+h5yRblU3lFphCdkeA="); @@ -156,7 +156,7 @@ mod tests { fn test_validate_password_user_key_wrong_password() { use std::num::NonZeroU32; - use bitwarden_crypto::{Kdf, MasterKey}; + use bitwarden_crypto::{EncString, Kdf, MasterKey}; use crate::client::{Client, LoginMethod, UserLoginMethod}; @@ -178,16 +178,16 @@ mod tests { let master_key = MasterKey::derive(password, email, &kdf).unwrap(); - let user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE="; + let user_key: EncString = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(); let private_key = "2.yN7l00BOlUE0Sb0M//Q53w==|EwKG/BduQRQ33Izqc/ogoBROIoI5dmgrxSo82sgzgAMIBt3A2FZ9vPRMY+GWT85JiqytDitGR3TqwnFUBhKUpRRAq4x7rA6A1arHrFp5Tp1p21O3SfjtvB3quiOKbqWk6ZaU1Np9HwqwAecddFcB0YyBEiRX3VwF2pgpAdiPbSMuvo2qIgyob0CUoC/h4Bz1be7Qa7B0Xw9/fMKkB1LpOm925lzqosyMQM62YpMGkjMsbZz0uPopu32fxzDWSPr+kekNNyLt9InGhTpxLmq1go/pXR2uw5dfpXc5yuta7DB0EGBwnQ8Vl5HPdDooqOTD9I1jE0mRyuBpWTTI3FRnu3JUh3rIyGBJhUmHqGZvw2CKdqHCIrQeQkkEYqOeJRJVdBjhv5KGJifqT3BFRwX/YFJIChAQpebNQKXe/0kPivWokHWwXlDB7S7mBZzhaAPidZvnuIhalE2qmTypDwHy22FyqV58T8MGGMchcASDi/QXI6kcdpJzPXSeU9o+NC68QDlOIrMVxKFeE7w7PvVmAaxEo0YwmuAzzKy9QpdlK0aab/xEi8V4iXj4hGepqAvHkXIQd+r3FNeiLfllkb61p6WTjr5urcmDQMR94/wYoilpG5OlybHdbhsYHvIzYoLrC7fzl630gcO6t4nM24vdB6Ymg9BVpEgKRAxSbE62Tqacxqnz9AcmgItb48NiR/He3n3ydGjPYuKk/ihZMgEwAEZvSlNxYONSbYrIGDtOY+8Nbt6KiH3l06wjZW8tcmFeVlWv+tWotnTY9IqlAfvNVTjtsobqtQnvsiDjdEVtNy/s2ci5TH+NdZluca2OVEr91Wayxh70kpM6ib4UGbfdmGgCo74gtKvKSJU0rTHakQ5L9JlaSDD5FamBRyI0qfL43Ad9qOUZ8DaffDCyuaVyuqk7cz9HwmEmvWU3VQ+5t06n/5kRDXttcw8w+3qClEEdGo1KeENcnXCB32dQe3tDTFpuAIMLqwXs6FhpawfZ5kPYvLPczGWaqftIs/RXJ/EltGc0ugw2dmTLpoQhCqrcKEBDoYVk0LDZKsnzitOGdi9mOWse7Se8798ib1UsHFUjGzISEt6upestxOeupSTOh0v4+AjXbDzRUyogHww3V+Bqg71bkcMxtB+WM+pn1XNbVTyl9NR040nhP7KEf6e9ruXAtmrBC2ah5cFEpLIot77VFZ9ilLuitSz+7T8n1yAh1IEG6xxXxninAZIzi2qGbH69O5RSpOJuJTv17zTLJQIIc781JwQ2TTwTGnx5wZLbffhCasowJKd2EVcyMJyhz6ru0PvXWJ4hUdkARJs3Xu8dus9a86N8Xk6aAPzBDqzYb1vyFIfBxP0oO8xFHgd30Cgmz8UrSE3qeWRrF8ftrI6xQnFjHBGWD/JWSvd6YMcQED0aVuQkuNW9ST/DzQThPzRfPUoiL10yAmV7Ytu4fR3x2sF0Yfi87YhHFuCMpV/DsqxmUizyiJuD938eRcH8hzR/VO53Qo3UIsqOLcyXtTv6THjSlTopQ+JOLOnHm1w8dzYbLN44OG44rRsbihMUQp+wUZ6bsI8rrOnm9WErzkbQFbrfAINdoCiNa6cimYIjvvnMTaFWNymqY1vZxGztQiMiHiHYwTfwHTXrb9j0uPM=|09J28iXv9oWzYtzK2LBT6Yht4IT4MijEkk0fwFdrVQ4=".parse().unwrap(); client .internal - .initialize_user_crypto_master_key(master_key, user_key.parse().unwrap(), private_key) + .initialize_user_crypto_master_key(master_key, user_key.clone().into(), private_key) .unwrap(); let result = - validate_password_user_key(&client, "asdfasdfasdf".to_string(), user_key.to_string()) + validate_password_user_key(&client, "asdfasdfasdf".to_string(), user_key.into()) .unwrap(); assert_eq!(result, "aOvkBXFhSdgrBWR3hZCMRoML9+h5yRblU3lFphCdkeA="); diff --git a/crates/bitwarden-core/src/auth/pin.rs b/crates/bitwarden-core/src/auth/pin.rs index 93e172f25..b2aeb5489 100644 --- a/crates/bitwarden-core/src/auth/pin.rs +++ b/crates/bitwarden-core/src/auth/pin.rs @@ -1,4 +1,4 @@ -use bitwarden_crypto::{EncString, PinKey}; +use bitwarden_crypto::{PinKey, WrappedSymmetricKey}; use crate::{ auth::AuthValidateError, @@ -10,7 +10,7 @@ use crate::{ pub(crate) fn validate_pin( client: &Client, pin: String, - pin_protected_user_key: EncString, + pin_protected_user_key: WrappedSymmetricKey, ) -> Result { let login_method = client .internal @@ -46,7 +46,7 @@ pub(crate) fn validate_pin( mod tests { use std::num::NonZeroU32; - use bitwarden_crypto::{Kdf, MasterKey}; + use bitwarden_crypto::{EncString, Kdf, MasterKey}; use super::*; use crate::client::{Client, LoginMethod, UserLoginMethod}; @@ -70,12 +70,12 @@ mod tests { let master_key = MasterKey::derive(password, email, &kdf).unwrap(); - let user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE="; + let user_key: EncString = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(); let private_key = "2.yN7l00BOlUE0Sb0M//Q53w==|EwKG/BduQRQ33Izqc/ogoBROIoI5dmgrxSo82sgzgAMIBt3A2FZ9vPRMY+GWT85JiqytDitGR3TqwnFUBhKUpRRAq4x7rA6A1arHrFp5Tp1p21O3SfjtvB3quiOKbqWk6ZaU1Np9HwqwAecddFcB0YyBEiRX3VwF2pgpAdiPbSMuvo2qIgyob0CUoC/h4Bz1be7Qa7B0Xw9/fMKkB1LpOm925lzqosyMQM62YpMGkjMsbZz0uPopu32fxzDWSPr+kekNNyLt9InGhTpxLmq1go/pXR2uw5dfpXc5yuta7DB0EGBwnQ8Vl5HPdDooqOTD9I1jE0mRyuBpWTTI3FRnu3JUh3rIyGBJhUmHqGZvw2CKdqHCIrQeQkkEYqOeJRJVdBjhv5KGJifqT3BFRwX/YFJIChAQpebNQKXe/0kPivWokHWwXlDB7S7mBZzhaAPidZvnuIhalE2qmTypDwHy22FyqV58T8MGGMchcASDi/QXI6kcdpJzPXSeU9o+NC68QDlOIrMVxKFeE7w7PvVmAaxEo0YwmuAzzKy9QpdlK0aab/xEi8V4iXj4hGepqAvHkXIQd+r3FNeiLfllkb61p6WTjr5urcmDQMR94/wYoilpG5OlybHdbhsYHvIzYoLrC7fzl630gcO6t4nM24vdB6Ymg9BVpEgKRAxSbE62Tqacxqnz9AcmgItb48NiR/He3n3ydGjPYuKk/ihZMgEwAEZvSlNxYONSbYrIGDtOY+8Nbt6KiH3l06wjZW8tcmFeVlWv+tWotnTY9IqlAfvNVTjtsobqtQnvsiDjdEVtNy/s2ci5TH+NdZluca2OVEr91Wayxh70kpM6ib4UGbfdmGgCo74gtKvKSJU0rTHakQ5L9JlaSDD5FamBRyI0qfL43Ad9qOUZ8DaffDCyuaVyuqk7cz9HwmEmvWU3VQ+5t06n/5kRDXttcw8w+3qClEEdGo1KeENcnXCB32dQe3tDTFpuAIMLqwXs6FhpawfZ5kPYvLPczGWaqftIs/RXJ/EltGc0ugw2dmTLpoQhCqrcKEBDoYVk0LDZKsnzitOGdi9mOWse7Se8798ib1UsHFUjGzISEt6upestxOeupSTOh0v4+AjXbDzRUyogHww3V+Bqg71bkcMxtB+WM+pn1XNbVTyl9NR040nhP7KEf6e9ruXAtmrBC2ah5cFEpLIot77VFZ9ilLuitSz+7T8n1yAh1IEG6xxXxninAZIzi2qGbH69O5RSpOJuJTv17zTLJQIIc781JwQ2TTwTGnx5wZLbffhCasowJKd2EVcyMJyhz6ru0PvXWJ4hUdkARJs3Xu8dus9a86N8Xk6aAPzBDqzYb1vyFIfBxP0oO8xFHgd30Cgmz8UrSE3qeWRrF8ftrI6xQnFjHBGWD/JWSvd6YMcQED0aVuQkuNW9ST/DzQThPzRfPUoiL10yAmV7Ytu4fR3x2sF0Yfi87YhHFuCMpV/DsqxmUizyiJuD938eRcH8hzR/VO53Qo3UIsqOLcyXtTv6THjSlTopQ+JOLOnHm1w8dzYbLN44OG44rRsbihMUQp+wUZ6bsI8rrOnm9WErzkbQFbrfAINdoCiNa6cimYIjvvnMTaFWNymqY1vZxGztQiMiHiHYwTfwHTXrb9j0uPM=|09J28iXv9oWzYtzK2LBT6Yht4IT4MijEkk0fwFdrVQ4=".parse().unwrap(); client .internal - .initialize_user_crypto_master_key(master_key, user_key.parse().unwrap(), private_key) + .initialize_user_crypto_master_key(master_key, user_key.into(), private_key) .unwrap(); client @@ -84,22 +84,22 @@ mod tests { #[test] fn test_validate_valid_pin() { let pin = "1234".to_string(); - let pin_protected_user_key = "2.BXgvdBUeEMyvumqAJkAzPA==|JScDPoqOkVdrC1X755Ubt8tS9pC/thvrvNf5CyNcRg8HZtZ466EcRo7aCqwUzLyTVNRkbCYtFYT+09acGGHur8tGuS7Kmg/pYeaUo4K0UKI=|NpIFg5P9z0SN1MffbixD9OQE0l+NiNmnRQJs/kTsyoQ=" + let pin_protected_user_key: EncString = "2.BXgvdBUeEMyvumqAJkAzPA==|JScDPoqOkVdrC1X755Ubt8tS9pC/thvrvNf5CyNcRg8HZtZ466EcRo7aCqwUzLyTVNRkbCYtFYT+09acGGHur8tGuS7Kmg/pYeaUo4K0UKI=|NpIFg5P9z0SN1MffbixD9OQE0l+NiNmnRQJs/kTsyoQ=" .parse() .unwrap(); let client = init_client(); - assert!(validate_pin(&client, pin.clone(), pin_protected_user_key).unwrap()); + assert!(validate_pin(&client, pin.clone(), pin_protected_user_key.into()).unwrap()); } #[test] fn test_validate_invalid_pin() { let pin = "1234".to_string(); - let pin_protected_user_key = "2.BXgvdBUeEMyvumqAJkAyPA==|JScDPoqOkVdrC1X755Ubt8tS9pC/thvrvNf5CyNcRg8HZtZ466EcRo7aCqwUzLyTVNRkbCYtFYT+09acGGHur8tGuS7Kmg/pYeaUo4K0UKI=|NpIFg5P9z0SN1MffbixD9OQE0l+NiNmnRQJs/kTsyoQ=" + let pin_protected_user_key: EncString = "2.BXgvdBUeEMyvumqAJkAyPA==|JScDPoqOkVdrC1X755Ubt8tS9pC/thvrvNf5CyNcRg8HZtZ466EcRo7aCqwUzLyTVNRkbCYtFYT+09acGGHur8tGuS7Kmg/pYeaUo4K0UKI=|NpIFg5P9z0SN1MffbixD9OQE0l+NiNmnRQJs/kTsyoQ=" .parse() .unwrap(); let client = init_client(); - assert!(!validate_pin(&client, pin.clone(), pin_protected_user_key).unwrap()); + assert!(!validate_pin(&client, pin.clone(), pin_protected_user_key.into()).unwrap()); } } diff --git a/crates/bitwarden-core/src/auth/register.rs b/crates/bitwarden-core/src/auth/register.rs index 42d62ffeb..6cc3cbd21 100644 --- a/crates/bitwarden-core/src/auth/register.rs +++ b/crates/bitwarden-core/src/auth/register.rs @@ -76,7 +76,7 @@ pub(super) fn make_register_keys( Ok(RegisterKeyResponse { master_password_hash, - encrypted_user_key: encrypted_user_key.to_string(), + encrypted_user_key: encrypted_user_key.into_inner().to_string(), keys, }) } diff --git a/crates/bitwarden-core/src/client/internal.rs b/crates/bitwarden-core/src/client/internal.rs index 725d95d33..149356ad0 100644 --- a/crates/bitwarden-core/src/client/internal.rs +++ b/crates/bitwarden-core/src/client/internal.rs @@ -4,7 +4,7 @@ use bitwarden_crypto::KeyStore; #[cfg(any(feature = "internal", feature = "secrets"))] use bitwarden_crypto::SymmetricCryptoKey; #[cfg(feature = "internal")] -use bitwarden_crypto::{EncString, Kdf, MasterKey, PinKey, UnsignedSharedKey}; +use bitwarden_crypto::{EncString, Kdf, MasterKey, PinKey, UnsignedSharedKey, WrappedSymmetricKey}; use chrono::Utc; use uuid::Uuid; @@ -176,7 +176,7 @@ impl InternalClient { pub(crate) fn initialize_user_crypto_master_key( &self, master_key: MasterKey, - user_key: EncString, + user_key: WrappedSymmetricKey, private_key: EncString, ) -> Result<(), EncryptionSettingsError> { let user_key = master_key.decrypt_user_key(user_key)?; @@ -200,7 +200,7 @@ impl InternalClient { pub(crate) fn initialize_user_crypto_pin( &self, pin_key: PinKey, - pin_protected_user_key: EncString, + pin_protected_user_key: WrappedSymmetricKey, private_key: EncString, ) -> Result<(), EncryptionSettingsError> { let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?; diff --git a/crates/bitwarden-core/src/mobile/crypto.rs b/crates/bitwarden-core/src/mobile/crypto.rs index 6e2905ce8..8a149cdbd 100644 --- a/crates/bitwarden-core/src/mobile/crypto.rs +++ b/crates/bitwarden-core/src/mobile/crypto.rs @@ -9,7 +9,7 @@ use std::collections::HashMap; use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_crypto::{ AsymmetricCryptoKey, CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey, - SymmetricCryptoKey, UnsignedSharedKey, UserKey, + SymmetricCryptoKey, UnsignedSharedKey, UserKey, WrappedSymmetricKey, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -134,6 +134,7 @@ pub async fn initialize_user_crypto( match req.method { InitUserCryptoMethod::Password { password, user_key } => { let user_key: EncString = user_key.parse()?; + let user_key: WrappedSymmetricKey = user_key.into(); let master_key = MasterKey::derive(&password, &req.email, &req.kdf_params)?; client @@ -153,7 +154,7 @@ pub async fn initialize_user_crypto( let pin_key = PinKey::derive(pin.as_bytes(), req.email.as_bytes(), &req.kdf_params)?; client.internal.initialize_user_crypto_pin( pin_key, - pin_protected_user_key, + pin_protected_user_key.into(), private_key, )?; } @@ -201,9 +202,11 @@ pub async fn initialize_user_crypto( let master_key = MasterKey::try_from(master_key_bytes.as_mut_slice())?; let user_key: EncString = user_key.parse()?; - client - .internal - .initialize_user_crypto_master_key(master_key, user_key, private_key)?; + client.internal.initialize_user_crypto_master_key( + master_key, + user_key.into(), + private_key, + )?; } } @@ -295,7 +298,7 @@ pub(super) fn update_password( Ok(UpdatePasswordResponse { password_hash, - new_key, + new_key: new_key.into_inner(), }) } @@ -368,7 +371,7 @@ fn derive_pin_protected_user_key( LoginMethod::ServiceAccount(_) => return Err(NotAuthenticatedError)?, }; - Ok(derived_key.encrypt_user_key(user_key)?) + Ok(derived_key.encrypt_user_key(user_key)?.into_inner()) } #[allow(missing_docs)] @@ -430,7 +433,7 @@ pub(super) fn derive_key_connector( ) -> Result { let master_key = MasterKey::derive(&request.password, &request.email, &request.kdf)?; master_key - .decrypt_user_key(request.user_key_encrypted) + .decrypt_user_key(request.user_key_encrypted.into()) .map_err(|_| WrongPasswordError)?; Ok(master_key.to_base64()) @@ -752,11 +755,11 @@ mod tests { ) .unwrap(); - let user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(); + let user_key: EncString = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(); let private_key ="2.yN7l00BOlUE0Sb0M//Q53w==|EwKG/BduQRQ33Izqc/ogoBROIoI5dmgrxSo82sgzgAMIBt3A2FZ9vPRMY+GWT85JiqytDitGR3TqwnFUBhKUpRRAq4x7rA6A1arHrFp5Tp1p21O3SfjtvB3quiOKbqWk6ZaU1Np9HwqwAecddFcB0YyBEiRX3VwF2pgpAdiPbSMuvo2qIgyob0CUoC/h4Bz1be7Qa7B0Xw9/fMKkB1LpOm925lzqosyMQM62YpMGkjMsbZz0uPopu32fxzDWSPr+kekNNyLt9InGhTpxLmq1go/pXR2uw5dfpXc5yuta7DB0EGBwnQ8Vl5HPdDooqOTD9I1jE0mRyuBpWTTI3FRnu3JUh3rIyGBJhUmHqGZvw2CKdqHCIrQeQkkEYqOeJRJVdBjhv5KGJifqT3BFRwX/YFJIChAQpebNQKXe/0kPivWokHWwXlDB7S7mBZzhaAPidZvnuIhalE2qmTypDwHy22FyqV58T8MGGMchcASDi/QXI6kcdpJzPXSeU9o+NC68QDlOIrMVxKFeE7w7PvVmAaxEo0YwmuAzzKy9QpdlK0aab/xEi8V4iXj4hGepqAvHkXIQd+r3FNeiLfllkb61p6WTjr5urcmDQMR94/wYoilpG5OlybHdbhsYHvIzYoLrC7fzl630gcO6t4nM24vdB6Ymg9BVpEgKRAxSbE62Tqacxqnz9AcmgItb48NiR/He3n3ydGjPYuKk/ihZMgEwAEZvSlNxYONSbYrIGDtOY+8Nbt6KiH3l06wjZW8tcmFeVlWv+tWotnTY9IqlAfvNVTjtsobqtQnvsiDjdEVtNy/s2ci5TH+NdZluca2OVEr91Wayxh70kpM6ib4UGbfdmGgCo74gtKvKSJU0rTHakQ5L9JlaSDD5FamBRyI0qfL43Ad9qOUZ8DaffDCyuaVyuqk7cz9HwmEmvWU3VQ+5t06n/5kRDXttcw8w+3qClEEdGo1KeENcnXCB32dQe3tDTFpuAIMLqwXs6FhpawfZ5kPYvLPczGWaqftIs/RXJ/EltGc0ugw2dmTLpoQhCqrcKEBDoYVk0LDZKsnzitOGdi9mOWse7Se8798ib1UsHFUjGzISEt6upestxOeupSTOh0v4+AjXbDzRUyogHww3V+Bqg71bkcMxtB+WM+pn1XNbVTyl9NR040nhP7KEf6e9ruXAtmrBC2ah5cFEpLIot77VFZ9ilLuitSz+7T8n1yAh1IEG6xxXxninAZIzi2qGbH69O5RSpOJuJTv17zTLJQIIc781JwQ2TTwTGnx5wZLbffhCasowJKd2EVcyMJyhz6ru0PvXWJ4hUdkARJs3Xu8dus9a86N8Xk6aAPzBDqzYb1vyFIfBxP0oO8xFHgd30Cgmz8UrSE3qeWRrF8ftrI6xQnFjHBGWD/JWSvd6YMcQED0aVuQkuNW9ST/DzQThPzRfPUoiL10yAmV7Ytu4fR3x2sF0Yfi87YhHFuCMpV/DsqxmUizyiJuD938eRcH8hzR/VO53Qo3UIsqOLcyXtTv6THjSlTopQ+JOLOnHm1w8dzYbLN44OG44rRsbihMUQp+wUZ6bsI8rrOnm9WErzkbQFbrfAINdoCiNa6cimYIjvvnMTaFWNymqY1vZxGztQiMiHiHYwTfwHTXrb9j0uPM=|09J28iXv9oWzYtzK2LBT6Yht4IT4MijEkk0fwFdrVQ4=".parse().unwrap(); client .internal - .initialize_user_crypto_master_key(master_key, user_key, private_key) + .initialize_user_crypto_master_key(master_key, user_key.into(), private_key) .unwrap(); let public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsy7RFHcX3C8Q4/OMmhhbFReYWfB45W9PDTEA8tUZwZmtOiN2RErIS2M1c+K/4HoDJ/TjpbX1f2MZcr4nWvKFuqnZXyewFc+jmvKVewYi+NAu2++vqKq2kKcmMNhwoQDQdQIVy/Uqlp4Cpi2cIwO6ogq5nHNJGR3jm+CpyrafYlbz1bPvL3hbyoGDuG2tgADhyhXUdFuef2oF3wMvn1lAJAvJnPYpMiXUFmj1ejmbwtlxZDrHgUJvUcp7nYdwUKaFoi+sOttHn3u7eZPtNvxMjhSS/X/1xBIzP/mKNLdywH5LoRxniokUk+fV3PYUxJsiU3lV0Trc/tH46jqd8ZGjmwIDAQAB"; diff --git a/crates/bitwarden-core/src/platform/generate_fingerprint.rs b/crates/bitwarden-core/src/platform/generate_fingerprint.rs index 21a16e976..9d16e2f78 100644 --- a/crates/bitwarden-core/src/platform/generate_fingerprint.rs +++ b/crates/bitwarden-core/src/platform/generate_fingerprint.rs @@ -79,14 +79,14 @@ pub(crate) fn generate_user_fingerprint( mod tests { use std::num::NonZeroU32; - use bitwarden_crypto::{Kdf, MasterKey}; + use bitwarden_crypto::{EncString, Kdf, MasterKey}; use super::*; use crate::Client; #[test] fn test_generate_user_fingerprint() { - let user_key = "2.oZg5RYpU2HjUAKI1DUQCkg==|PyRzI9kZpt66P2OedH8CHOeU0/lgKLkhIJiKDijdyFqIemBSIBoslhfQh/P1TK9xgZp0smgD6+5+yNbZfOpBaCVrsT3WWAO78xOWizduRe4=|xfDLDZSJ+yZAdh388flVg7SMDBJuMs0+CHTjutKs4uQ="; + let user_key: EncString = "2.oZg5RYpU2HjUAKI1DUQCkg==|PyRzI9kZpt66P2OedH8CHOeU0/lgKLkhIJiKDijdyFqIemBSIBoslhfQh/P1TK9xgZp0smgD6+5+yNbZfOpBaCVrsT3WWAO78xOWizduRe4=|xfDLDZSJ+yZAdh388flVg7SMDBJuMs0+CHTjutKs4uQ=".parse().unwrap(); let private_key = "2.tY6WsWKUbBwNU8wROuipiQ==|DNFL1d19xVojUKTTy2gxT+9J1VXbMQLcbMnx1HSeA6U3yZhsLR6DPaGibb3Bp8doIHtrsxzL/JeLb4gLDZ8RnDhFfE4iLRaPakX14kbBXrKH9/uW/zc7TqIVciWhI1PaeFlu8wnVuGt3e5Ysx6Y7Uw7RS8pRT5aE3sX3aDPGZTAdTutLn1VUfkShS5OK5HJl9CdiwV2wOcrf4w/WqtaNUUqGdsJ8C4ELlpBzHxqs+lEm+8pGPYmuGQIjVc0eOR9Tza9GTk3ih1XGc1znOCoKUZbtA29RfbwfmJy/yGi/3RLWZFQGCCij4cLC5OpldiX4JWL5Dhox44p/5IVF3rfxTVz3GCyDOoHevRG/06sUBq6nhbdCQf3lJvxwcQJhoQg4rsapM3rgol+u+TbXRiwWPbfswuLkRlvGFKtKUWMa4S57gj0CFYgSBPdTyhZTB44D7JQ2bd901Ur1dYWcDe4Kn3ZawpxL0cX2ZPlE3v8FXFJf2s8DJytL8yu73GasDzVmaGHxueWWVz7EHjh+pmB4oaAHARcY8d3LActAyl/+bcFRPYQJ68ae6DJhYYJGHIBWMImf2BifGgUX8vUFfUAYjne3D82lRyZQHs3xbl+ZxEPgWiPYRWUtxGXLLP4f9mbl+LeJdehtHNjC8kOduBL0CsP4gmugzNNUXI+Izc/9svno6kFr6SU0LA3MGrOU8ao7UCQbf/Pj/RKnG1gRmBDQqf7IMm6jOyTwdde9NpfQb32iH11PkuAKBvEtUuq9BeAKWjoZku+ycsN2jZH0hzd/QrU2c+E4+yHwX3wSxxorNOXt5EZkJbEDBlpRyE1zWoyy0wIYfcChYLvFN8QFHchlw5wmHxL+OOgdgndAtV/2DCx+NB6caY31qLictME+1GPPlQ7QvicMLgmpSWq83rs4ex/My6p3hCRSrJJiLvjEDZLYWKHHLd5tsPRAjX8ADNWB1VeIeiJrj1wpOCc1PbWpbljbbTsBmVPo6iKm/UDGAHBdQ//0j3FQg8f5w/j+McsoaMpDNHNTiLvjWERR+RBmsEA0lEL00wZz/DHlzOAYHLYYqFMT7GBCQD+Wk/l1TL+X2agUy7Irlk7QbZ4ivfdNIpSW8Ct9MGE6o4wV+nIpXURojgBBTcP85RTBLXXGrIprnK1G/VE8ONag3+nkqIyChjYyk5QMsxqOqSHsbiOxhCdXypbCbY4g9yKJtBJ/ADjxmELj0X7pqsTFqC0eRT7rk9qTBcYBBu6rwlAfq8AKjDB7WjNjzLaMi6lBoe4petBn1xcLkXD5hHra0TULxcYrq8MIb+Vk4CBZZdwwyVm/28SwSjHBIBpRysPAonDDsp3KlahwXEFvRDQR/oFww172GI7cx8SoPn93Qh0JfpTAAowsO3meR8bzUSyd7v3rmtaBPsWHE9zUXye/6nloMU5joEcD6uJaxd0kdaWWIoKLH++zHW1R776wJrS6u+TIWZgHqiIJoCd9fV25BnQcbZRKd6mnfNQkchJ6c6ozXKrFaa8DLdERdfh84+isw5mzW2zMJwHEwtKt6LUTyieC2exzPAwPxJT1+IMjuzuwiLnvGKOq+kwE/LWBSB0ZfGuCP/3jMM8OCfe7Hbpt1TfXcUxUzj6sSjkjQB6qBt+TINRdOFA=|fppguME86utsAOKrBYn6XU95q7daVbZ+3dD9OVkQlAw="; let fingerprint_material = "a09726a0-9590-49d1-a5f5-afe300b6a515"; @@ -105,7 +105,7 @@ mod tests { .internal .initialize_user_crypto_master_key( master_key, - user_key.parse().unwrap(), + user_key.into(), private_key.parse().unwrap(), ) .unwrap(); diff --git a/crates/bitwarden-core/src/uniffi_support.rs b/crates/bitwarden-core/src/uniffi_support.rs index 87676449e..00682408b 100644 --- a/crates/bitwarden-core/src/uniffi_support.rs +++ b/crates/bitwarden-core/src/uniffi_support.rs @@ -2,13 +2,18 @@ use std::num::NonZeroU32; -use bitwarden_crypto::{EncString, UnsignedSharedKey}; +use bitwarden_crypto::{EncString, UnsignedSharedKey, WrappedSymmetricKey}; use uuid::Uuid; use crate::UniffiCustomTypeConverter; uniffi::ffi_converter_forward!(NonZeroU32, bitwarden_crypto::UniFfiTag, crate::UniFfiTag); uniffi::ffi_converter_forward!(EncString, bitwarden_crypto::UniFfiTag, crate::UniFfiTag); +uniffi::ffi_converter_forward!( + WrappedSymmetricKey, + bitwarden_crypto::UniFfiTag, + crate::UniFfiTag +); uniffi::ffi_converter_forward!( UnsignedSharedKey, bitwarden_crypto::UniFfiTag, diff --git a/crates/bitwarden-crypto/src/keys/master_key.rs b/crates/bitwarden-crypto/src/keys/master_key.rs index f0e32934b..3f65ce8fa 100644 --- a/crates/bitwarden-crypto/src/keys/master_key.rs +++ b/crates/bitwarden-crypto/src/keys/master_key.rs @@ -12,7 +12,7 @@ use super::{ }; use crate::{ util::{self}, - CryptoError, EncString, KeyDecryptable, Result, SymmetricCryptoKey, UserKey, + CryptoError, EncString, Result, SymmetricCryptoKey, UserKey, WrappedSymmetricKey, }; #[derive(Copy, Clone)] @@ -66,17 +66,17 @@ impl MasterKey { } /// Generate a new random user key and encrypt it with the master key. - pub fn make_user_key(&self) -> Result<(UserKey, EncString)> { + pub fn make_user_key(&self) -> Result<(UserKey, WrappedSymmetricKey)> { make_user_key(rand::thread_rng(), self) } /// Encrypt the users user key - pub fn encrypt_user_key(&self, user_key: &SymmetricCryptoKey) -> Result { + pub fn encrypt_user_key(&self, user_key: &SymmetricCryptoKey) -> Result { encrypt_user_key(self.inner_bytes(), user_key) } /// Decrypt the users user key - pub fn decrypt_user_key(&self, user_key: EncString) -> Result { + pub fn decrypt_user_key(&self, user_key: WrappedSymmetricKey) -> Result { decrypt_user_key(self.inner_bytes(), user_key) } @@ -124,18 +124,19 @@ impl TryFrom<&SymmetricCryptoKey> for MasterKey { pub(super) fn encrypt_user_key( master_key: &Pin>>, user_key: &SymmetricCryptoKey, -) -> Result { +) -> Result { let stretched_master_key = stretch_key(master_key)?; let user_key_bytes = Zeroizing::new(user_key.to_encoded()); EncString::encrypt_aes256_hmac(&user_key_bytes, &stretched_master_key) + .map(WrappedSymmetricKey::from) } /// Helper function to decrypt a user key with a master or pin key or key-connector-key. pub(super) fn decrypt_user_key( key: &Pin>>, - user_key: EncString, + user_key: WrappedSymmetricKey, ) -> Result { - let mut dec: Vec = match user_key { + match user_key.as_inner() { // Legacy. user_keys were encrypted using `Aes256Cbc_B64` a long time ago. We've since // moved to using `Aes256Cbc_HmacSha256_B64`. However, we still need to support // decrypting these old keys. @@ -143,20 +144,16 @@ pub(super) fn decrypt_user_key( let legacy_key = SymmetricCryptoKey::Aes256CbcKey(super::Aes256CbcKey { enc_key: Box::pin(GenericArray::clone_from_slice(key)), }); - user_key.decrypt_with_key(&legacy_key)? + user_key.unwrap_with(&legacy_key) } EncString::Aes256Cbc_HmacSha256_B64 { .. } => { let stretched_key = SymmetricCryptoKey::Aes256CbcHmacKey(stretch_key(key)?); - user_key.decrypt_with_key(&stretched_key)? + user_key.unwrap_with(&stretched_key) } - EncString::Cose_Encrypt0_B64 { .. } => { - return Err(CryptoError::OperationNotSupported( - crate::error::UnsupportedOperation::EncryptionNotImplementedForKey, - )); - } - }; - - SymmetricCryptoKey::try_from(dec.as_mut_slice()) + EncString::Cose_Encrypt0_B64 { .. } => Err(CryptoError::OperationNotSupported( + crate::error::UnsupportedOperation::EncryptionNotImplementedForKey, + )), + } } /// Generate a new random user key and encrypt it with the master key. @@ -168,7 +165,7 @@ pub(super) fn decrypt_user_key( fn make_user_key( rng: impl rand::RngCore + rand::CryptoRng, master_key: &MasterKey, -) -> Result<(UserKey, EncString)> { +) -> Result<(UserKey, WrappedSymmetricKey)> { let user_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key_internal(rng); let protected = master_key.encrypt_user_key(&user_key)?; Ok((UserKey::new(user_key), protected)) @@ -303,7 +300,7 @@ mod tests { let user_key: EncString = "0.8UClLa8IPE1iZT7chy5wzQ==|6PVfHnVk5S3XqEtQemnM5yb4JodxmPkkWzmDRdfyHtjORmvxqlLX40tBJZ+CKxQWmS8tpEB5w39rbgHg/gqs0haGdZG4cPbywsgGzxZ7uNI=".parse().unwrap(); - let decrypted = master_key.decrypt_user_key(user_key).unwrap(); + let decrypted = master_key.decrypt_user_key(user_key.into()).unwrap(); let SymmetricCryptoKey::Aes256CbcHmacKey(decrypted) = &decrypted else { panic!("Decrypted key is not an Aes256CbcHmacKey"); }; diff --git a/crates/bitwarden-crypto/src/keys/pin_key.rs b/crates/bitwarden-crypto/src/keys/pin_key.rs index e069a4c62..eea23acd8 100644 --- a/crates/bitwarden-crypto/src/keys/pin_key.rs +++ b/crates/bitwarden-crypto/src/keys/pin_key.rs @@ -5,6 +5,7 @@ use super::{ }; use crate::{ keys::key_encryptable::CryptoKey, EncString, KeyEncryptable, Result, SymmetricCryptoKey, + WrappedSymmetricKey, }; /// Pin Key. @@ -19,12 +20,12 @@ impl PinKey { } /// Encrypt the users user key - pub fn encrypt_user_key(&self, user_key: &SymmetricCryptoKey) -> Result { + pub fn encrypt_user_key(&self, user_key: &SymmetricCryptoKey) -> Result { encrypt_user_key(&self.0 .0, user_key) } /// Decrypt the users user key - pub fn decrypt_user_key(&self, user_key: EncString) -> Result { + pub fn decrypt_user_key(&self, user_key: WrappedSymmetricKey) -> Result { decrypt_user_key(&self.0 .0, user_key) } } diff --git a/crates/bitwarden-crypto/src/lib.rs b/crates/bitwarden-crypto/src/lib.rs index d3a0e304d..f7020b4dd 100644 --- a/crates/bitwarden-crypto/src/lib.rs +++ b/crates/bitwarden-crypto/src/lib.rs @@ -15,6 +15,8 @@ static ALLOC: ZeroizingAllocator = ZeroizingAllocator(std::a mod aes; mod enc_string; pub use enc_string::{EncString, UnsignedSharedKey}; +mod safe; +pub use safe::WrappedSymmetricKey; mod error; pub use error::CryptoError; pub(crate) use error::Result; diff --git a/crates/bitwarden-crypto/src/safe/key_wrap.rs b/crates/bitwarden-crypto/src/safe/key_wrap.rs new file mode 100644 index 000000000..d4f13a00c --- /dev/null +++ b/crates/bitwarden-crypto/src/safe/key_wrap.rs @@ -0,0 +1,143 @@ +use std::str::FromStr; + +use serde::{Deserialize, Serialize}; + +use crate::{ + error::UnsupportedOperation, CryptoError, EncString, KeyDecryptable, KeyEncryptable, + SymmetricCryptoKey, +}; + +#[cfg(feature = "wasm")] +#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)] +const TS_CUSTOM_TYPES: &'static str = r#" +export type WrappedSymmetricKey = string; +"#; + +/// A wrapped symmetric key is an an [EncString], created where a wrapping key is used to encrypt a +/// key_to_wrap. +/// +/// Wrapped keys such as cipher keys, or attachment keys, are used to create a layer of indirection, +/// so that keys can be shared mor granularly, and so that data can be rotated more easily. +#[derive(Clone, PartialEq)] +pub struct WrappedSymmetricKey(EncString); + +impl From for WrappedSymmetricKey { + fn from(enc_string: EncString) -> Self { + WrappedSymmetricKey(enc_string) + } +} + +impl Serialize for WrappedSymmetricKey { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.0.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for WrappedSymmetricKey { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + EncString::deserialize(deserializer).map(WrappedSymmetricKey) + } +} + +impl std::fmt::Debug for WrappedSymmetricKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("WrappedSymmetricKey") + .field("inner_enc_string", &self.as_inner()) + .finish() + } +} + +impl schemars::JsonSchema for WrappedSymmetricKey { + fn schema_name() -> String { + "WrappedSymmetricKey".to_string() + } + + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + EncString::json_schema(gen) + } +} + +impl WrappedSymmetricKey { + pub fn into_inner(self) -> EncString { + self.0 + } + + pub fn as_inner(&self) -> &EncString { + &self.0 + } + + pub fn try_from_optional(s: Option) -> Result, CryptoError> { + EncString::try_from_optional(s).map(|enc_string| enc_string.map(WrappedSymmetricKey)) + } +} + +impl FromStr for WrappedSymmetricKey { + type Err = CryptoError; + + fn from_str(s: &str) -> Result { + EncString::from_str(s).map(WrappedSymmetricKey) + } +} + +impl WrappedSymmetricKey { + /// Unwraps a the wrapped symmetric key using the provided wrapping key, returning the contained + /// wrapped key. + pub fn unwrap_with( + &self, + wrapping_key: &SymmetricCryptoKey, + ) -> Result { + let decrypted_bytes: Vec = self.0.decrypt_with_key(wrapping_key)?; + SymmetricCryptoKey::try_from(decrypted_bytes) + } +} + +impl SymmetricCryptoKey { + /// Wraps (encrypts) the key using the provided wrapping key. + /// + /// Use this if you have a symmetric crypto key that should protect another symmetric crypto + /// key. + pub fn wrap_with( + &self, + wrapping_key: &SymmetricCryptoKey, + ) -> Result { + use crate::SymmetricCryptoKey::*; + + // `Aes256CbcHmacKey` can wrap keys by encrypting their byte serialization obtained using + // `SymmetricCryptoKey::to_encoded()`. `XChaCha20Poly1305Key` need to specify the + // content format to be either octet stream, in case the wrapped key is a Aes256CbcHmacKey + // or `Aes256CbcKey`, or by specifying the content format to be CoseKey, in case the + // wrapped key is a `XChaCha20Poly1305Key`. + match (wrapping_key, self) { + (Aes256CbcHmacKey(_), Aes256CbcHmacKey(_) | Aes256CbcKey(_)) => { + let encoded = self.to_encoded(); + let enc_string = encoded.encrypt_with_key(wrapping_key)?; + Ok(WrappedSymmetricKey(enc_string)) + } + _ => Err(CryptoError::OperationNotSupported( + UnsupportedOperation::EncryptionNotImplementedForKey, + )), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_wrap_unwrap() { + let wrapping_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); + let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key(); + + let wrapped_key = key.wrap_with(&wrapping_key).unwrap(); + let unwrapped_key = wrapped_key.unwrap_with(&wrapping_key).unwrap(); + + assert_eq!(key, unwrapped_key); + } +} diff --git a/crates/bitwarden-crypto/src/safe/mod.rs b/crates/bitwarden-crypto/src/safe/mod.rs new file mode 100644 index 000000000..06b46ae4b --- /dev/null +++ b/crates/bitwarden-crypto/src/safe/mod.rs @@ -0,0 +1,4 @@ +//! This module contains safe cryptographic building blocks that should be used +//! first and foremost, before any other functions from the bitwarden-crypto crate. +mod key_wrap; +pub use key_wrap::*; diff --git a/crates/bitwarden-crypto/src/store/context.rs b/crates/bitwarden-crypto/src/store/context.rs index 261067af8..f28b3c735 100644 --- a/crates/bitwarden-crypto/src/store/context.rs +++ b/crates/bitwarden-crypto/src/store/context.rs @@ -9,7 +9,7 @@ use super::KeyStoreInner; use crate::{ derive_shareable_key, error::UnsupportedOperation, store::backend::StoreBackend, AsymmetricCryptoKey, CryptoError, EncString, KeyId, KeyIds, Result, SymmetricCryptoKey, - UnsignedSharedKey, + UnsignedSharedKey, WrappedSymmetricKey, }; /// The context of a crypto operation using [super::KeyStore] @@ -42,7 +42,7 @@ use crate::{ /// # pub Ids => SymmKeyId, AsymmKeyId; /// # } /// struct Data { -/// key: EncString, +/// key: WrappedSymmetricKey, /// name: String, /// } /// # impl IdentifyKey for Data { @@ -127,29 +127,26 @@ impl KeyStoreContext<'_, Ids> { // TODO: All these encrypt x key with x key look like they need to be made generic, // but I haven't found the best way to do that yet. - /// Decrypt a symmetric key into the context by using an already existing symmetric key + /// Unwrap a symmetric key into the context by using an already existing symmetric key /// /// # Arguments /// - /// * `encryption_key` - The key id used to decrypt the `encrypted_key`. It must already exist - /// in the context + /// * `wrapping_key` - The key id used to unwrap the `key_to_unwrap`. It must already exist in + /// the context /// * `new_key_id` - The key id where the decrypted key will be stored. If it already exists, it /// will be overwritten - /// * `encrypted_key` - The key to decrypt + /// * `key_to_unwrap` - The key to decrypt pub fn unwrap_symmetric_key( &mut self, - encryption_key: Ids::Symmetric, + wrapping_key: Ids::Symmetric, new_key_id: Ids::Symmetric, - encrypted_key: &EncString, + key_to_unwrap: &WrappedSymmetricKey, ) -> Result { - let mut new_key_material = - self.decrypt_data_with_symmetric_key(encryption_key, encrypted_key)?; + let wrapping_key = self.get_symmetric_key(wrapping_key)?; + let unwrapped_key = key_to_unwrap.unwrap_with(wrapping_key)?; #[allow(deprecated)] - self.set_symmetric_key( - new_key_id, - SymmetricCryptoKey::try_from(new_key_material.as_mut_slice())?, - )?; + self.set_symmetric_key(new_key_id, unwrapped_key)?; // Returning the new key identifier for convenience Ok(new_key_id) @@ -167,26 +164,10 @@ impl KeyStoreContext<'_, Ids> { &self, wrapping_key: Ids::Symmetric, key_to_wrap: Ids::Symmetric, - ) -> Result { - use SymmetricCryptoKey::*; - + ) -> Result { let wrapping_key_instance = self.get_symmetric_key(wrapping_key)?; let key_to_wrap_instance = self.get_symmetric_key(key_to_wrap)?; - // `Aes256CbcHmacKey` can wrap keys by encrypting their byte serialization obtained using - // `SymmetricCryptoKey::to_encoded()`. `XChaCha20Poly1305Key` need to specify the - // content format to be either octet stream, in case the wrapped key is a Aes256CbcHmacKey - // or `Aes256CbcKey`, or by specifying the content format to be CoseKey, in case the - // wrapped key is a `XChaCha20Poly1305Key`. - match (wrapping_key_instance, key_to_wrap_instance) { - (Aes256CbcHmacKey(_), Aes256CbcHmacKey(_) | Aes256CbcKey(_)) => self - .encrypt_data_with_symmetric_key( - wrapping_key, - key_to_wrap_instance.to_encoded().as_slice(), - ), - _ => Err(CryptoError::OperationNotSupported( - UnsupportedOperation::EncryptionNotImplementedForKey, - )), - } + key_to_wrap_instance.wrap_with(wrapping_key_instance) } /// Decapsulate a symmetric key into the context by using an already existing asymmetric key diff --git a/crates/bitwarden-crypto/src/uniffi_support.rs b/crates/bitwarden-crypto/src/uniffi_support.rs index 3e9869cb9..27f4c4b99 100644 --- a/crates/bitwarden-crypto/src/uniffi_support.rs +++ b/crates/bitwarden-crypto/src/uniffi_support.rs @@ -1,6 +1,8 @@ use std::{num::NonZeroU32, str::FromStr}; -use crate::{CryptoError, EncString, UniffiCustomTypeConverter, UnsignedSharedKey}; +use crate::{ + CryptoError, EncString, UniffiCustomTypeConverter, UnsignedSharedKey, WrappedSymmetricKey, +}; uniffi::custom_type!(NonZeroU32, u32); @@ -30,6 +32,20 @@ impl UniffiCustomTypeConverter for EncString { } } +uniffi::custom_type!(WrappedSymmetricKey, String); +impl UniffiCustomTypeConverter for WrappedSymmetricKey { + type Builtin = String; + + fn into_custom(val: Self::Builtin) -> uniffi::Result { + let enc_string: EncString = val.parse()?; + Ok(WrappedSymmetricKey::from(enc_string)) + } + + fn from_custom(obj: Self) -> Self::Builtin { + obj.as_inner().to_string() + } +} + uniffi::custom_type!(UnsignedSharedKey, String); impl UniffiCustomTypeConverter for UnsignedSharedKey { @@ -43,3 +59,11 @@ impl UniffiCustomTypeConverter for UnsignedSharedKey { obj.to_string() } } + +// Uniffi doesn't emit unused types, this is a dummy record to ensure that the custom type +// converters are emitted +#[allow(dead_code)] +#[derive(uniffi::Record)] +struct CryptoUniffiConverterDummyRecord { + wrapped_symmetric_key: WrappedSymmetricKey, +} diff --git a/crates/bitwarden-uniffi/src/auth/mod.rs b/crates/bitwarden-uniffi/src/auth/mod.rs index 5f90b1673..9ee367792 100644 --- a/crates/bitwarden-uniffi/src/auth/mod.rs +++ b/crates/bitwarden-uniffi/src/auth/mod.rs @@ -110,10 +110,13 @@ impl AuthClient { password: String, encrypted_user_key: String, ) -> Result { + let encrypted_user_key = encrypted_user_key + .parse::() + .map_err(Error::Crypto)?; Ok(self .0 .auth() - .validate_password_user_key(password, encrypted_user_key) + .validate_password_user_key(password, encrypted_user_key.into()) .map_err(Error::AuthValidate)?) } @@ -128,7 +131,7 @@ impl AuthClient { Ok(self .0 .auth() - .validate_pin(pin, pin_protected_user_key) + .validate_pin(pin, pin_protected_user_key.into()) .map_err(Error::AuthValidate)?) } diff --git a/crates/bitwarden-vault/src/cipher/attachment.rs b/crates/bitwarden-vault/src/cipher/attachment.rs index 5d997f631..32c1693ac 100644 --- a/crates/bitwarden-vault/src/cipher/attachment.rs +++ b/crates/bitwarden-vault/src/cipher/attachment.rs @@ -1,6 +1,7 @@ use bitwarden_core::key_management::{KeyIds, SymmetricKeyId}; use bitwarden_crypto::{ CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, + WrappedSymmetricKey, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "wasm")] @@ -20,7 +21,7 @@ pub struct Attachment { /// Readable size, ex: "4.2 KB" or "1.43 GB" pub size_name: Option, pub file_name: Option, - pub key: Option, + pub key: Option, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -33,7 +34,7 @@ pub struct AttachmentView { pub size: Option, pub size_name: Option, pub file_name: Option, - pub key: Option, + pub key: Option, } #[derive(Serialize, Deserialize, Debug)] @@ -178,7 +179,7 @@ impl TryFrom for Attachment size: attachment.size, size_name: attachment.size_name, file_name: EncString::try_from_optional(attachment.file_name)?, - key: EncString::try_from_optional(attachment.key)?, + key: WrappedSymmetricKey::try_from_optional(attachment.key)?, }) } } diff --git a/crates/bitwarden-vault/src/cipher/cipher.rs b/crates/bitwarden-vault/src/cipher/cipher.rs index 36a1b011b..4be8dcce8 100644 --- a/crates/bitwarden-vault/src/cipher/cipher.rs +++ b/crates/bitwarden-vault/src/cipher/cipher.rs @@ -5,6 +5,7 @@ use bitwarden_core::{ }; use bitwarden_crypto::{ CryptoError, Decryptable, EncString, Encryptable, IdentifyKey, KeyStoreContext, + WrappedSymmetricKey, }; use bitwarden_error::bitwarden_error; use chrono::{DateTime, Utc}; @@ -76,7 +77,7 @@ pub struct Cipher { /// More recent ciphers uses individual encryption keys to encrypt the other fields of the /// Cipher. - pub key: Option, + pub key: Option, pub name: EncString, pub notes: Option, @@ -116,7 +117,7 @@ pub struct CipherView { pub collection_ids: Vec, /// Temporary, required to support re-encrypting existing items. - pub key: Option, + pub key: Option, pub name: String, pub notes: Option, @@ -168,7 +169,7 @@ pub struct CipherListView { pub collection_ids: Vec, /// Temporary, required to support calculating TOTP from CipherListView. - pub key: Option, + pub key: Option, pub name: String, pub subtitle: String, @@ -320,7 +321,7 @@ impl Cipher { pub(super) fn decrypt_cipher_key( ctx: &mut KeyStoreContext, key: SymmetricKeyId, - ciphers_key: &Option, + ciphers_key: &Option, ) -> Result { const CIPHER_KEY: SymmetricKeyId = SymmetricKeyId::Local("cipher_key"); match ciphers_key { @@ -453,7 +454,7 @@ impl CipherView { ctx: &mut KeyStoreContext, key: SymmetricKeyId, ) -> Result<(), CryptoError> { - let old_ciphers_key = Cipher::decrypt_cipher_key(ctx, key, &self.key)?; + let old_ciphers_key = Cipher::decrypt_cipher_key(ctx, key, &self.key.take())?; const NEW_KEY: SymmetricKeyId = SymmetricKeyId::Local("new_cipher_key"); @@ -489,8 +490,9 @@ impl CipherView { if let Some(attachments) = &mut self.attachments { for attachment in attachments { if let Some(attachment_key) = &mut attachment.key { - let dec_attachment_key: Vec = attachment_key.decrypt(ctx, old_key)?; - *attachment_key = dec_attachment_key.encrypt(ctx, new_key)?; + let tmp_attachment_key_id = SymmetricKeyId::Local("attachment_key"); + ctx.unwrap_symmetric_key(old_key, tmp_attachment_key_id, attachment_key)?; + *attachment_key = ctx.wrap_symmetric_key(new_key, tmp_attachment_key_id)?; } } } @@ -544,8 +546,9 @@ impl CipherView { // If the cipher has a key, we need to re-encrypt it with the new organization key if let Some(cipher_key) = &mut self.key { - let dec_cipher_key: Vec = cipher_key.decrypt(ctx, old_key)?; - *cipher_key = dec_cipher_key.encrypt(ctx, new_key)?; + let tmp_cipher_key_id = SymmetricKeyId::Local("cipher_key"); + ctx.unwrap_symmetric_key(old_key, tmp_cipher_key_id, cipher_key)?; + *cipher_key = ctx.wrap_symmetric_key(new_key, tmp_cipher_key_id)?; } else { // If the cipher does not have a key, we need to reencrypt all attachment keys self.reencrypt_attachment_keys(ctx, old_key, new_key)?; @@ -715,7 +718,7 @@ impl TryFrom for Cipher { creation_date: require!(cipher.creation_date).parse()?, deleted_date: cipher.deleted_date.map(|d| d.parse()).transpose()?, revision_date: require!(cipher.revision_date).parse()?, - key: EncString::try_from_optional(cipher.key)?, + key: WrappedSymmetricKey::try_from_optional(cipher.key)?, }) } } @@ -944,11 +947,14 @@ mod tests { .unwrap(); // Make sure that the cipher key is decryptable - let _: Vec = original_cipher - .key - .unwrap() - .decrypt(&mut key_store.context(), SymmetricKeyId::User) - .unwrap(); + let wrapped_key = original_cipher.key.unwrap(); + let mut ctx = key_store.context(); + ctx.unwrap_symmetric_key( + SymmetricKeyId::User, + SymmetricKeyId::Local("test_cipher_key"), + &wrapped_key, + ) + .unwrap(); } #[test] @@ -1090,12 +1096,20 @@ mod tests { // Check that the attachment key has been re-encrypted with the org key, // and the value matches with the original attachment key let new_attachment_key = cipher.attachments.unwrap()[0].key.clone().unwrap(); - let new_attachment_key_dec: Vec<_> = new_attachment_key - .decrypt(&mut key_store.context(), org_key) + let mut ctx = key_store.context(); + let new_attachment_key_id = ctx + .unwrap_symmetric_key( + org_key, + SymmetricKeyId::Local("test_attachment_key"), + &new_attachment_key, + ) + .unwrap(); + #[allow(deprecated)] + let new_attachment_key_dec = ctx + .dangerous_get_symmetric_key(new_attachment_key_id) .unwrap(); - let new_attachment_key_dec: SymmetricCryptoKey = new_attachment_key_dec.try_into().unwrap(); - assert_eq!(new_attachment_key_dec, attachment_key_val); + assert_eq!(*new_attachment_key_dec, attachment_key_val); let cred2: Fido2CredentialFullView = cipher .login @@ -1152,19 +1166,20 @@ mod tests { cipher.move_to_organization(&mut ctx, org).unwrap(); // Check that the cipher key has been re-encrypted with the org key, - let new_cipher_key_dec: Vec<_> = cipher - .key - .clone() - .unwrap() - .decrypt(&mut ctx, org_key) + let wrapped_new_cipher_key = cipher.key.clone().unwrap(); + let new_cipher_key_dec = ctx + .unwrap_symmetric_key( + org_key, + SymmetricKeyId::Local("test_cipher_key"), + &wrapped_new_cipher_key, + ) .unwrap(); - - let new_cipher_key_dec: SymmetricCryptoKey = new_cipher_key_dec.try_into().unwrap(); - + #[allow(deprecated)] + let new_cipher_key_dec = ctx.dangerous_get_symmetric_key(new_cipher_key_dec).unwrap(); #[allow(deprecated)] let cipher_key_val = ctx.dangerous_get_symmetric_key(cipher_key).unwrap(); - assert_eq!(new_cipher_key_dec, *cipher_key_val); + assert_eq!(new_cipher_key_dec, cipher_key_val); // Check that the attachment key hasn't changed assert_eq!( @@ -1172,8 +1187,9 @@ mod tests { .key .as_ref() .unwrap() + .as_inner() .to_string(), - attachment_key_enc.to_string() + attachment_key_enc.as_inner().to_string() ); let cred2: Fido2Credential = cipher diff --git a/crates/bitwarden-vault/src/cipher/cipher_client.rs b/crates/bitwarden-vault/src/cipher/cipher_client.rs index 8269d94e1..15a2c2c18 100644 --- a/crates/bitwarden-vault/src/cipher/cipher_client.rs +++ b/crates/bitwarden-vault/src/cipher/cipher_client.rs @@ -278,8 +278,8 @@ mod tests { // Ensure attachment key is updated since it's now protected by the cipher key assert_ne!( - attachment.clone().key.unwrap().to_string(), - attachment_view.clone().key.unwrap().to_string() + attachment.clone().key.unwrap().as_inner().to_string(), + attachment_view.clone().key.unwrap().as_inner().to_string() ); assert_eq!(attachment_view.file_name.as_deref(), Some("h.txt")); @@ -320,8 +320,8 @@ mod tests { // Ensure attachment key is still the same since it's protected by the cipher key assert_eq!( - attachment.clone().key.as_ref().unwrap().to_string(), - attachment_view.key.as_ref().unwrap().to_string() + attachment.clone().key.as_ref().unwrap(), + attachment_view.key.as_ref().unwrap(), ); let content = client diff --git a/crates/bitwarden-vault/src/uniffi_support.rs b/crates/bitwarden-vault/src/uniffi_support.rs index 932f0de7a..9fb051a2b 100644 --- a/crates/bitwarden-vault/src/uniffi_support.rs +++ b/crates/bitwarden-vault/src/uniffi_support.rs @@ -1,7 +1,12 @@ -use bitwarden_crypto::EncString; +use bitwarden_crypto::{EncString, WrappedSymmetricKey}; use uuid::Uuid; uniffi::ffi_converter_forward!(EncString, bitwarden_crypto::UniFfiTag, crate::UniFfiTag); +uniffi::ffi_converter_forward!( + WrappedSymmetricKey, + bitwarden_crypto::UniFfiTag, + crate::UniFfiTag +); type DateTime = chrono::DateTime; uniffi::ffi_converter_forward!(DateTime, bitwarden_core::UniFfiTag, crate::UniFfiTag); diff --git a/crates/bitwarden-wasm-internal/src/pure_crypto.rs b/crates/bitwarden-wasm-internal/src/pure_crypto.rs index 259460734..35b4b8f74 100644 --- a/crates/bitwarden-wasm-internal/src/pure_crypto.rs +++ b/crates/bitwarden-wasm-internal/src/pure_crypto.rs @@ -2,6 +2,7 @@ use std::str::FromStr; use bitwarden_crypto::{ CryptoError, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey, SymmetricCryptoKey, + WrappedSymmetricKey, }; use wasm_bindgen::prelude::*; @@ -82,7 +83,7 @@ impl PureCrypto { let master_key = MasterKey::derive(master_password.as_str(), email.as_str(), &kdf)?; let encrypted_user_key = EncString::from_str(&encrypted_user_key)?; let result = master_key - .decrypt_user_key(encrypted_user_key) + .decrypt_user_key(encrypted_user_key.into()) .map_err(|_| CryptoError::InvalidKey)?; Ok(result.to_encoded()) } @@ -96,7 +97,7 @@ impl PureCrypto { let master_key = MasterKey::derive(master_password.as_str(), email.as_str(), &kdf)?; let user_key = SymmetricCryptoKey::try_from(user_key)?; let result = master_key.encrypt_user_key(&user_key)?; - Ok(result.to_string()) + Ok(result.as_inner().to_string()) } // Generate userkey @@ -107,6 +108,27 @@ impl PureCrypto { pub fn generate_user_key_xchacha20_poly1305() -> Vec { SymmetricCryptoKey::make_xchacha20_poly1305_key().to_encoded() } + + // Key Wrap + pub fn wrap_symmetric_key( + key_to_be_wrapped: Vec, + wrapping_key: Vec, + ) -> Result { + let key_to_be_wrapped: SymmetricCryptoKey = key_to_be_wrapped.try_into()?; + let wrapping_key: SymmetricCryptoKey = wrapping_key.try_into()?; + let wrapped_key: EncString = key_to_be_wrapped.wrap_with(&wrapping_key)?.into_inner(); + Ok(wrapped_key.to_string()) + } + + pub fn unwrap_symmetric_key( + wrapped_key: String, + wrapping_key: Vec, + ) -> Result, CryptoError> { + let wrapped_key: WrappedSymmetricKey = EncString::from_str(&wrapped_key)?.into(); + let wrapping_key: SymmetricCryptoKey = wrapping_key.try_into()?; + let unwrapped_key: SymmetricCryptoKey = wrapped_key.unwrap_with(&wrapping_key)?; + Ok(unwrapped_key.to_encoded()) + } } #[cfg(test)]