Skip to content

Commit 812865f

Browse files
quextendani-garcia
andauthored
[PM-22861] Account security state (#322)
## 🎟️ Tracking https://bitwarden.atlassian.net/browse/PM-22861 ## 📔 Objective This adds the initial version of account security state to the sdk, on init of the sdk state, and for enrollment to v2 crypto. Account security state is an attestation to a specific version and featureset that is enabled. The version is used to prevent cryptographic downgrades of the account state. This will prevent downgrades in the following ways: - Arbitrary old states cannot be created by the server, because the state is signed for a user by the user's signing key, the server can only record entire states - A signed in session will not accept a sync with an older state - Some sign in mechanisms will not accept a sync with an older state (login-with-device 2.0, PRF 2.0, both still to be specified) A security version then is used to lock down specific features. If a feature is to be disabled, then a migrator is needed. After migrating the account not to use the retired feature, the version is incremented and any subsequent syncs with that format included are rejected. This PR stores the account security state on the client for v2 users. However, besides validating that the state is correctly signed, it is not yet used. ## ⏰ Reminders before review - Contributor guidelines followed - All formatters and local linters executed and passed - Written new unit and / or integration tests where applicable - Protected functional changes with optionality (feature flags) - Used internationalization (i18n) for all UI strings - CI builds passed - Communicated to DevOps any deployment requirements - Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team ## 🦮 Reviewer guidelines <!-- Suggested interactions but feel free to use (or not) as you desire! --> - 👍 (`:+1:`) or similar for great changes - 📝 (`:memo:`) or ℹ️ (`:information_source:`) for notes or general info - ❓ (`:question:`) for questions - 🤔 (`:thinking:`) or 💭 (`:thought_balloon:`) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion - 🎨 (`:art:`) for suggestions / improvements - ❌ (`:x:`) or ⚠️ (`:warning:`) for more significant problems or concerns needing attention - 🌱 (`:seedling:`) or ♻️ (`:recycle:`) for future improvements or indications of technical debt - ⛏ (`:pick:`) for minor or nitpick changes --------- Co-authored-by: Daniel García <[email protected]>
1 parent 212e190 commit 812865f

File tree

34 files changed

+1011
-212
lines changed

34 files changed

+1011
-212
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ reqwest = { version = ">=0.12.5, <0.13", features = [
5757
], default-features = false }
5858
schemars = { version = ">=0.8.9, <0.9", features = ["uuid1", "chrono"] }
5959
serde = { version = ">=1.0, <2.0", features = ["derive"] }
60+
serde_bytes = { version = ">=0.11.17, <0.12.0" }
6061
serde_json = ">=1.0.96, <2.0"
6162
serde_qs = ">=0.12.0, <0.16"
6263
serde_repr = ">=0.1.12, <0.2"

crates/bitwarden-core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ rand = ">=0.8.5, <0.9"
4545
reqwest = { workspace = true }
4646
schemars = { workspace = true }
4747
serde = { workspace = true }
48+
serde_bytes = { workspace = true }
4849
serde_json = { workspace = true }
4950
serde_qs = { workspace = true }
5051
serde_repr = { workspace = true }

crates/bitwarden-core/src/auth/auth_request.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,12 @@ mod tests {
115115
use bitwarden_crypto::{BitwardenLegacyKeyBytes, Kdf, MasterKey, SpkiPublicKeyBytes};
116116

117117
use super::*;
118-
use crate::key_management::{
119-
crypto::{AuthRequestMethod, InitUserCryptoMethod, InitUserCryptoRequest},
120-
SymmetricKeyId,
118+
use crate::{
119+
client::internal::UserKeyState,
120+
key_management::{
121+
crypto::{AuthRequestMethod, InitUserCryptoMethod, InitUserCryptoRequest},
122+
SymmetricKeyId,
123+
},
121124
};
122125

123126
#[test]
@@ -164,7 +167,15 @@ mod tests {
164167
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();
165168
client
166169
.internal
167-
.initialize_user_crypto_master_key(master_key, user_key, private_key, None)
170+
.initialize_user_crypto_master_key(
171+
master_key,
172+
user_key,
173+
UserKeyState {
174+
private_key,
175+
signing_key: None,
176+
security_state: None,
177+
},
178+
)
168179
.unwrap();
169180

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

233244
existing_device
234245
.internal
235-
.initialize_user_crypto_master_key(master_key, user_key, private_key.clone(), None)
246+
.initialize_user_crypto_master_key(
247+
master_key,
248+
user_key,
249+
UserKeyState {
250+
private_key: private_key.clone(),
251+
signing_key: None,
252+
security_state: None,
253+
},
254+
)
236255
.unwrap();
237256

238257
// Initialize a new device which will request to be logged in
@@ -251,6 +270,7 @@ mod tests {
251270
email: email.to_owned(),
252271
private_key,
253272
signing_key: None,
273+
security_state: None,
254274
method: InitUserCryptoMethod::AuthRequest {
255275
request_private_key: auth_req.private_key,
256276
method: AuthRequestMethod::UserKey {

crates/bitwarden-core/src/auth/login/api_key.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88
login::{response::two_factor::TwoFactorProviders, LoginError, PasswordLoginResponse},
99
JwtToken,
1010
},
11-
client::{LoginMethod, UserLoginMethod},
11+
client::{internal::UserKeyState, LoginMethod, UserLoginMethod},
1212
require, Client,
1313
};
1414

@@ -54,8 +54,11 @@ pub(crate) async fn login_api_key(
5454
client.internal.initialize_user_crypto_master_key(
5555
master_key,
5656
user_key,
57-
private_key,
58-
None,
57+
UserKeyState {
58+
private_key,
59+
signing_key: None,
60+
security_state: None,
61+
},
5962
)?;
6063
}
6164

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ pub(crate) async fn complete_auth_request(
121121
email: auth_req.email,
122122
private_key: require!(r.private_key).parse()?,
123123
signing_key: None,
124+
security_state: None,
124125
method: InitUserCryptoMethod::AuthRequest {
125126
request_private_key: auth_req.private_key,
126127
method,

crates/bitwarden-core/src/auth/login/password.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ pub(crate) async fn login_password(
2323
) -> Result<PasswordLoginResponse, LoginError> {
2424
use bitwarden_crypto::{EncString, HashPurpose, MasterKey};
2525

26-
use crate::{client::UserLoginMethod, require};
26+
use crate::{
27+
client::{internal::UserKeyState, UserLoginMethod},
28+
require,
29+
};
2730

2831
info!("password logging in");
2932

@@ -53,8 +56,11 @@ pub(crate) async fn login_password(
5356
client.internal.initialize_user_crypto_master_key(
5457
master_key,
5558
user_key,
56-
private_key,
57-
None,
59+
UserKeyState {
60+
private_key,
61+
signing_key: None,
62+
security_state: None,
63+
},
5864
)?;
5965
}
6066

crates/bitwarden-core/src/auth/password/validate.rs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,12 @@ pub(crate) fn validate_password_user_key(
8080

8181
#[cfg(test)]
8282
mod tests {
83-
use bitwarden_crypto::Kdf;
83+
use bitwarden_crypto::{EncString, Kdf};
8484

85-
use crate::auth::password::{validate::validate_password_user_key, validate_password};
85+
use crate::{
86+
auth::password::{validate::validate_password_user_key, validate_password},
87+
client::internal::UserKeyState,
88+
};
8689

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

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

138-
let user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
141+
let user_key: EncString = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap();
139142
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();
140143

141144
client
142145
.internal
143146
.initialize_user_crypto_master_key(
144147
master_key,
145-
user_key.parse().unwrap(),
146-
private_key,
147-
None,
148+
user_key.clone(),
149+
UserKeyState {
150+
private_key,
151+
signing_key: None,
152+
security_state: None,
153+
},
148154
)
149155
.unwrap();
150156

@@ -191,8 +197,11 @@ mod tests {
191197
.initialize_user_crypto_master_key(
192198
master_key,
193199
user_key.parse().unwrap(),
194-
private_key,
195-
None,
200+
UserKeyState {
201+
private_key,
202+
signing_key: None,
203+
security_state: None,
204+
},
196205
)
197206
.unwrap();
198207

crates/bitwarden-core/src/auth/pin.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ mod tests {
4949
use bitwarden_crypto::{Kdf, MasterKey};
5050

5151
use super::*;
52-
use crate::client::{Client, LoginMethod, UserLoginMethod};
52+
use crate::client::{internal::UserKeyState, Client, LoginMethod, UserLoginMethod};
5353

5454
fn init_client() -> Client {
5555
let client = Client::new(None);
@@ -78,8 +78,11 @@ mod tests {
7878
.initialize_user_crypto_master_key(
7979
master_key,
8080
user_key.parse().unwrap(),
81-
private_key,
82-
None,
81+
UserKeyState {
82+
private_key,
83+
signing_key: None,
84+
security_state: None,
85+
},
8386
)
8487
.unwrap();
8588

crates/bitwarden-core/src/auth/tde.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ use bitwarden_crypto::{
44
TrustDeviceResponse, UnsignedSharedKey, UserKey,
55
};
66

7-
use crate::{client::encryption_settings::EncryptionSettingsError, Client};
7+
use crate::{
8+
client::{encryption_settings::EncryptionSettingsError, internal::UserKeyState},
9+
Client,
10+
};
811

912
/// This function generates a new user key and key pair, initializes the client's crypto with the
1013
/// generated user key, and encrypts the user key with the organization public key for admin
@@ -41,10 +44,13 @@ pub(super) fn make_register_tde_keys(
4144
));
4245
client.internal.initialize_user_crypto_decrypted_key(
4346
user_key.0,
44-
key_pair.private.clone(),
45-
// Note: Signing keys are not supported on registration yet. This needs to be changed as
46-
// soon as registration is supported.
47-
None,
47+
UserKeyState {
48+
private_key: key_pair.private.clone(),
49+
// TODO (https://bitwarden.atlassian.net/browse/PM-21771) Signing keys are not supported on registration yet. This needs to be changed as
50+
// soon as registration is supported.
51+
signing_key: None,
52+
security_state: None,
53+
},
4854
)?;
4955

5056
Ok(RegisterTdeKeyResponse {

0 commit comments

Comments
 (0)