bitwarden_core/auth/
register.rs

1use bitwarden_api_identity::{
2    apis::accounts_api::accounts_register_post,
3    models::{KeysRequestModel, RegisterRequestModel},
4};
5use bitwarden_crypto::{
6    default_pbkdf2_iterations, CryptoError, HashPurpose, Kdf, MasterKey, RsaKeyPair,
7};
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10
11use crate::{ApiError, Client};
12
13#[derive(Serialize, Deserialize, Debug)]
14#[serde(rename_all = "camelCase", deny_unknown_fields)]
15pub struct RegisterRequest {
16    pub email: String,
17    pub name: Option<String>,
18    pub password: String,
19    pub password_hint: Option<String>,
20}
21
22#[derive(Debug, Error)]
23pub enum RegisterError {
24    #[error(transparent)]
25    Crypto(#[from] CryptoError),
26    #[error(transparent)]
27    Api(#[from] ApiError),
28}
29
30/// Half baked implementation of user registration
31pub(super) async fn register(client: &Client, req: &RegisterRequest) -> Result<(), RegisterError> {
32    let config = client.internal.get_api_configurations().await;
33
34    let kdf = Kdf::default();
35
36    let keys = make_register_keys(req.email.to_owned(), req.password.to_owned(), kdf)?;
37
38    accounts_register_post(
39        &config.identity,
40        Some(RegisterRequestModel {
41            name: req.name.to_owned(),
42            email: req.email.to_owned(),
43            master_password_hash: keys.master_password_hash,
44            master_password_hint: req.password_hint.to_owned(),
45            captcha_response: None, // TODO: Add
46            key: Some(keys.encrypted_user_key.to_string()),
47            keys: Some(Box::new(KeysRequestModel {
48                public_key: keys.keys.public,
49                encrypted_private_key: keys.keys.private.to_string(),
50            })),
51            token: None,
52            organization_user_id: None,
53            kdf: Some(bitwarden_api_identity::models::KdfType::PBKDF2_SHA256),
54            kdf_iterations: Some(default_pbkdf2_iterations().get() as i32),
55            kdf_memory: None,
56            kdf_parallelism: None,
57            reference_data: None, // TODO: Add
58        }),
59    )
60    .await
61    .map_err(ApiError::from)?;
62
63    Ok(())
64}
65
66pub(super) fn make_register_keys(
67    email: String,
68    password: String,
69    kdf: Kdf,
70) -> Result<RegisterKeyResponse, CryptoError> {
71    let master_key = MasterKey::derive(&password, &email, &kdf)?;
72    let master_password_hash =
73        master_key.derive_master_key_hash(password.as_bytes(), HashPurpose::ServerAuthorization)?;
74    let (user_key, encrypted_user_key) = master_key.make_user_key()?;
75    let keys = user_key.make_key_pair()?;
76
77    Ok(RegisterKeyResponse {
78        master_password_hash,
79        encrypted_user_key: encrypted_user_key.to_string(),
80        keys,
81    })
82}
83
84#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
85pub struct RegisterKeyResponse {
86    pub master_password_hash: String,
87    pub encrypted_user_key: String,
88    pub keys: RsaKeyPair,
89}