bitwarden_core/key_management/
crypto_client.rs1use bitwarden_crypto::{CryptoError, Decryptable, Kdf};
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 DeriveKeyConnectorError, DeriveKeyConnectorRequest, EnrollAdminPasswordResetError,
10 MakeKeyPairResponse, VerifyAsymmetricKeysRequest, VerifyAsymmetricKeysResponse,
11 derive_key_connector, make_key_pair, verify_asymmetric_keys,
12};
13#[cfg(any(feature = "wasm", test))]
14use crate::key_management::PasswordProtectedKeyEnvelope;
15#[cfg(feature = "internal")]
16use crate::key_management::{
17 SymmetricKeyId,
18 crypto::{
19 DerivePinKeyResponse, InitOrgCryptoRequest, InitUserCryptoRequest, UpdatePasswordResponse,
20 derive_pin_key, derive_pin_user_key, enroll_admin_password_reset, get_user_encryption_key,
21 initialize_org_crypto, initialize_user_crypto,
22 },
23};
24use crate::{
25 Client,
26 client::encryption_settings::EncryptionSettingsError,
27 error::StatefulCryptoError,
28 key_management::crypto::{
29 CryptoClientError, EnrollPinResponse, UpdateKdfResponse, UserCryptoV2KeysResponse,
30 enroll_pin, get_v2_rotated_account_keys, make_update_kdf, make_update_password,
31 make_v2_keys_for_v1_user,
32 },
33};
34
35#[cfg_attr(feature = "wasm", wasm_bindgen)]
37pub struct CryptoClient {
38 pub(crate) client: crate::Client,
39}
40
41#[cfg_attr(feature = "wasm", wasm_bindgen)]
42impl CryptoClient {
43 pub async fn initialize_user_crypto(
46 &self,
47 req: InitUserCryptoRequest,
48 ) -> Result<(), EncryptionSettingsError> {
49 initialize_user_crypto(&self.client, req).await
50 }
51
52 pub async fn initialize_org_crypto(
55 &self,
56 req: InitOrgCryptoRequest,
57 ) -> Result<(), EncryptionSettingsError> {
58 initialize_org_crypto(&self.client, req).await
59 }
60
61 pub fn make_key_pair(&self, user_key: B64) -> Result<MakeKeyPairResponse, CryptoError> {
64 make_key_pair(user_key)
65 }
66
67 pub fn verify_asymmetric_keys(
71 &self,
72 request: VerifyAsymmetricKeysRequest,
73 ) -> Result<VerifyAsymmetricKeysResponse, CryptoError> {
74 verify_asymmetric_keys(request)
75 }
76
77 pub fn make_keys_for_user_crypto_v2(
79 &self,
80 ) -> Result<UserCryptoV2KeysResponse, StatefulCryptoError> {
81 make_v2_keys_for_v1_user(&self.client)
82 }
83
84 pub fn get_v2_rotated_account_keys(
86 &self,
87 ) -> Result<UserCryptoV2KeysResponse, StatefulCryptoError> {
88 get_v2_rotated_account_keys(&self.client)
89 }
90
91 pub fn make_update_kdf(
95 &self,
96 password: String,
97 kdf: Kdf,
98 ) -> Result<UpdateKdfResponse, CryptoClientError> {
99 make_update_kdf(&self.client, &password, &kdf)
100 }
101
102 pub fn enroll_pin(&self, pin: String) -> Result<EnrollPinResponse, CryptoClientError> {
106 enroll_pin(&self.client, pin)
107 }
108
109 pub fn enroll_pin_with_encrypted_pin(
113 &self,
114 encrypted_pin: String,
116 ) -> Result<EnrollPinResponse, CryptoClientError> {
117 let encrypted_pin: EncString = encrypted_pin.parse()?;
118 let pin = encrypted_pin.decrypt(
119 &mut self.client.internal.get_key_store().context_mut(),
120 SymmetricKeyId::User,
121 )?;
122 enroll_pin(&self.client, pin)
123 }
124
125 #[cfg(any(feature = "wasm", test))]
128 pub fn unseal_password_protected_key_envelope(
129 &self,
130 pin: String,
131 envelope: PasswordProtectedKeyEnvelope,
132 ) -> Result<Vec<u8>, CryptoClientError> {
133 let mut ctx = self.client.internal.get_key_store().context_mut();
134 let key_slot = SymmetricKeyId::Local("unseal_password_protected_key_envelope");
135 envelope.unseal(key_slot, pin.as_str(), &mut ctx)?;
136 #[allow(deprecated)]
137 let key = ctx.dangerous_get_symmetric_key(key_slot)?;
138 Ok(key.to_encoded().to_vec())
139 }
140}
141
142impl CryptoClient {
143 pub async fn get_user_encryption_key(&self) -> Result<B64, CryptoClientError> {
146 get_user_encryption_key(&self.client).await
147 }
148
149 pub fn make_update_password(
153 &self,
154 new_password: String,
155 ) -> Result<UpdatePasswordResponse, CryptoClientError> {
156 make_update_password(&self.client, new_password)
157 }
158
159 pub fn derive_pin_key(&self, pin: String) -> Result<DerivePinKeyResponse, CryptoClientError> {
163 derive_pin_key(&self.client, pin)
164 }
165
166 pub fn derive_pin_user_key(
169 &self,
170 encrypted_pin: EncString,
171 ) -> Result<EncString, CryptoClientError> {
172 derive_pin_user_key(&self.client, encrypted_pin)
173 }
174
175 pub fn enroll_admin_password_reset(
178 &self,
179 public_key: B64,
180 ) -> Result<UnsignedSharedKey, EnrollAdminPasswordResetError> {
181 enroll_admin_password_reset(&self.client, public_key)
182 }
183
184 pub fn derive_key_connector(
186 &self,
187 request: DeriveKeyConnectorRequest,
188 ) -> Result<B64, DeriveKeyConnectorError> {
189 derive_key_connector(request)
190 }
191}
192
193impl Client {
194 pub fn crypto(&self) -> CryptoClient {
196 CryptoClient {
197 client: self.clone(),
198 }
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use bitwarden_crypto::{BitwardenLegacyKeyBytes, SymmetricCryptoKey};
205
206 use super::*;
207 use crate::client::test_accounts::test_bitwarden_com_account;
208
209 #[tokio::test]
210 async fn test_enroll_pin_envelope() {
211 let client = Client::init_test_account(test_bitwarden_com_account()).await;
213 let user_key_initial =
214 SymmetricCryptoKey::try_from(client.crypto().get_user_encryption_key().await.unwrap())
215 .unwrap();
216
217 let pin = "1234";
219 let enroll_response = client.crypto().enroll_pin(pin.to_string()).unwrap();
220 let re_enroll_response = client
221 .crypto()
222 .enroll_pin_with_encrypted_pin(enroll_response.user_key_encrypted_pin.to_string())
223 .unwrap();
224
225 let secret = BitwardenLegacyKeyBytes::from(
226 client
227 .crypto()
228 .unseal_password_protected_key_envelope(
229 pin.to_string(),
230 re_enroll_response.pin_protected_user_key_envelope,
231 )
232 .unwrap(),
233 );
234 let user_key_final = SymmetricCryptoKey::try_from(&secret).unwrap();
235 assert_eq!(user_key_initial, user_key_final);
236 }
237}