-
Notifications
You must be signed in to change notification settings - Fork 50
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
Add sensitive types to SymmetricCryptoKey #672
Conversation
pub fn decode_base64<T: base64::Engine>(self, engine: T) -> Result<SensitiveVec, CryptoError> { | ||
// Preallocate a Vec with the necessary capacity | ||
let len = base64::decoded_len_estimate(self.value.len()); | ||
let mut value = SensitiveVec::new(Box::new(Vec::with_capacity(len))); |
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.
Do we know what happens when a vector grows, will it make a copy of the data or have pointers to the new locations.
We should probably provide warnings for this behaviour.
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.
When the Vec grows over capacity, it's going to make a new allocation that is bigger than the current one, copy the data over and then dealloc the old allocation. Note that the deallocation of the older data won't call the Vec's zeroize so it's definitely problematic. The same will happen with Strings as they are Vecs under the hood, but I think it's more unlikely for a string to be appended to.
I think adding warnings to those types sounds like a good idea yeah!
// IMPORTANT: This should not be used outside of test code | ||
// Note that we can't just mark it with #[cfg(test)] because that only applies | ||
// when testing this crate, not when testing other crates that depend on it. | ||
// By at least limiting it to &'static str we should be able to avoid accidental usages |
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.
Would exposing a specific feature and only using that feature in tests work?
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.
Hmm I think it might, we could even enable the feature in dev-dependencies too to avoid requiring it when calling cargo test
. I'll give it a try!
No New Or Fixed Issues Found |
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.
Looks good, some open questions on expectations for inputs/outputs.
/// Uniffi doesn't seem to be generating the SensitiveString unless it's being used by | ||
/// a record somewhere. This is a workaround to make sure the type is generated. | ||
#[derive(uniffi::Record)] | ||
struct SupportSensitiveString { | ||
sensitive_string: SensitiveString, | ||
} |
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.
Let's open an issue for this.
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.
Opened PM-7214
attachment.key = Some( | ||
attachment_key | ||
.to_vec() | ||
.expose() |
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.
Exposing a sensitive value for encrypting seems silly. Could we implement encrypt for sensitive values?
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.
We currently implement encrypt for sensitive values, the problem is for Vec
s we implement encrypt on &[u8]
and rely on rust auto deref to coerce the Vec to a slice, but that will not happen for Sensitive.
This is going to require either updating all the Encrypt impls to work only on Vec or SensitiveVec, or adding a specific impl only for Sensitive, which might have to be done carefully to avoid conflicts with the current blanket impl.
I think this is best left for a separate PR to avoid bloating this one, though.
@@ -67,7 +67,7 @@ impl MasterKey { | |||
let stretched_key = stretch_kdf_key(&self.0)?; | |||
|
|||
EncString::encrypt_aes256_hmac( | |||
user_key.to_vec().as_slice(), | |||
user_key.to_vec().expose(), |
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.
Should we look into if the encrypt methods should accept sensitive values?
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.
Definitely, but it needs to be updated at the same time as the KeyEncryptable impls, as they use this function and we don't want to be boxing/unboxing all the time.
pub fn to_vec(&self) -> Zeroizing<Vec<u8>> { | ||
let mut buf = Vec::with_capacity(self.total_len()); | ||
pub fn to_vec(&self) -> SensitiveVec { | ||
let mut buf = SensitiveVec::new(Box::new(Vec::with_capacity(self.total_len()))); |
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.
Maybe add a comment why it's important to allocate the full length. It could be seen as an optimization method otherwise.
let mut buf = SensitiveVec::new(Box::new(Vec::with_capacity(self.total_len()))); | |
// Prevent accidental copies by allocating the full size | |
let mut buf = SensitiveVec::new(Box::new(Vec::with_capacity(self.total_len()))); |
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.
Done
crates/bitwarden/src/auth/renew.rs
Outdated
@@ -63,8 +63,10 @@ pub(crate) async fn renew_token(client: &mut Client) -> Result<()> { | |||
) = (&result, state_file, client.get_encryption_settings()) | |||
{ | |||
if let Some(enc_key) = enc_settings.get_key(&None) { | |||
let state = | |||
ClientState::new(r.access_token.clone(), enc_key.to_base64()); | |||
let state = ClientState::new( |
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.
Should state expect sensitive values directly?
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.
Just checked and it's not too big a diff, changed it
# Conflicts: # crates/bitwarden-crypto/src/keys/master_key.rs
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.
SM state needs to be backwards compatible, did we check if the struct works with old state files?
@@ -15,11 +15,11 @@ const STATE_VERSION: u32 = 1; | |||
pub struct ClientState { | |||
pub(crate) version: u32, | |||
pub(crate) token: String, | |||
pub(crate) encryption_key: String, | |||
pub(crate) encryption_key: SensitiveString, |
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.
Any concerns with backwards compatibility here?
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.
I gave it a test to be sure now and it works yeah. The struct is only used for serialization/deserialization and the sensitive type works the same way as a plain string there.
Type of change
Objective
This is a small subset of #536, which only contains the minimum code to protect the import/export functions on SymmetricCryptoKey. It also enables the from_base64 test on SymmetricCryptoKey as that passes now.
After this PR is merged I'll be expanding the use of Sensitive to other parts of the codebase while adding some extra memory testing checks to validate that it works for them.