bitwarden_core/key_management/
crypto_client.rs1use bitwarden_crypto::{CryptoError, Decryptable};
2#[cfg(feature = "internal")]
3use bitwarden_crypto::{EncString, UnsignedSharedKey};
4use bitwarden_encoding::B64;
5#[cfg(feature = "wasm")]
6use wasm_bindgen::prelude::*;
7
8use super::crypto::{
9 derive_key_connector, make_key_pair, verify_asymmetric_keys, DeriveKeyConnectorError,
10 DeriveKeyConnectorRequest, EnrollAdminPasswordResetError, MakeKeyPairResponse,
11 VerifyAsymmetricKeysRequest, VerifyAsymmetricKeysResponse,
12};
13#[cfg(any(feature = "wasm", test))]
14use crate::key_management::PasswordProtectedKeyEnvelope;
15#[cfg(feature = "internal")]
16use crate::key_management::{
17 crypto::{
18 derive_pin_key, derive_pin_user_key, enroll_admin_password_reset, get_user_encryption_key,
19 initialize_org_crypto, initialize_user_crypto, update_password, DerivePinKeyResponse,
20 InitOrgCryptoRequest, InitUserCryptoRequest, UpdatePasswordResponse,
21 },
22 SymmetricKeyId,
23};
24use crate::{
25 client::encryption_settings::EncryptionSettingsError,
26 error::StatefulCryptoError,
27 key_management::crypto::{
28 enroll_pin, get_v2_rotated_account_keys, make_v2_keys_for_v1_user, CryptoClientError,
29 EnrollPinResponse, UserCryptoV2KeysResponse,
30 },
31 Client,
32};
33
34#[cfg_attr(feature = "wasm", wasm_bindgen)]
36pub struct CryptoClient {
37 pub(crate) client: crate::Client,
38}
39
40#[cfg_attr(feature = "wasm", wasm_bindgen)]
41impl CryptoClient {
42 pub async fn initialize_user_crypto(
45 &self,
46 req: InitUserCryptoRequest,
47 ) -> Result<(), EncryptionSettingsError> {
48 initialize_user_crypto(&self.client, req).await
49 }
50
51 pub async fn initialize_org_crypto(
54 &self,
55 req: InitOrgCryptoRequest,
56 ) -> Result<(), EncryptionSettingsError> {
57 initialize_org_crypto(&self.client, req).await
58 }
59
60 pub fn make_key_pair(&self, user_key: B64) -> Result<MakeKeyPairResponse, CryptoError> {
63 make_key_pair(user_key)
64 }
65
66 pub fn verify_asymmetric_keys(
70 &self,
71 request: VerifyAsymmetricKeysRequest,
72 ) -> Result<VerifyAsymmetricKeysResponse, CryptoError> {
73 verify_asymmetric_keys(request)
74 }
75
76 pub fn make_keys_for_user_crypto_v2(
78 &self,
79 ) -> Result<UserCryptoV2KeysResponse, StatefulCryptoError> {
80 make_v2_keys_for_v1_user(&self.client)
81 }
82
83 pub fn get_v2_rotated_account_keys(
85 &self,
86 ) -> Result<UserCryptoV2KeysResponse, StatefulCryptoError> {
87 get_v2_rotated_account_keys(&self.client)
88 }
89
90 pub fn enroll_pin(&self, pin: String) -> Result<EnrollPinResponse, CryptoClientError> {
94 enroll_pin(&self.client, pin)
95 }
96
97 pub fn enroll_pin_with_encrypted_pin(
101 &self,
102 encrypted_pin: String,
104 ) -> Result<EnrollPinResponse, CryptoClientError> {
105 let encrypted_pin: EncString = encrypted_pin.parse()?;
106 let pin = encrypted_pin.decrypt(
107 &mut self.client.internal.get_key_store().context_mut(),
108 SymmetricKeyId::User,
109 )?;
110 enroll_pin(&self.client, pin)
111 }
112
113 #[cfg(any(feature = "wasm", test))]
116 pub fn unseal_password_protected_key_envelope(
117 &self,
118 pin: String,
119 envelope: PasswordProtectedKeyEnvelope,
120 ) -> Result<Vec<u8>, CryptoClientError> {
121 let mut ctx = self.client.internal.get_key_store().context_mut();
122 let key_slot = SymmetricKeyId::Local("unseal_password_protected_key_envelope");
123 envelope.unseal(key_slot, pin.as_str(), &mut ctx)?;
124 #[allow(deprecated)]
125 let key = ctx.dangerous_get_symmetric_key(key_slot)?;
126 Ok(key.to_encoded().to_vec())
127 }
128}
129
130impl CryptoClient {
131 pub async fn get_user_encryption_key(&self) -> Result<B64, CryptoClientError> {
134 get_user_encryption_key(&self.client).await
135 }
136
137 pub fn update_password(
140 &self,
141 new_password: String,
142 ) -> Result<UpdatePasswordResponse, CryptoClientError> {
143 update_password(&self.client, new_password)
144 }
145
146 pub fn derive_pin_key(&self, pin: String) -> Result<DerivePinKeyResponse, CryptoClientError> {
150 derive_pin_key(&self.client, pin)
151 }
152
153 pub fn derive_pin_user_key(
156 &self,
157 encrypted_pin: EncString,
158 ) -> Result<EncString, CryptoClientError> {
159 derive_pin_user_key(&self.client, encrypted_pin)
160 }
161
162 pub fn enroll_admin_password_reset(
165 &self,
166 public_key: B64,
167 ) -> Result<UnsignedSharedKey, EnrollAdminPasswordResetError> {
168 enroll_admin_password_reset(&self.client, public_key)
169 }
170
171 pub fn derive_key_connector(
173 &self,
174 request: DeriveKeyConnectorRequest,
175 ) -> Result<B64, DeriveKeyConnectorError> {
176 derive_key_connector(request)
177 }
178}
179
180impl Client {
181 pub fn crypto(&self) -> CryptoClient {
183 CryptoClient {
184 client: self.clone(),
185 }
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use bitwarden_crypto::{BitwardenLegacyKeyBytes, SymmetricCryptoKey};
192
193 use super::*;
194 use crate::client::test_accounts::test_bitwarden_com_account;
195
196 #[tokio::test]
197 async fn test_enroll_pin_envelope() {
198 let client = Client::init_test_account(test_bitwarden_com_account()).await;
200 let user_key_initial =
201 SymmetricCryptoKey::try_from(client.crypto().get_user_encryption_key().await.unwrap())
202 .unwrap();
203
204 let pin = "1234";
206 let enroll_response = client.crypto().enroll_pin(pin.to_string()).unwrap();
207 let re_enroll_response = client
208 .crypto()
209 .enroll_pin_with_encrypted_pin(enroll_response.user_key_encrypted_pin.to_string())
210 .unwrap();
211
212 let secret = BitwardenLegacyKeyBytes::from(
213 client
214 .crypto()
215 .unseal_password_protected_key_envelope(
216 pin.to_string(),
217 re_enroll_response.pin_protected_user_key_envelope,
218 )
219 .unwrap(),
220 );
221 let user_key_final = SymmetricCryptoKey::try_from(&secret).unwrap();
222 assert_eq!(user_key_initial, user_key_final);
223 }
224}