use bitwarden_crypto::{EncString, KeyStoreContext};
use key_management::LocalUserDataKeyState;
use thiserror::Error;
use tracing::instrument;
use crate::{
key_management,
key_management::{KeySlotIds, SymmetricKeySlotId},
};
#[derive(Debug, Clone)]
pub(crate) struct WrappedLocalUserDataKey(pub(crate) EncString);
impl WrappedLocalUserDataKey {
#[instrument(skip(ctx), err)]
pub(crate) fn from_context_user_key(
ctx: &mut KeyStoreContext<KeySlotIds>,
) -> Result<Self, LocalUserDataKeyError> {
let wrapped_local_user_data_key = ctx
.wrap_symmetric_key(SymmetricKeySlotId::User, SymmetricKeySlotId::User)
.map_err(|_| LocalUserDataKeyError::EncryptionFailed)?;
Ok(WrappedLocalUserDataKey(wrapped_local_user_data_key))
}
#[instrument(skip(self, ctx), err)]
pub(crate) fn unwrap_to_context(
&self,
ctx: &mut KeyStoreContext<KeySlotIds>,
) -> Result<(), LocalUserDataKeyError> {
let local_id = ctx
.unwrap_symmetric_key(SymmetricKeySlotId::User, &self.0)
.map_err(|_| LocalUserDataKeyError::DecryptionFailed)?;
ctx.persist_symmetric_key(local_id, SymmetricKeySlotId::LocalUserData)
.map_err(|_| LocalUserDataKeyError::DecryptionFailed)?;
Ok(())
}
}
#[derive(Debug, Error)]
pub enum LocalUserDataKeyError {
#[error("Decryption failed")]
DecryptionFailed,
#[error("Encryption failed")]
EncryptionFailed,
}
impl From<WrappedLocalUserDataKey> for LocalUserDataKeyState {
fn from(wrapped_key: WrappedLocalUserDataKey) -> Self {
Self {
wrapped_key: wrapped_key.0,
}
}
}
#[cfg(test)]
mod tests {
use bitwarden_crypto::{Decryptable, KeyStore, PrimitiveEncryptable};
use super::*;
use crate::key_management::{KeySlotIds, SymmetricKeySlotId};
fn make_key_store_with_user_key() -> KeyStore<KeySlotIds> {
let key_store = KeyStore::<KeySlotIds>::default();
let mut ctx = key_store.context_mut();
let user_key = ctx.generate_symmetric_key();
ctx.persist_symmetric_key(user_key, SymmetricKeySlotId::User)
.expect("persisting user key should succeed");
drop(ctx);
key_store
}
#[test]
fn test_from_context_user_key_wraps_user_key() {
let key_store = make_key_store_with_user_key();
let mut ctx = key_store.context_mut();
let plaintext = "test data";
let ciphertext = plaintext
.encrypt(&mut ctx, SymmetricKeySlotId::User)
.expect("encryption with user key should succeed");
let wrapped = WrappedLocalUserDataKey::from_context_user_key(&mut ctx)
.expect("wrapping should succeed");
wrapped
.unwrap_to_context(&mut ctx)
.expect("unwrapping should succeed");
let decrypted: String = ciphertext
.decrypt(&mut ctx, SymmetricKeySlotId::LocalUserData)
.expect("decryption with local user data key should succeed");
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_unwrap_to_context_fails_with_wrong_key() {
let key_store_a = make_key_store_with_user_key();
let wrapped = {
let mut ctx = key_store_a.context_mut();
WrappedLocalUserDataKey::from_context_user_key(&mut ctx)
.expect("wrapping should succeed")
};
let key_store_b = make_key_store_with_user_key();
let mut ctx_b = key_store_b.context_mut();
assert!(
wrapped.unwrap_to_context(&mut ctx_b).is_err(),
"unwrapping with a different key should fail"
);
}
}