From aad70143868ebef6694fbdc95766e290da70f612 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Tue, 27 Feb 2024 13:47:07 +0100 Subject: [PATCH] [PM-6437] Implement admin password reset logic (#631) Implement cryptographic logic for admin password reset. --- crates/bitwarden-uniffi/src/crypto.rs | 15 ++++- crates/bitwarden/src/mobile/client_crypto.rs | 16 +++-- crates/bitwarden/src/mobile/crypto.rs | 61 ++++++++++++++++++++ 3 files changed, 87 insertions(+), 5 deletions(-) diff --git a/crates/bitwarden-uniffi/src/crypto.rs b/crates/bitwarden-uniffi/src/crypto.rs index 2d847b33d..0f877d52e 100644 --- a/crates/bitwarden-uniffi/src/crypto.rs +++ b/crates/bitwarden-uniffi/src/crypto.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use bitwarden::mobile::crypto::{ DerivePinKeyResponse, InitOrgCryptoRequest, InitUserCryptoRequest, UpdatePasswordResponse, }; -use bitwarden_crypto::EncString; +use bitwarden_crypto::{AsymmetricEncString, EncString}; use crate::{error::Result, Client}; @@ -83,4 +83,17 @@ impl ClientCrypto { .derive_pin_user_key(encrypted_pin) .await?) } + + pub async fn enroll_admin_password_reset( + &self, + public_key: String, + ) -> Result { + Ok(self + .0 + .0 + .write() + .await + .crypto() + .enroll_admin_password_reset(public_key)?) + } } diff --git a/crates/bitwarden/src/mobile/client_crypto.rs b/crates/bitwarden/src/mobile/client_crypto.rs index f6ea3346b..6ef65975d 100644 --- a/crates/bitwarden/src/mobile/client_crypto.rs +++ b/crates/bitwarden/src/mobile/client_crypto.rs @@ -1,14 +1,14 @@ #[cfg(feature = "internal")] -use bitwarden_crypto::EncString; +use bitwarden_crypto::{AsymmetricEncString, EncString}; use crate::Client; #[cfg(feature = "internal")] use crate::{ error::Result, mobile::crypto::{ - derive_pin_key, derive_pin_user_key, get_user_encryption_key, initialize_org_crypto, - initialize_user_crypto, update_password, DerivePinKeyResponse, InitOrgCryptoRequest, - InitUserCryptoRequest, UpdatePasswordResponse, + derive_pin_key, derive_pin_user_key, enroll_admin_password_reset, get_user_encryption_key, + initialize_org_crypto, initialize_user_crypto, update_password, DerivePinKeyResponse, + InitOrgCryptoRequest, InitUserCryptoRequest, UpdatePasswordResponse, }, }; @@ -49,6 +49,14 @@ impl<'a> ClientCrypto<'a> { pub async fn derive_pin_user_key(&mut self, encrypted_pin: EncString) -> Result { derive_pin_user_key(self.client, encrypted_pin) } + + #[cfg(feature = "internal")] + pub fn enroll_admin_password_reset( + &mut self, + public_key: String, + ) -> Result { + enroll_admin_password_reset(self.client, public_key) + } } impl<'a> Client { diff --git a/crates/bitwarden/src/mobile/crypto.rs b/crates/bitwarden/src/mobile/crypto.rs index a48b29b15..214643023 100644 --- a/crates/bitwarden/src/mobile/crypto.rs +++ b/crates/bitwarden/src/mobile/crypto.rs @@ -286,6 +286,24 @@ fn derive_pin_protected_user_key( Ok(derived_key.encrypt_user_key(user_key)?) } +#[cfg(feature = "internal")] +pub(super) fn enroll_admin_password_reset( + client: &mut Client, + public_key: String, +) -> Result { + use base64::{engine::general_purpose::STANDARD, Engine}; + use bitwarden_crypto::AsymmetricPublicCryptoKey; + + let public_key = AsymmetricPublicCryptoKey::from_der(&STANDARD.decode(public_key)?)?; + let enc = client.get_encryption_settings()?; + let key = enc.get_key(&None).ok_or(Error::VaultLocked)?; + + Ok(AsymmetricEncString::encrypt_rsa2048_oaep_sha1( + &key.to_vec(), + &public_key, + )?) +} + #[cfg(test)] mod tests { use super::*; @@ -461,4 +479,47 @@ mod tests { .to_base64() ); } + + #[cfg(feature = "internal")] + #[test] + fn test_enroll_admin_password_reset() { + use std::{num::NonZeroU32, ops::Deref}; + + use base64::{engine::general_purpose::STANDARD, Engine}; + use bitwarden_crypto::AsymmetricCryptoKey; + + let mut client = Client::new(None); + client.set_login_method(LoginMethod::User(UserLoginMethod::Username { + client_id: "7b821276-e27c-400b-9853-606393c87f18".to_owned(), + email: "test@bitwarden.com".to_owned(), + kdf: Kdf::PBKDF2 { + iterations: NonZeroU32::new(600_000).unwrap(), + }, + })); + + let user_key = "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 + .initialize_user_crypto("asdfasdfasdf", user_key, private_key) + .unwrap(); + + let public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsy7RFHcX3C8Q4/OMmhhbFReYWfB45W9PDTEA8tUZwZmtOiN2RErIS2M1c+K/4HoDJ/TjpbX1f2MZcr4nWvKFuqnZXyewFc+jmvKVewYi+NAu2++vqKq2kKcmMNhwoQDQdQIVy/Uqlp4Cpi2cIwO6ogq5nHNJGR3jm+CpyrafYlbz1bPvL3hbyoGDuG2tgADhyhXUdFuef2oF3wMvn1lAJAvJnPYpMiXUFmj1ejmbwtlxZDrHgUJvUcp7nYdwUKaFoi+sOttHn3u7eZPtNvxMjhSS/X/1xBIzP/mKNLdywH5LoRxniokUk+fV3PYUxJsiU3lV0Trc/tH46jqd8ZGjmwIDAQAB"; + + let encrypted = enroll_admin_password_reset(&mut client, public_key.to_owned()).unwrap(); + + 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 private_key = + AsymmetricCryptoKey::from_der(&STANDARD.decode(private_key).unwrap()).unwrap(); + let decrypted: Vec = encrypted.decrypt_with_key(&private_key).unwrap(); + + let expected = client + .get_encryption_settings() + .unwrap() + .get_key(&None) + .unwrap() + .to_vec() + .deref() + .clone(); + assert_eq!(decrypted, expected); + } }