Skip to content

Commit d0e08f0

Browse files
committed
Add v2 encryption init
1 parent c4cbe47 commit d0e08f0

File tree

2 files changed

+81
-35
lines changed

2 files changed

+81
-35
lines changed

crates/bitwarden-core/src/client/encryption_settings.rs

Lines changed: 78 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use thiserror::Error;
88
#[cfg(any(feature = "internal", feature = "secrets"))]
99
use uuid::Uuid;
1010

11+
use bitwarden_crypto::CryptoError;
12+
1113
#[cfg(any(feature = "internal", feature = "secrets"))]
1214
use crate::key_management::{KeyIds, SymmetricKeyId};
1315
use crate::{error::UserIdAlreadySetError, MissingPrivateKeyError, VaultLockedError};
@@ -28,6 +30,12 @@ pub enum EncryptionSettingsError {
2830
#[error("Invalid private key")]
2931
InvalidPrivateKey,
3032

33+
#[error("Invalid signing key")]
34+
InvalidSigningKey,
35+
36+
#[error("Invalid security state")]
37+
InvalidSecurityState,
38+
3139
#[error(transparent)]
3240
MissingPrivateKey(#[from] MissingPrivateKeyError),
3341

@@ -48,51 +56,86 @@ impl EncryptionSettings {
4856
user_key: SymmetricCryptoKey,
4957
private_key: EncString,
5058
signing_key: Option<EncString>,
51-
_security_state: Option<SignedSecurityState>,
59+
security_state: Option<SignedSecurityState>,
5260
store: &KeyStore<KeyIds>,
5361
) -> Result<(), EncryptionSettingsError> {
54-
use bitwarden_crypto::{
55-
AsymmetricCryptoKey, CoseSerializable, CryptoError, KeyDecryptable, SigningKey,
56-
};
62+
use bitwarden_crypto::{AsymmetricCryptoKey, KeyDecryptable};
5763
use log::warn;
5864

59-
use crate::key_management::{AsymmetricKeyId, SigningKeyId, SymmetricKeyId};
60-
61-
let private_key = {
62-
let dec: Vec<u8> = private_key.decrypt_with_key(&user_key)?;
63-
64-
// FIXME: [PM-11690] - Temporarily ignore invalid private keys until we have a recovery
65-
// process in place.
66-
AsymmetricCryptoKey::from_der(&dec)
67-
.map_err(|_| {
68-
warn!("Invalid private key");
69-
})
70-
.ok()
65+
use crate::key_management::{AsymmetricKeyId, SymmetricKeyId};
7166

72-
// Some(
73-
// AsymmetricCryptoKey::from_der(&dec)
74-
// .map_err(|_| EncryptionSettingsError::InvalidPrivateKey)?,
75-
// )
67+
// This is an all-or-nothing check. The server cannot pretend a signing key or security state to be missing, because they are *always* present when the
68+
// user key is an XChaCha20Poly1305Key. Thus, the server or network cannot lie about the presence of these, because otherwise the entire user account will
69+
// fail to decrypt.
70+
let is_v2_user = if let SymmetricCryptoKey::XChaCha20Poly1305Key(_) = user_key {
71+
true
72+
} else {
73+
false
7674
};
77-
let signing_key = signing_key
78-
.map(|key| {
79-
let dec: Vec<u8> = key.decrypt_with_key(&user_key)?;
80-
SigningKey::from_cose(dec.as_slice()).map_err(Into::<CryptoError>::into)
81-
})
82-
.transpose()?;
8375

84-
// FIXME: [PM-18098] When this is part of crypto we won't need to use deprecated methods
85-
#[allow(deprecated)]
86-
{
87-
let mut ctx = store.context_mut();
88-
ctx.set_symmetric_key(SymmetricKeyId::User, user_key)?;
89-
if let Some(private_key) = private_key {
76+
if is_v2_user {
77+
// For v2 users, we mandate the signing key and security state to be present
78+
// The private key must also be valid.
79+
80+
use bitwarden_crypto::{security_state::SecurityState, CoseSerializable, SigningKey};
81+
82+
// Both of these are required for v2 users
83+
let signing_key = signing_key.ok_or(EncryptionSettingsError::Crypto(
84+
CryptoError::SecurityDowngrade("Signing key is required for v2 users".to_string()),
85+
))?;
86+
let security_state = security_state.ok_or(EncryptionSettingsError::Crypto(
87+
CryptoError::SecurityDowngrade(
88+
"Security state is required for v2 users".to_string(),
89+
),
90+
))?;
91+
92+
// Everything MUST decrypt.
93+
let signing_key: Vec<u8> = signing_key.decrypt_with_key(&user_key)?;
94+
let signing_key = SigningKey::from_cose(&signing_key)
95+
.map_err(|_| EncryptionSettingsError::InvalidSigningKey)?;
96+
let private_key: Vec<u8> = private_key.decrypt_with_key(&user_key)?;
97+
let private_key = AsymmetricCryptoKey::from_der(&private_key)
98+
.map_err(|_| EncryptionSettingsError::InvalidPrivateKey)?;
99+
let _security_state: SecurityState = security_state
100+
.verify_and_unwrap(&signing_key.to_verifying_key())
101+
.map_err(|_| EncryptionSettingsError::InvalidSecurityState)?;
102+
103+
#[allow(deprecated)]
104+
{
105+
use crate::key_management::SigningKeyId;
106+
107+
let mut ctx = store.context_mut();
108+
ctx.set_symmetric_key(SymmetricKeyId::User, user_key)?;
90109
ctx.set_asymmetric_key(AsymmetricKeyId::UserPrivateKey, private_key)?;
91-
}
92-
93-
if let Some(signing_key) = signing_key {
94110
ctx.set_signing_key(SigningKeyId::UserSigningKey, signing_key)?;
95111
}
112+
} else {
113+
let private_key = {
114+
let dec: Vec<u8> = private_key.decrypt_with_key(&user_key)?;
115+
116+
// FIXME: [PM-11690] - Temporarily ignore invalid private keys until we have a recovery
117+
// process in place.
118+
AsymmetricCryptoKey::from_der(&dec)
119+
.map_err(|_| {
120+
warn!("Invalid private key");
121+
})
122+
.ok()
123+
124+
// Some(
125+
// AsymmetricCryptoKey::from_der(&dec)
126+
// .map_err(|_| EncryptionSettingsError::InvalidPrivateKey)?,
127+
// )
128+
};
129+
130+
// FIXME: [PM-18098] When this is part of crypto we won't need to use deprecated methods
131+
#[allow(deprecated)]
132+
{
133+
let mut ctx = store.context_mut();
134+
ctx.set_symmetric_key(SymmetricKeyId::User, user_key)?;
135+
if let Some(private_key) = private_key {
136+
ctx.set_asymmetric_key(AsymmetricKeyId::UserPrivateKey, private_key)?;
137+
}
138+
}
96139
}
97140

98141
Ok(())

crates/bitwarden-crypto/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ pub enum CryptoError {
6969

7070
#[error("Uninitialized error")]
7171
UninitializedError,
72+
73+
#[error("Attempted security downgrade {0}")]
74+
SecurityDowngrade(String),
7275
}
7376

7477
#[derive(Debug, Error)]

0 commit comments

Comments
 (0)