Skip to content

[PM-22861] Account security state #322

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

Merged
merged 68 commits into from
Jul 15, 2025
Merged
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
7fa725f
Implement security context
quexten Jul 3, 2025
0387b44
Cargo fmt
quexten Jul 3, 2025
6c1a57d
Apply clippy fixes
quexten Jul 3, 2025
f4bd1e2
Apply cargo fmt
quexten Jul 3, 2025
ae4590e
Fix build
quexten Jul 3, 2025
c7dc40a
Cleanup
quexten Jul 4, 2025
eaf788e
Update crates/bitwarden-core/src/key_management/crypto.rs
quexten Jul 4, 2025
c7854b4
Merge branch 'km/account-security-version' of github.com:bitwarden/sdโ€ฆ
quexten Jul 4, 2025
2c65c3d
Split init to two functions
quexten Jul 4, 2025
db94fd3
Replace `EncryptionSettingsError::Crypto` with into
quexten Jul 4, 2025
77a2ebe
Fix imports
quexten Jul 4, 2025
14f5d3f
Cargo fmt
quexten Jul 4, 2025
4fea3dc
Cleanup
quexten Jul 4, 2025
46d2202
Cleanup init with enum
quexten Jul 4, 2025
b20e5a4
Cleanup
quexten Jul 4, 2025
566dc47
Fix clippy warnings
quexten Jul 4, 2025
678a9e3
Move security state to core
quexten Jul 4, 2025
b891ec4
Fix version range
quexten Jul 4, 2025
3dd6366
Fix clippy errors
quexten Jul 4, 2025
24ec20d
Fix comment
quexten Jul 4, 2025
6f17cd4
Move serde bytes to workspace
quexten Jul 8, 2025
9922265
Make signing key and security state non-optional
quexten Jul 8, 2025
5f98cab
Remove unused import
quexten Jul 8, 2025
429066a
Add comment
quexten Jul 8, 2025
162e218
Remove unused import
quexten Jul 8, 2025
35677ac
Merge branch 'main' into km/account-security-version
quexten Jul 9, 2025
381b547
Tmp
quexten Jul 9, 2025
d89a17d
Simplify rotation usage, and store security state
quexten Jul 9, 2025
80fe0b7
Fix build
quexten Jul 10, 2025
3885e36
Fix sm build
quexten Jul 10, 2025
e6816f2
Apply clippy fixes
quexten Jul 10, 2025
96548c2
Apply clippy fixes
quexten Jul 10, 2025
620d3c2
Fix test
quexten Jul 10, 2025
959b2e7
Fix tests
quexten Jul 10, 2025
bf269f6
Remove unused import
quexten Jul 10, 2025
5902ea2
Rename to "UserCryptoV2KeysResponse"
quexten Jul 10, 2025
cd8077c
Update crates/bitwarden-core/src/key_management/mod.rs
quexten Jul 11, 2025
e063963
Fix renames
quexten Jul 11, 2025
a0ee4d7
Add error for base64 deserialization
quexten Jul 11, 2025
d10af3d
Clean up key rotation
quexten Jul 11, 2025
5d6ae55
Cleanup
quexten Jul 11, 2025
16c6248
Cleanup get_v2_rotated_account_keys
quexten Jul 11, 2025
d7dea45
Remove arc
quexten Jul 11, 2025
8a00fff
Remove remaining arc references
quexten Jul 11, 2025
4f2b05a
Fix build
quexten Jul 11, 2025
080c6ea
Merge branch 'main' into km/account-security-version
quexten Jul 11, 2025
f76daa8
Re-sort impl after definition
quexten Jul 11, 2025
9e44be8
Update comment
quexten Jul 11, 2025
2c64b9c
Remove json schema
quexten Jul 11, 2025
6e85d6b
Merge branch 'km/account-security-version' of github.com:bitwarden/sdโ€ฆ
quexten Jul 11, 2025
35821d9
Clarify comment
quexten Jul 11, 2025
d01b953
Add test for v1 user trying to rotate using v2 rotation method
quexten Jul 11, 2025
e3dea12
Cargo fmt
quexten Jul 14, 2025
da0a0d8
Extract individual user crypto state values into UserKeyState struct
quexten Jul 14, 2025
4bc1214
Fix build
quexten Jul 14, 2025
7c4f562
Swap to uuid
quexten Jul 14, 2025
ee78cb7
Move impl to struct
quexten Jul 14, 2025
f4007c1
Clean up imports
quexten Jul 14, 2025
1e25d0e
Clean up imports
quexten Jul 14, 2025
17758a4
Fix build
quexten Jul 14, 2025
a85f84f
Add test vectors and tests
quexten Jul 14, 2025
932f9b8
Add comment for signature keys on registration
quexten Jul 14, 2025
2511017
Lift up use
quexten Jul 14, 2025
8ce7e26
Cargo fmt
quexten Jul 14, 2025
95827df
Move stateful crypto error to core crate
quexten Jul 14, 2025
6fa7eff
Fix build on non-internal feature flag
quexten Jul 14, 2025
dfaaca9
Filter import to internal feature only
quexten Jul 14, 2025
736f744
Merge branch 'main' into km/account-security-version
quexten Jul 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -57,6 +57,7 @@ reqwest = { version = ">=0.12.5, <0.13", features = [
], default-features = false }
schemars = { version = ">=0.8.9, <0.9", features = ["uuid1", "chrono"] }
serde = { version = ">=1.0, <2.0", features = ["derive"] }
serde_bytes = { version = ">=0.11.17, <0.12.0" }
serde_json = ">=1.0.96, <2.0"
serde_qs = ">=0.12.0, <0.16"
serde_repr = ">=0.1.12, <0.2"
1 change: 1 addition & 0 deletions crates/bitwarden-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -45,6 +45,7 @@ rand = ">=0.8.5, <0.9"
reqwest = { workspace = true }
schemars = { workspace = true }
serde = { workspace = true }
serde_bytes = { workspace = true }
serde_json = { workspace = true }
serde_qs = { workspace = true }
serde_repr = { workspace = true }
30 changes: 25 additions & 5 deletions crates/bitwarden-core/src/auth/auth_request.rs
Original file line number Diff line number Diff line change
@@ -115,9 +115,12 @@ mod tests {
use bitwarden_crypto::{BitwardenLegacyKeyBytes, Kdf, MasterKey, SpkiPublicKeyBytes};

use super::*;
use crate::key_management::{
crypto::{AuthRequestMethod, InitUserCryptoMethod, InitUserCryptoRequest},
SymmetricKeyId,
use crate::{
client::internal::UserKeyState,
key_management::{
crypto::{AuthRequestMethod, InitUserCryptoMethod, InitUserCryptoRequest},
SymmetricKeyId,
},
};

#[test]
@@ -164,7 +167,15 @@ mod tests {
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
.internal
.initialize_user_crypto_master_key(master_key, user_key, private_key, None)
.initialize_user_crypto_master_key(
master_key,
user_key,
UserKeyState {
private_key,
signing_key: None,
security_state: None,
},
)
.unwrap();

let public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvyLRDUwXB4BfQ507D4meFPmwn5zwy3IqTPJO4plrrhnclWahXa240BzyFW9gHgYu+Jrgms5xBfRTBMcEsqqNm7+JpB6C1B6yvnik0DpJgWQw1rwvy4SUYidpR/AWbQi47n/hvnmzI/sQxGddVfvWu1iTKOlf5blbKYAXnUE5DZBGnrWfacNXwRRdtP06tFB0LwDgw+91CeLSJ9py6dm1qX5JIxoO8StJOQl65goLCdrTWlox+0Jh4xFUfCkb+s3px+OhSCzJbvG/hlrSRcUz5GnwlCEyF3v5lfUtV96MJD+78d8pmH6CfFAp2wxKRAbGdk+JccJYO6y6oIXd3Fm7twIDAQAB";
@@ -232,7 +243,15 @@ mod tests {

existing_device
.internal
.initialize_user_crypto_master_key(master_key, user_key, private_key.clone(), None)
.initialize_user_crypto_master_key(
master_key,
user_key,
UserKeyState {
private_key: private_key.clone(),
signing_key: None,
security_state: None,
},
)
.unwrap();

// Initialize a new device which will request to be logged in
@@ -251,6 +270,7 @@ mod tests {
email: email.to_owned(),
private_key,
signing_key: None,
security_state: None,
method: InitUserCryptoMethod::AuthRequest {
request_private_key: auth_req.private_key,
method: AuthRequestMethod::UserKey {
9 changes: 6 additions & 3 deletions crates/bitwarden-core/src/auth/login/api_key.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
login::{response::two_factor::TwoFactorProviders, LoginError, PasswordLoginResponse},
JwtToken,
},
client::{LoginMethod, UserLoginMethod},
client::{internal::UserKeyState, LoginMethod, UserLoginMethod},
require, Client,
};

@@ -54,8 +54,11 @@
client.internal.initialize_user_crypto_master_key(
master_key,
user_key,
private_key,
None,
UserKeyState {
private_key,
signing_key: None,
security_state: None,
},

Check warning on line 61 in crates/bitwarden-core/src/auth/login/api_key.rs

Codecov / codecov/patch

crates/bitwarden-core/src/auth/login/api_key.rs#L57-L61

Added lines #L57 - L61 were not covered by tests
)?;
}

1 change: 1 addition & 0 deletions crates/bitwarden-core/src/auth/login/auth_request.rs
Original file line number Diff line number Diff line change
@@ -121,6 +121,7 @@
email: auth_req.email,
private_key: require!(r.private_key).parse()?,
signing_key: None,
security_state: None,

Check warning on line 124 in crates/bitwarden-core/src/auth/login/auth_request.rs

Codecov / codecov/patch

crates/bitwarden-core/src/auth/login/auth_request.rs#L124

Added line #L124 was not covered by tests
method: InitUserCryptoMethod::AuthRequest {
request_private_key: auth_req.private_key,
method,
12 changes: 9 additions & 3 deletions crates/bitwarden-core/src/auth/login/password.rs
Original file line number Diff line number Diff line change
@@ -23,7 +23,10 @@
) -> Result<PasswordLoginResponse, LoginError> {
use bitwarden_crypto::{EncString, HashPurpose, MasterKey};

use crate::{client::UserLoginMethod, require};
use crate::{
client::{internal::UserKeyState, UserLoginMethod},
require,
};

info!("password logging in");

@@ -53,8 +56,11 @@
client.internal.initialize_user_crypto_master_key(
master_key,
user_key,
private_key,
None,
UserKeyState {
private_key,
signing_key: None,
security_state: None,
},

Check warning on line 63 in crates/bitwarden-core/src/auth/login/password.rs

Codecov / codecov/patch

crates/bitwarden-core/src/auth/login/password.rs#L59-L63

Added lines #L59 - L63 were not covered by tests
)?;
}

25 changes: 17 additions & 8 deletions crates/bitwarden-core/src/auth/password/validate.rs
Original file line number Diff line number Diff line change
@@ -80,9 +80,12 @@ pub(crate) fn validate_password_user_key(

#[cfg(test)]
mod tests {
use bitwarden_crypto::Kdf;
use bitwarden_crypto::{EncString, Kdf};

use crate::auth::password::{validate::validate_password_user_key, validate_password};
use crate::{
auth::password::{validate::validate_password_user_key, validate_password},
client::internal::UserKeyState,
};

#[test]
fn test_validate_password() {
@@ -135,16 +138,19 @@ mod tests {

let master_key = MasterKey::derive(password, email, &kdf).unwrap();

let user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
let user_key: EncString = "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
.internal
.initialize_user_crypto_master_key(
master_key,
user_key.parse().unwrap(),
private_key,
None,
user_key.clone(),
UserKeyState {
private_key,
signing_key: None,
security_state: None,
},
)
.unwrap();

@@ -191,8 +197,11 @@ mod tests {
.initialize_user_crypto_master_key(
master_key,
user_key.parse().unwrap(),
private_key,
None,
UserKeyState {
private_key,
signing_key: None,
security_state: None,
},
)
.unwrap();

9 changes: 6 additions & 3 deletions crates/bitwarden-core/src/auth/pin.rs
Original file line number Diff line number Diff line change
@@ -49,7 +49,7 @@ mod tests {
use bitwarden_crypto::{Kdf, MasterKey};

use super::*;
use crate::client::{Client, LoginMethod, UserLoginMethod};
use crate::client::{internal::UserKeyState, Client, LoginMethod, UserLoginMethod};

fn init_client() -> Client {
let client = Client::new(None);
@@ -78,8 +78,11 @@ mod tests {
.initialize_user_crypto_master_key(
master_key,
user_key.parse().unwrap(),
private_key,
None,
UserKeyState {
private_key,
signing_key: None,
security_state: None,
},
)
.unwrap();

16 changes: 11 additions & 5 deletions crates/bitwarden-core/src/auth/tde.rs
Original file line number Diff line number Diff line change
@@ -4,7 +4,10 @@
TrustDeviceResponse, UnsignedSharedKey, UserKey,
};

use crate::{client::encryption_settings::EncryptionSettingsError, Client};
use crate::{
client::{encryption_settings::EncryptionSettingsError, internal::UserKeyState},
Client,
};

/// This function generates a new user key and key pair, initializes the client's crypto with the
/// generated user key, and encrypts the user key with the organization public key for admin
@@ -41,10 +44,13 @@
));
client.internal.initialize_user_crypto_decrypted_key(
user_key.0,
key_pair.private.clone(),
// Note: Signing keys are not supported on registration yet. This needs to be changed as
// soon as registration is supported.
None,
UserKeyState {
private_key: key_pair.private.clone(),
// TODO (https://bitwarden.atlassian.net/browse/PM-21771) Signing keys are not supported on registration yet. This needs to be changed as
// soon as registration is supported.
signing_key: None,
security_state: None,
},

Check warning on line 53 in crates/bitwarden-core/src/auth/tde.rs

Codecov / codecov/patch

crates/bitwarden-core/src/auth/tde.rs#L47-L53

Added lines #L47 - L53 were not covered by tests
)?;

Ok(RegisterTdeKeyResponse {
2 changes: 2 additions & 0 deletions crates/bitwarden-core/src/client/client.rs
Original file line number Diff line number Diff line change
@@ -105,6 +105,8 @@ impl Client {
external_client,
key_store: KeyStore::default(),
#[cfg(feature = "internal")]
security_state: RwLock::new(None),
#[cfg(feature = "internal")]
repository_map: StateRegistry::new(),
}),
}
Loading