Skip to content

[PM-18102] Use opaque autogenerated local key ids #274

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

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions crates/bitwarden-core/src/key_management/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ key_ids! {
User,
Organization(uuid::Uuid),
#[local]
Local(&'static str),
Local(uuid::Uuid),
}

#[asymmetric]
pub enum AsymmetricKeyId {
UserPrivateKey,
#[local]
Local(&'static str),
Local(uuid::Uuid),
}

pub KeyIds => SymmetricKeyId, AsymmetricKeyId;
Expand Down
35 changes: 21 additions & 14 deletions crates/bitwarden-crypto/src/store/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,14 @@ use crate::{
/// # #[symmetric]
/// # pub enum SymmKeyId {
/// # User,
/// # Local(&'static str),
/// # #[local]
/// # Local(uuid::Uuid),
/// # }
/// # #[asymmetric]
/// # pub enum AsymmKeyId {
/// # UserPrivate,
/// # #[local]
/// # Local(uuid::Uuid),
/// # }
/// # pub Ids => SymmKeyId, AsymmKeyId;
/// # }
Expand All @@ -51,11 +54,10 @@ use crate::{
/// # }
/// # }
///
/// const LOCAL_KEY: SymmKeyId = SymmKeyId::Local("local_key_id");
///
/// impl Encryptable<Ids, SymmKeyId, EncString> for Data {
/// fn encrypt(&self, ctx: &mut KeyStoreContext<Ids>, key: SymmKeyId) -> Result<EncString, CryptoError> {
/// let local_key_id = ctx.unwrap_symmetric_key(key, LOCAL_KEY, &self.key)?;
/// let local_key_id = ctx.unwrap_symmetric_key(key, &self.key)?;
/// self.name.encrypt(ctx, local_key_id)
/// }
/// }
Expand Down Expand Up @@ -133,15 +135,13 @@ impl<Ids: KeyIds> KeyStoreContext<'_, Ids> {
///
/// * `encryption_key` - The key id used to decrypt the `encrypted_key`. It must already exist
/// in the context
/// * `new_key_id` - The key id where the decrypted key will be stored. If it already exists, it
/// will be overwritten
/// * `encrypted_key` - The key to decrypt
pub fn unwrap_symmetric_key(
&mut self,
encryption_key: Ids::Symmetric,
new_key_id: Ids::Symmetric,
encrypted_key: &EncString,
) -> Result<Ids::Symmetric> {
let new_key_id = Ids::Symmetric::new_local();
let mut new_key_material =
self.decrypt_data_with_symmetric_key(encryption_key, encrypted_key)?;

Expand Down Expand Up @@ -245,7 +245,8 @@ impl<Ids: KeyIds> KeyStoreContext<'_, Ids> {
}

/// Generate a new random symmetric key and store it in the context
pub fn generate_symmetric_key(&mut self, key_id: Ids::Symmetric) -> Result<Ids::Symmetric> {
pub fn generate_symmetric_key(&mut self) -> Result<Ids::Symmetric> {
let key_id = Ids::Symmetric::new_local();
let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
#[allow(deprecated)]
self.set_symmetric_key(key_id, key)?;
Expand All @@ -258,11 +259,11 @@ impl<Ids: KeyIds> KeyStoreContext<'_, Ids> {
/// Bitwarden `clients` repository.
pub fn derive_shareable_key(
&mut self,
key_id: Ids::Symmetric,
secret: Zeroizing<[u8; 16]>,
name: &str,
info: Option<&str>,
) -> Result<Ids::Symmetric> {
let key_id = Ids::Symmetric::new_local();
#[allow(deprecated)]
self.set_symmetric_key(
key_id,
Expand Down Expand Up @@ -325,6 +326,13 @@ impl<Ids: KeyIds> KeyStoreContext<'_, Ids> {
Ok(())
}

/// Add a new symmetric key to the local context, returning a new unique identifier for it.
pub fn add_local_symmetric_key(&mut self, key: SymmetricCryptoKey) -> Result<Ids::Symmetric> {
let key_id = Ids::Symmetric::new_local();
self.local_symmetric_keys.upsert(key_id, key);
Ok(key_id)
}

#[deprecated(note = "This function should ideally never be used outside this crate")]
#[allow(missing_docs)]
pub fn set_asymmetric_key(
Expand Down Expand Up @@ -383,6 +391,8 @@ impl<Ids: KeyIds> KeyStoreContext<'_, Ids> {
#[cfg(test)]
#[allow(deprecated)]
mod tests {
use uuid::Uuid;

use crate::{
store::{tests::DataView, KeyStore},
traits::tests::{TestIds, TestSymmKey},
Expand Down Expand Up @@ -416,15 +426,15 @@ mod tests {
let mut ctx = store.context();

// Generate and insert a key
let key_1_id = TestSymmKey::C(1);
let key_1_id = TestSymmKey::C(Uuid::new_v4());
let key_1 = SymmetricCryptoKey::make_aes256_cbc_hmac_key();

ctx.set_symmetric_key(key_1_id, key_1.clone()).unwrap();

assert!(ctx.has_symmetric_key(key_1_id));

// Generate and insert a new key
let key_2_id = TestSymmKey::C(2);
let key_2_id = TestSymmKey::C(Uuid::new_v4());
let key_2 = SymmetricCryptoKey::make_aes256_cbc_hmac_key();

ctx.set_symmetric_key(key_2_id, key_2.clone()).unwrap();
Expand All @@ -435,10 +445,7 @@ mod tests {
let key_2_enc = ctx.wrap_symmetric_key(key_1_id, key_2_id).unwrap();

// Decrypt the new key with the old key in a different identifier
let new_key_id = TestSymmKey::C(3);

ctx.unwrap_symmetric_key(key_1_id, new_key_id, &key_2_enc)
.unwrap();
let new_key_id = ctx.unwrap_symmetric_key(key_1_id, &key_2_enc).unwrap();

// Now `key_2_id` and `new_key_id` contain the same key, so we should be able to encrypt
// with one and decrypt with the other
Expand Down
4 changes: 3 additions & 1 deletion crates/bitwarden-crypto/src/store/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,13 @@ pub use context::KeyStoreContext;
/// pub enum SymmKeyId {
/// User,
/// #[local]
/// Local(&'static str)
/// Local(uuid::Uuid),
/// }
/// #[asymmetric]
/// pub enum AsymmKeyId {
/// UserPrivate,
/// #[local]
/// Local(uuid::Uuid),
/// }
/// pub Ids => SymmKeyId, AsymmKeyId;
/// }
Expand Down
22 changes: 19 additions & 3 deletions crates/bitwarden-crypto/src/traits/key_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ pub trait KeyId:
/// Returns whether the key is local to the current context or shared globally by the
/// key store. See [crate::store::KeyStoreContext] for more information.
fn is_local(&self) -> bool;

/// Creates a new unique local key identifier.
fn new_local() -> Self;
}

/// Represents a set of all the key identifiers that need to be defined to use a key store.
Expand All @@ -45,12 +48,14 @@ pub trait KeyIds {
/// User,
/// Org(uuid::Uuid),
/// #[local]
/// Local(&'static str),
/// Local(uuid::Uuid),
/// }
///
/// #[asymmetric]
/// pub enum AsymmKeyId {
/// PrivateKey,
/// #[local]
/// Local(uuid::Uuid),
/// }
/// pub Ids => SymmKeyId, AsymmKeyId;
/// }
Expand Down Expand Up @@ -85,6 +90,12 @@ macro_rules! key_ids {
key_ids!(@variant_value $( $variant_tag )? ),
)* }
}

fn new_local() -> Self {
$(
{ key_ids!(@new_local $variant $( $variant_tag )? ) }
)*
}
}
)+

Expand All @@ -104,10 +115,15 @@ macro_rules! key_ids {

( @variant_value local ) => { true };
( @variant_value ) => { false };

( @new_local $variant:ident local ) => { Self::$variant(uuid::Uuid::new_v4()) };
( @new_local $variant:ident ) => {{}};
}

#[cfg(test)]
pub(crate) mod tests {
use uuid::Uuid;

use crate::{
traits::tests::{TestAsymmKey, TestSymmKey},
KeyId,
Expand All @@ -117,10 +133,10 @@ pub(crate) mod tests {
fn test_local() {
assert!(!TestSymmKey::A(0).is_local());
assert!(!TestSymmKey::B((4, 10)).is_local());
assert!(TestSymmKey::C(8).is_local());
assert!(TestSymmKey::C(Uuid::new_v4()).is_local());

assert!(!TestAsymmKey::A(0).is_local());
assert!(!TestAsymmKey::B.is_local());
assert!(TestAsymmKey::C("test").is_local());
assert!(TestAsymmKey::C(Uuid::new_v4()).is_local());
}
}
4 changes: 2 additions & 2 deletions crates/bitwarden-crypto/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ pub(crate) mod tests {
B((u8, u8)),

#[local]
C(u8),
C(uuid::Uuid),
}

#[asymmetric]
pub enum TestAsymmKey {
A(u8),
B,
#[local]
C(&'static str),
C(uuid::Uuid),
}

pub TestIds => TestSymmKey, TestAsymmKey;
Expand Down
4 changes: 1 addition & 3 deletions crates/bitwarden-send/src/send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,6 @@ pub struct SendListView {
pub expiration_date: Option<DateTime<Utc>>,
}

const SEND_KEY: SymmetricKeyId = SymmetricKeyId::Local("send_key");

impl Send {
#[allow(missing_docs)]
pub fn get_key(
Expand All @@ -164,7 +162,7 @@ impl Send {
key: &[u8],
) -> Result<SymmetricKeyId, CryptoError> {
let key = Zeroizing::new(key.try_into().map_err(|_| CryptoError::InvalidKeyLen)?);
ctx.derive_shareable_key(SEND_KEY, key, "send", Some("send"))
ctx.derive_shareable_key(key, "send", Some("send"))
}
}

Expand Down
6 changes: 2 additions & 4 deletions crates/bitwarden-vault/src/cipher/attachment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ pub struct AttachmentFileView<'a> {
pub attachment: AttachmentView,
pub contents: &'a [u8],
}
const ATTACHMENT_KEY: SymmetricKeyId = SymmetricKeyId::Local("attachment_key");

impl IdentifyKey<SymmetricKeyId> for AttachmentFileView<'_> {
fn key_identifier(&self) -> SymmetricKeyId {
Expand All @@ -90,7 +89,7 @@ impl Encryptable<KeyIds, SymmetricKeyId, AttachmentEncryptResult> for Attachment

// Because this is a new attachment, we have to generate a key for it, encrypt the contents
// with it, and then encrypt the key with the cipher key
let attachment_key = ctx.generate_symmetric_key(ATTACHMENT_KEY)?;
let attachment_key = ctx.generate_symmetric_key()?;
let encrypted_contents = self.contents.encrypt(ctx, attachment_key)?;
attachment.key = Some(ctx.wrap_symmetric_key(ciphers_key, attachment_key)?);

Expand Down Expand Up @@ -127,8 +126,7 @@ impl Decryptable<KeyIds, SymmetricKeyId, Vec<u8>> for AttachmentFile {

// Version 2 or 3, `AttachmentKey` or `CipherKey(AttachmentKey)`
if let Some(attachment_key) = &self.attachment.key {
let content_key =
ctx.unwrap_symmetric_key(ciphers_key, ATTACHMENT_KEY, attachment_key)?;
let content_key = ctx.unwrap_symmetric_key(ciphers_key, attachment_key)?;
self.contents.decrypt(ctx, content_key)
} else {
// Legacy attachment version 1, use user/org key
Expand Down
22 changes: 6 additions & 16 deletions crates/bitwarden-vault/src/cipher/cipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,9 +342,8 @@ impl Cipher {
key: SymmetricKeyId,
ciphers_key: &Option<EncString>,
) -> Result<SymmetricKeyId, CryptoError> {
const CIPHER_KEY: SymmetricKeyId = SymmetricKeyId::Local("cipher_key");
match ciphers_key {
Some(ciphers_key) => ctx.unwrap_symmetric_key(key, CIPHER_KEY, ciphers_key),
Some(ciphers_key) => ctx.unwrap_symmetric_key(key, ciphers_key),
None => Ok(key),
}
}
Expand Down Expand Up @@ -476,9 +475,7 @@ impl CipherView {
) -> Result<(), CryptoError> {
let old_ciphers_key = Cipher::decrypt_cipher_key(ctx, key, &self.key)?;

const NEW_KEY: SymmetricKeyId = SymmetricKeyId::Local("new_cipher_key");

let new_key = ctx.generate_symmetric_key(NEW_KEY)?;
let new_key = ctx.generate_symmetric_key()?;

self.reencrypt_attachment_keys(ctx, old_ciphers_key, new_key)?;
self.reencrypt_fido2_credentials(ctx, old_ciphers_key, new_key)?;
Expand Down Expand Up @@ -963,9 +960,8 @@ mod tests {

let mut original_cipher = generate_cipher();
{
const CIPHER_KEY: SymmetricKeyId = SymmetricKeyId::Local("test_cipher_key");
let mut ctx = key_store.context();
let cipher_key = ctx.generate_symmetric_key(CIPHER_KEY).unwrap();
let cipher_key = ctx.generate_symmetric_key().unwrap();

original_cipher.key = Some(
ctx.wrap_symmetric_key(SymmetricKeyId::User, cipher_key)
Expand Down Expand Up @@ -1087,9 +1083,7 @@ mod tests {
// Attachment has a key that is encrypted with the user key, as the cipher has no key itself
let (attachment_key_enc, attachment_key_val) = {
let mut ctx = key_store.context();
let attachment_key = ctx
.generate_symmetric_key(SymmetricKeyId::Local("test_attachment_key"))
.unwrap();
let attachment_key = ctx.generate_symmetric_key().unwrap();
let attachment_key_enc = ctx
.wrap_symmetric_key(SymmetricKeyId::User, attachment_key)
.unwrap();
Expand Down Expand Up @@ -1154,17 +1148,13 @@ mod tests {

let mut ctx = key_store.context();

let cipher_key = ctx
.generate_symmetric_key(SymmetricKeyId::Local("test_cipher_key"))
.unwrap();
let cipher_key = ctx.generate_symmetric_key().unwrap();
let cipher_key_enc = ctx
.wrap_symmetric_key(SymmetricKeyId::User, cipher_key)
.unwrap();

// Attachment has a key that is encrypted with the cipher key
let attachment_key = ctx
.generate_symmetric_key(SymmetricKeyId::Local("test_attachment_key"))
.unwrap();
let attachment_key = ctx.generate_symmetric_key().unwrap();
let attachment_key_enc = ctx.wrap_symmetric_key(cipher_key, attachment_key).unwrap();

let mut cipher = generate_cipher();
Expand Down
Loading
Loading