-
Notifications
You must be signed in to change notification settings - Fork 17
[PM-24051] Init crypto and update kdf with MasterPasswordUnlock #399
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
44773f7
0712878
8ca20a8
28a8423
b089ea0
0d35393
a08179d
b3647f2
36d3136
7c8664d
9c7d50d
70ad9d3
110f4db
ff86adf
fa253a3
4a95dc5
cd37e31
3bf880f
93a3d0c
f0c4f2a
3e6a46a
7c34725
042678c
9676069
7973f12
5a4f314
7e425ca
c9aaa8c
2591773
3212002
9b293c7
5b3ecf8
77c2d68
a487fff
00811a9
da700c0
ecfde52
debf701
ac64be7
92d710d
8c3b599
70dee0c
ad732ce
5e6f1dd
d11f653
0e104bb
a9f46db
3cef902
ae10e35
1f32993
b5218cd
6861006
b22537f
edd0cbf
af667b0
7d538ca
e5f16dc
3cf5cc4
13ecd8e
1b8ccee
6b98aaf
4a00571
308066b
260078b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ use crate::{ | |
JwtToken, | ||
}, | ||
client::{internal::UserKeyState, LoginMethod, UserLoginMethod}, | ||
key_management::UserDecryptionData, | ||
require, Client, | ||
}; | ||
|
||
|
@@ -32,34 +33,54 @@ pub(crate) async fn login_api_key( | |
let kdf = client.auth().prelogin(email.clone()).await?; | ||
|
||
client.internal.set_tokens( | ||
r.access_token.clone(), | ||
r.refresh_token.clone(), | ||
r.access_token.to_owned(), | ||
r.refresh_token.to_owned(), | ||
Comment on lines
+36
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ๐ญ not a rust expert It is weird to me that we are keeping From the rust docs https://doc.rust-lang.org/std/borrow/trait.ToOwned.html
It sounds like we should prefer clone when going from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree on this. In this case we are not even going from borrowed data to owned data, so semantically it is confusing saying "to owned" (implying we are converting to an owned object (by cloning usually). We're going from String to String (T to T), so we should clone. |
||
r.expires_in, | ||
); | ||
|
||
let master_key = MasterKey::derive(&input.password, &email, &kdf)?; | ||
let private_key = r.private_key.as_deref(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ref the other comment about TryFrom<Option<&str>> |
||
let private_key: EncString = require!(private_key).parse()?; | ||
|
||
let user_key_state = UserKeyState { | ||
private_key, | ||
signing_key: None, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Not in this PR) looks like we don't have support for parsing the new keys from the token response here yet. We should make a follow-up ticket somewhere to fix this. |
||
security_state: None, | ||
}; | ||
|
||
if let Some(master_password_unlock) = r | ||
.user_decryption_options | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion (non-blocking): We have this long chain quite a bit. It may be nice to just implement "master_password_unlock() -> Option" on the decryptionData. Then, you can rewrite as (untested):
|
||
.as_ref() | ||
.map(UserDecryptionData::try_from) | ||
.transpose()? | ||
.and_then(|user_decryption| user_decryption.master_password_unlock) | ||
{ | ||
client | ||
.internal | ||
.initialize_user_crypto_master_password_unlock( | ||
input.password.clone(), | ||
master_password_unlock, | ||
user_key_state, | ||
)?; | ||
} else { | ||
Comment on lines
+50
to
+64
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. โ๏ธ
|
||
let user_key = r.key.as_deref(); | ||
let user_key: EncString = require!(user_key).parse()?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ref the comment about TryFrom<Option<&str>> |
||
let master_key = MasterKey::derive(&input.password, &email, &kdf)?; | ||
|
||
client.internal.initialize_user_crypto_master_key( | ||
master_key, | ||
user_key, | ||
user_key_state, | ||
)?; | ||
} | ||
|
||
client | ||
.internal | ||
.set_login_method(LoginMethod::User(UserLoginMethod::ApiKey { | ||
client_id: input.client_id.to_owned(), | ||
client_secret: input.client_secret.to_owned(), | ||
client_id: input.client_id.clone(), | ||
client_secret: input.client_secret.clone(), | ||
email, | ||
kdf, | ||
})); | ||
|
||
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, | ||
UserKeyState { | ||
private_key, | ||
signing_key: None, | ||
security_state: None, | ||
}, | ||
)?; | ||
} | ||
|
||
Ok(ApiKeyLoginResponse::process_response(response)) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -89,20 +89,11 @@ pub(crate) async fn complete_auth_request( | |
.await?; | ||
|
||
if let IdentityTokenResponse::Authenticated(r) = response { | ||
let kdf = Kdf::default(); | ||
|
||
client.internal.set_tokens( | ||
r.access_token.clone(), | ||
r.refresh_token.clone(), | ||
r.expires_in, | ||
); | ||
client | ||
.internal | ||
.set_login_method(LoginMethod::User(UserLoginMethod::Username { | ||
client_id: "web".to_owned(), | ||
email: auth_req.email.to_owned(), | ||
kdf: kdf.clone(), | ||
})); | ||
|
||
let method = match res.master_password_hash { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should probably just drop this code. Support for this auth method on the approving side has been removed 3 months ago: bitwarden/clients#14236 (not this PR) |
||
Some(_) => AuthRequestMethod::MasterKey { | ||
|
@@ -118,8 +109,8 @@ pub(crate) async fn complete_auth_request( | |
.crypto() | ||
.initialize_user_crypto(InitUserCryptoRequest { | ||
user_id: None, | ||
kdf_params: kdf, | ||
email: auth_req.email, | ||
kdf_params: Kdf::default(), | ||
email: auth_req.email.to_owned(), | ||
private_key: require!(r.private_key).parse()?, | ||
signing_key: None, | ||
security_state: None, | ||
|
@@ -130,6 +121,14 @@ pub(crate) async fn complete_auth_request( | |
}) | ||
.await?; | ||
|
||
client | ||
.internal | ||
.set_login_method(LoginMethod::User(UserLoginMethod::Username { | ||
client_id: "web".to_owned(), | ||
email: auth_req.email.to_owned(), | ||
kdf: Kdf::default(), | ||
})); | ||
Comment on lines
+124
to
+130
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. โ Looking to clarify. So this will be a change in behavior? Previously this would get overridden by the
With the difference being Reading through the old flow previously kdf was getting set to default and emails match up. Odd for initing crypto to have that side effect but I don't understand all the flows. |
||
|
||
Ok(()) | ||
} else { | ||
Err(LoginError::AuthenticationFailed) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ use crate::auth::{ | |
use crate::{ | ||
auth::{api::request::PasswordTokenRequest, login::LoginError, login::TwoFactorRequest}, | ||
client::LoginMethod, | ||
key_management::UserDecryptionData, | ||
Client, | ||
}; | ||
|
||
|
@@ -41,26 +42,48 @@ pub(crate) async fn login_password( | |
r.refresh_token.clone(), | ||
r.expires_in, | ||
); | ||
|
||
let private_key = r.private_key.as_deref(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like this two-line pattern to convert from Option<&str> to EncString repeats a fair bit in this PR. Could we just implement TryFrom<Option<&str>> for EncString, and then remove all of thees temporary variables? It would make this a bit shorter and more readable. |
||
let private_key: EncString = require!(private_key).parse()?; | ||
|
||
let user_key_state = UserKeyState { | ||
private_key, | ||
signing_key: None, | ||
security_state: None, | ||
}; | ||
|
||
if let Some(master_password_unlock) = r | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. โ๏ธ Same comment about the match for readability. |
||
.user_decryption_options | ||
.as_ref() | ||
.map(UserDecryptionData::try_from) | ||
.transpose()? | ||
.and_then(|user_decryption| user_decryption.master_password_unlock) | ||
{ | ||
client | ||
.internal | ||
.initialize_user_crypto_master_password_unlock( | ||
input.password.clone(), | ||
master_password_unlock, | ||
user_key_state, | ||
)?; | ||
} else { | ||
let user_key = r.key.as_deref(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ref the comment above about TryFrom<Option<&str>> |
||
let user_key: EncString = require!(user_key).parse()?; | ||
|
||
client.internal.initialize_user_crypto_master_key( | ||
master_key, | ||
user_key, | ||
user_key_state, | ||
)?; | ||
} | ||
|
||
client | ||
.internal | ||
.set_login_method(LoginMethod::User(UserLoginMethod::Username { | ||
client_id: "web".to_owned(), | ||
email: input.email.to_owned(), | ||
kdf: input.kdf.to_owned(), | ||
email: input.email.clone(), | ||
kdf: input.kdf.clone(), | ||
})); | ||
|
||
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, | ||
UserKeyState { | ||
private_key, | ||
signing_key: None, | ||
security_state: None, | ||
}, | ||
)?; | ||
} | ||
|
||
Ok(PasswordLoginResponse::process_response(response)) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
๐ญ question:
initialize_crypto_single_org_key
didn't have the side effect of setting the login method? Is this change just to have consistency with the others? I'm a little hesitant because it's SM.