Skip to content
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

Merged
merged 8 commits into from
Apr 1, 2024
Merged

Add sensitive types to SymmetricCryptoKey #672

merged 8 commits into from
Apr 1, 2024

Conversation

dani-garcia
Copy link
Member

@dani-garcia dani-garcia commented Mar 20, 2024

Type of change

- [ ] Bug fix
- [ ] New feature development
- [x] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
- [ ] Build/deploy pipeline (DevOps)
- [ ] Other

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.

Copy link

codecov bot commented Mar 20, 2024

Codecov Report

Attention: Patch coverage is 81.72589% with 36 lines in your changes are missing coverage. Please review.

Project coverage is 60.63%. Comparing base (9cd9a15) to head (c57d6ef).

Files Patch % Lines
crates/bitwarden-crypto/src/sensitive/sensitive.rs 90.08% 12 Missing ⚠️
crates/bitwarden-crypto/src/uniffi_support.rs 0.00% 6 Missing ⚠️
crates/bitwarden/src/mobile/crypto.rs 50.00% 5 Missing ⚠️
crates/bitwarden/src/vault/cipher/attachment.rs 16.66% 5 Missing ⚠️
crates/bitwarden-crypto/src/sensitive/decrypted.rs 0.00% 3 Missing ⚠️
crates/bitwarden-uniffi/src/crypto.rs 0.00% 1 Missing ⚠️
crates/bitwarden/src/auth/login/access_token.rs 66.66% 1 Missing ⚠️
crates/bitwarden/src/auth/tde.rs 0.00% 1 Missing ⚠️
crates/bitwarden/src/mobile/client_crypto.rs 0.00% 1 Missing ⚠️
crates/bitwarden/src/secrets_manager/state.rs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #672      +/-   ##
==========================================
+ Coverage   60.41%   60.63%   +0.21%     
==========================================
  Files         171      173       +2     
  Lines       10426    10555     +129     
==========================================
+ Hits         6299     6400     +101     
- Misses       4127     4155      +28     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@dani-garcia dani-garcia marked this pull request as ready for review March 20, 2024 16:42
@dani-garcia dani-garcia requested a review from Hinton March 20, 2024 16:42
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)));
Copy link
Member

@Hinton Hinton Mar 28, 2024

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.

Copy link
Member Author

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!

Comment on lines +136 to +139
// 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
Copy link
Member

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?

Copy link
Member Author

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!

Copy link
Contributor

github-actions bot commented Mar 28, 2024

Logo
Checkmarx One – Scan Summary & Detailscb9bcd93-8686-426f-ac7b-01df7600efc5

No New Or Fixed Issues Found

Copy link
Member

@Hinton Hinton left a 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.

Comment on lines +63 to +68
/// 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,
}
Copy link
Member

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.

Copy link
Member Author

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()
Copy link
Member

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?

Copy link
Member Author

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 Vecs 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.

crates/bitwarden-crypto/src/keys/device_key.rs Outdated Show resolved Hide resolved
@@ -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(),
Copy link
Member

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?

Copy link
Member Author

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())));
Copy link
Member

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.

Suggested change
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())));

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -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(
Copy link
Member

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?

Copy link
Member Author

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

@dani-garcia dani-garcia requested a review from Hinton April 1, 2024 10:11
Copy link
Member

@Hinton Hinton left a 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,
Copy link
Member

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?

Copy link
Member Author

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.

@dani-garcia dani-garcia merged commit 538b9f2 into main Apr 1, 2024
75 checks passed
@dani-garcia dani-garcia deleted the ps/sensitive branch April 1, 2024 14:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants