use std::{
cell::Cell,
sync::{RwLockReadGuard, RwLockWriteGuard},
};
use coset::iana::KeyOperation;
use serde::Serialize;
use tracing::instrument;
use zeroize::Zeroizing;
use super::KeyStoreInner;
use crate::{
BitwardenLegacyKeyBytes, ContentFormat, CoseEncrypt0Bytes, CoseKeyBytes, CoseSerializable,
CryptoError, EncString, KeyDecryptable, KeyEncryptable, KeySlotId, KeySlotIds, LocalId,
Pkcs8PrivateKeyBytes, PrivateKey, PublicKey, PublicKeyEncryptionAlgorithm, Result,
RotatedUserKeys, Signature, SignatureAlgorithm, SignedObject, SignedPublicKey,
SignedPublicKeyMessage, SigningKey, SymmetricCryptoKey, SymmetricKeyAlgorithm, VerifyingKey,
derive_shareable_key, error::UnsupportedOperationError, signing, store::backend::StoreBackend,
};
#[must_use]
pub struct KeyStoreContext<'a, Ids: KeySlotIds> {
pub(super) global_keys: GlobalKeys<'a, Ids>,
pub(super) local_symmetric_keys: Box<dyn StoreBackend<Ids::Symmetric>>,
pub(super) local_private_keys: Box<dyn StoreBackend<Ids::Private>>,
pub(super) local_signing_keys: Box<dyn StoreBackend<Ids::Signing>>,
pub(super) security_state_version: u64,
pub(super) _phantom: std::marker::PhantomData<(Cell<()>, RwLockReadGuard<'static, ()>)>,
}
pub(crate) enum GlobalKeys<'a, Ids: KeySlotIds> {
ReadOnly(RwLockReadGuard<'a, KeyStoreInner<Ids>>),
ReadWrite(RwLockWriteGuard<'a, KeyStoreInner<Ids>>),
}
impl<Ids: KeySlotIds> GlobalKeys<'_, Ids> {
pub fn get(&self) -> &KeyStoreInner<Ids> {
match self {
GlobalKeys::ReadOnly(keys) => keys,
GlobalKeys::ReadWrite(keys) => keys,
}
}
pub fn get_mut(&mut self) -> Result<&mut KeyStoreInner<Ids>> {
match self {
GlobalKeys::ReadOnly(_) => Err(CryptoError::ReadOnlyKeyStore),
GlobalKeys::ReadWrite(keys) => Ok(keys),
}
}
}
impl<Ids: KeySlotIds> KeyStoreContext<'_, Ids> {
pub fn clear_local(&mut self) {
self.local_symmetric_keys.clear();
self.local_private_keys.clear();
self.local_signing_keys.clear();
}
pub fn get_security_state_version(&self) -> u64 {
self.security_state_version
}
pub fn retain_symmetric_keys(&mut self, f: fn(Ids::Symmetric) -> bool) {
if let Ok(keys) = self.global_keys.get_mut() {
keys.symmetric_keys.retain(f);
}
self.local_symmetric_keys.retain(f);
}
pub fn retain_private_keys(&mut self, f: fn(Ids::Private) -> bool) {
if let Ok(keys) = self.global_keys.get_mut() {
keys.private_keys.retain(f);
}
self.local_private_keys.retain(f);
}
fn drop_symmetric_key(&mut self, key_id: Ids::Symmetric) -> Result<()> {
if key_id.is_local() {
self.local_symmetric_keys.remove(key_id);
} else {
self.global_keys.get_mut()?.symmetric_keys.remove(key_id);
}
Ok(())
}
fn drop_private_key(&mut self, key_id: Ids::Private) -> Result<()> {
if key_id.is_local() {
self.local_private_keys.remove(key_id);
} else {
self.global_keys.get_mut()?.private_keys.remove(key_id);
}
Ok(())
}
fn drop_signing_key(&mut self, key_id: Ids::Signing) -> Result<()> {
if key_id.is_local() {
self.local_signing_keys.remove(key_id);
} else {
self.global_keys.get_mut()?.signing_keys.remove(key_id);
}
Ok(())
}
#[instrument(skip(self, wrapped_key), err)]
pub fn unwrap_symmetric_key(
&mut self,
wrapping_key: Ids::Symmetric,
wrapped_key: &EncString,
) -> Result<Ids::Symmetric> {
let wrapping_key = self.get_symmetric_key(wrapping_key)?;
let key = match (wrapped_key, wrapping_key) {
(EncString::Aes256Cbc_B64 { iv, data }, SymmetricCryptoKey::Aes256CbcKey(key)) => {
SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(
crate::aes::decrypt_aes256(iv, data.clone(), &key.enc_key)
.map_err(|_| CryptoError::Decrypt)?,
))?
}
(
EncString::Aes256Cbc_HmacSha256_B64 { iv, mac, data },
SymmetricCryptoKey::Aes256CbcHmacKey(key),
) => SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(
crate::aes::decrypt_aes256_hmac(iv, mac, data.clone(), &key.mac_key, &key.enc_key)
.map_err(|_| CryptoError::Decrypt)?,
))?,
(
EncString::Cose_Encrypt0_B64 { data },
SymmetricCryptoKey::XChaCha20Poly1305Key(key),
) => {
let (content_bytes, content_format) = crate::cose::decrypt_xchacha20_poly1305(
&CoseEncrypt0Bytes::from(data.clone()),
key,
)?;
match content_format {
ContentFormat::BitwardenLegacyKey => {
SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(content_bytes))?
}
ContentFormat::CoseKey => SymmetricCryptoKey::try_from_cose(&content_bytes)?,
_ => return Err(CryptoError::InvalidKey),
}
}
_ => {
tracing::warn!(
"Unsupported unwrap operation for the given key and data {:?}, {:?}",
wrapping_key,
wrapped_key
);
return Err(CryptoError::InvalidKey);
}
};
let new_key_id = Ids::Symmetric::new_local(LocalId::new());
#[allow(deprecated)]
self.set_symmetric_key(new_key_id, key)?;
Ok(new_key_id)
}
pub fn persist_symmetric_key(
&mut self,
from: Ids::Symmetric,
to: Ids::Symmetric,
) -> Result<()> {
if !from.is_local() || to.is_local() {
return Err(CryptoError::InvalidKeyStoreOperation);
}
let key = self.get_symmetric_key(from)?.to_owned();
self.drop_symmetric_key(from)?;
#[allow(deprecated)]
self.set_symmetric_key(to, key)?;
Ok(())
}
pub fn persist_private_key(&mut self, from: Ids::Private, to: Ids::Private) -> Result<()> {
if !from.is_local() || to.is_local() {
return Err(CryptoError::InvalidKeyStoreOperation);
}
let key = self.get_private_key(from)?.to_owned();
self.drop_private_key(from)?;
#[allow(deprecated)]
self.set_private_key(to, key)?;
Ok(())
}
pub fn persist_signing_key(&mut self, from: Ids::Signing, to: Ids::Signing) -> Result<()> {
if !from.is_local() || to.is_local() {
return Err(CryptoError::InvalidKeyStoreOperation);
}
let key = self.get_signing_key(from)?.to_owned();
self.drop_signing_key(from)?;
#[allow(deprecated)]
self.set_signing_key(to, key)?;
Ok(())
}
pub fn wrap_signing_key(
&self,
wrapping_key: Ids::Symmetric,
key_to_wrap: Ids::Signing,
) -> Result<EncString> {
let wrapping_key = self.get_symmetric_key(wrapping_key)?;
let signing_key = self.get_signing_key(key_to_wrap)?.to_owned();
signing_key.to_cose().encrypt_with_key(wrapping_key)
}
pub fn wrap_private_key(
&self,
wrapping_key: Ids::Symmetric,
key_to_wrap: Ids::Private,
) -> Result<EncString> {
let wrapping_key = self.get_symmetric_key(wrapping_key)?;
let private_key = self.get_private_key(key_to_wrap)?.to_owned();
private_key.to_der()?.encrypt_with_key(wrapping_key)
}
#[instrument(skip(self, wrapped_key), err)]
pub fn unwrap_private_key(
&mut self,
wrapping_key: Ids::Symmetric,
wrapped_key: &EncString,
) -> Result<Ids::Private> {
let wrapping_key = self.get_symmetric_key(wrapping_key)?;
let private_key_bytes: Vec<u8> = wrapped_key.decrypt_with_key(wrapping_key)?;
let private_key = PrivateKey::from_der(&Pkcs8PrivateKeyBytes::from(private_key_bytes))?;
Ok(self.add_local_private_key(private_key))
}
pub fn unwrap_signing_key(
&mut self,
wrapping_key: Ids::Symmetric,
wrapped_key: &EncString,
) -> Result<Ids::Signing> {
let wrapping_key = self.get_symmetric_key(wrapping_key)?;
let signing_key_bytes: Vec<u8> = wrapped_key.decrypt_with_key(wrapping_key)?;
let signing_key = SigningKey::from_cose(&CoseKeyBytes::from(signing_key_bytes))?;
Ok(self.add_local_signing_key(signing_key))
}
pub fn get_verifying_key(&self, signing_key_id: Ids::Signing) -> Result<VerifyingKey> {
let signing_key = self.get_signing_key(signing_key_id)?;
Ok(signing_key.to_verifying_key())
}
pub fn get_public_key(&self, private_key_id: Ids::Private) -> Result<PublicKey> {
let private_key = self.get_private_key(private_key_id)?;
Ok(private_key.to_public_key())
}
pub fn wrap_symmetric_key(
&self,
wrapping_key: Ids::Symmetric,
key_to_wrap: Ids::Symmetric,
) -> Result<EncString> {
use SymmetricCryptoKey::*;
let wrapping_key_instance = self.get_symmetric_key(wrapping_key)?;
let key_to_wrap_instance = self.get_symmetric_key(key_to_wrap)?;
match (wrapping_key_instance, key_to_wrap_instance) {
(
Aes256CbcHmacKey(_),
Aes256CbcHmacKey(_) | Aes256CbcKey(_) | XChaCha20Poly1305Key(_),
) => self.encrypt_data_with_symmetric_key(
wrapping_key,
key_to_wrap_instance
.to_encoded()
.as_ref()
.to_vec()
.as_slice(),
ContentFormat::BitwardenLegacyKey,
),
(XChaCha20Poly1305Key(_), _) => {
let encoded = key_to_wrap_instance.to_encoded_raw();
let content_format = encoded.content_format();
self.encrypt_data_with_symmetric_key(
wrapping_key,
Into::<Vec<u8>>::into(encoded).as_slice(),
content_format,
)
}
_ => Err(CryptoError::OperationNotSupported(
UnsupportedOperationError::EncryptionNotImplementedForKey,
)),
}
}
pub fn has_symmetric_key(&self, key_id: Ids::Symmetric) -> bool {
self.get_symmetric_key(key_id).is_ok()
}
pub fn has_private_key(&self, key_id: Ids::Private) -> bool {
self.get_private_key(key_id).is_ok()
}
pub fn has_signing_key(&self, key_id: Ids::Signing) -> bool {
self.get_signing_key(key_id).is_ok()
}
pub fn generate_symmetric_key(&mut self) -> Ids::Symmetric {
self.add_local_symmetric_key(SymmetricCryptoKey::make_aes256_cbc_hmac_key())
}
pub fn make_symmetric_key(&mut self, algorithm: SymmetricKeyAlgorithm) -> Ids::Symmetric {
self.add_local_symmetric_key(SymmetricCryptoKey::make(algorithm))
}
pub fn make_private_key(&mut self, algorithm: PublicKeyEncryptionAlgorithm) -> Ids::Private {
self.add_local_private_key(PrivateKey::make(algorithm))
}
pub fn make_signing_key(&mut self, algorithm: SignatureAlgorithm) -> Ids::Signing {
self.add_local_signing_key(SigningKey::make(algorithm))
}
pub fn derive_shareable_key(
&mut self,
secret: Zeroizing<[u8; 16]>,
name: &str,
info: Option<&str>,
) -> Result<Ids::Symmetric> {
let key_id = Ids::Symmetric::new_local(LocalId::new());
#[allow(deprecated)]
self.set_symmetric_key(
key_id,
SymmetricCryptoKey::Aes256CbcHmacKey(derive_shareable_key(secret, name, info)),
)?;
Ok(key_id)
}
#[deprecated(note = "This function should ideally never be used outside this crate")]
pub fn dangerous_get_symmetric_key(
&self,
key_id: Ids::Symmetric,
) -> Result<&SymmetricCryptoKey> {
self.get_symmetric_key(key_id)
}
#[deprecated(note = "This function should ideally never be used outside this crate")]
pub fn dangerous_get_signing_key(&self, key_id: Ids::Signing) -> Result<&SigningKey> {
self.get_signing_key(key_id)
}
#[deprecated(note = "This function should ideally never be used outside this crate")]
pub fn dangerous_get_private_key(&self, key_id: Ids::Private) -> Result<&PrivateKey> {
self.get_private_key(key_id)
}
pub fn make_signed_public_key(
&self,
private_key_id: Ids::Private,
signing_key_id: Ids::Signing,
) -> Result<SignedPublicKey> {
let public_key = self.get_private_key(private_key_id)?.to_public_key();
let signing_key = self.get_signing_key(signing_key_id)?;
let signed_public_key =
SignedPublicKeyMessage::from_public_key(&public_key)?.sign(signing_key)?;
Ok(signed_public_key)
}
pub(crate) fn get_symmetric_key(&self, key_id: Ids::Symmetric) -> Result<&SymmetricCryptoKey> {
if key_id.is_local() {
self.local_symmetric_keys.get(key_id)
} else {
self.global_keys.get().symmetric_keys.get(key_id)
}
.ok_or_else(|| crate::CryptoError::MissingKeyId(format!("{key_id:?}")))
}
pub(super) fn get_private_key(&self, key_id: Ids::Private) -> Result<&PrivateKey> {
if key_id.is_local() {
self.local_private_keys.get(key_id)
} else {
self.global_keys.get().private_keys.get(key_id)
}
.ok_or_else(|| crate::CryptoError::MissingKeyId(format!("{key_id:?}")))
}
pub(super) fn get_signing_key(&self, key_id: Ids::Signing) -> Result<&SigningKey> {
if key_id.is_local() {
self.local_signing_keys.get(key_id)
} else {
self.global_keys.get().signing_keys.get(key_id)
}
.ok_or_else(|| crate::CryptoError::MissingKeyId(format!("{key_id:?}")))
}
#[deprecated(note = "This function should ideally never be used outside this crate")]
pub fn set_symmetric_key(
&mut self,
key_id: Ids::Symmetric,
key: SymmetricCryptoKey,
) -> Result<()> {
self.set_symmetric_key_internal(key_id, key)
}
pub(crate) fn set_symmetric_key_internal(
&mut self,
key_id: Ids::Symmetric,
key: SymmetricCryptoKey,
) -> Result<()> {
if key_id.is_local() {
self.local_symmetric_keys.upsert(key_id, key);
} else {
self.global_keys
.get_mut()?
.symmetric_keys
.upsert(key_id, key);
}
Ok(())
}
pub fn add_local_symmetric_key(&mut self, key: SymmetricCryptoKey) -> Ids::Symmetric {
let key_id = Ids::Symmetric::new_local(LocalId::new());
self.local_symmetric_keys.upsert(key_id, key);
key_id
}
pub fn get_symmetric_key_algorithm(
&self,
key_id: Ids::Symmetric,
) -> Result<SymmetricKeyAlgorithm> {
let key = self.get_symmetric_key(key_id)?;
match key {
SymmetricCryptoKey::Aes256CbcKey(_) => Err(CryptoError::OperationNotSupported(
UnsupportedOperationError::EncryptionNotImplementedForKey,
)),
SymmetricCryptoKey::Aes256CbcHmacKey(_) => Ok(SymmetricKeyAlgorithm::Aes256CbcHmac),
SymmetricCryptoKey::XChaCha20Poly1305Key(_) => {
Ok(SymmetricKeyAlgorithm::XChaCha20Poly1305)
}
}
}
#[deprecated(note = "This function should ideally never be used outside this crate")]
pub fn set_private_key(&mut self, key_id: Ids::Private, key: PrivateKey) -> Result<()> {
if key_id.is_local() {
self.local_private_keys.upsert(key_id, key);
} else {
self.global_keys.get_mut()?.private_keys.upsert(key_id, key);
}
Ok(())
}
pub fn add_local_private_key(&mut self, key: PrivateKey) -> Ids::Private {
let key_id = Ids::Private::new_local(LocalId::new());
self.local_private_keys.upsert(key_id, key);
key_id
}
#[deprecated(note = "This function should ideally never be used outside this crate")]
pub fn set_signing_key(&mut self, key_id: Ids::Signing, key: SigningKey) -> Result<()> {
if key_id.is_local() {
self.local_signing_keys.upsert(key_id, key);
} else {
self.global_keys.get_mut()?.signing_keys.upsert(key_id, key);
}
Ok(())
}
pub fn add_local_signing_key(&mut self, key: SigningKey) -> Ids::Signing {
let key_id = Ids::Signing::new_local(LocalId::new());
self.local_signing_keys.upsert(key_id, key);
key_id
}
#[instrument(skip(self, data), err)]
pub(crate) fn decrypt_data_with_symmetric_key(
&self,
key: Ids::Symmetric,
data: &EncString,
) -> Result<Vec<u8>> {
let key = self.get_symmetric_key(key)?;
match (data, key) {
(EncString::Aes256Cbc_B64 { iv, data }, SymmetricCryptoKey::Aes256CbcKey(key)) => {
crate::aes::decrypt_aes256(iv, data.clone(), &key.enc_key)
.map_err(|_| CryptoError::Decrypt)
}
(
EncString::Aes256Cbc_HmacSha256_B64 { iv, mac, data },
SymmetricCryptoKey::Aes256CbcHmacKey(key),
) => crate::aes::decrypt_aes256_hmac(iv, mac, data.clone(), &key.mac_key, &key.enc_key)
.map_err(|_| CryptoError::Decrypt),
(
EncString::Cose_Encrypt0_B64 { data },
SymmetricCryptoKey::XChaCha20Poly1305Key(key),
) => {
let (data, _) = crate::cose::decrypt_xchacha20_poly1305(
&CoseEncrypt0Bytes::from(data.clone()),
key,
)?;
Ok(data)
}
_ => {
tracing::warn!("Unsupported decryption operation for the given key and data");
Err(CryptoError::InvalidKey)
}
}
}
pub(crate) fn encrypt_data_with_symmetric_key(
&self,
key: Ids::Symmetric,
data: &[u8],
content_format: ContentFormat,
) -> Result<EncString> {
let key = self.get_symmetric_key(key)?;
match key {
SymmetricCryptoKey::Aes256CbcKey(_) => Err(CryptoError::OperationNotSupported(
UnsupportedOperationError::EncryptionNotImplementedForKey,
)),
SymmetricCryptoKey::Aes256CbcHmacKey(key) => EncString::encrypt_aes256_hmac(data, key),
SymmetricCryptoKey::XChaCha20Poly1305Key(key) => {
if !key.supported_operations.contains(&KeyOperation::Encrypt) {
return Err(CryptoError::KeyOperationNotSupported(KeyOperation::Encrypt));
}
EncString::encrypt_xchacha20_poly1305(data, key, content_format)
}
}
}
pub fn sign<Message: Serialize>(
&self,
key: Ids::Signing,
message: &Message,
namespace: &crate::SigningNamespace,
) -> Result<SignedObject> {
self.get_signing_key(key)?.sign(message, namespace)
}
#[allow(unused)]
pub(crate) fn sign_detached<Message: Serialize>(
&self,
key: Ids::Signing,
message: &Message,
namespace: &crate::SigningNamespace,
) -> Result<(Signature, signing::SerializedMessage)> {
self.get_signing_key(key)?.sign_detached(message, namespace)
}
pub fn dangerous_get_v2_rotated_account_keys(
&self,
current_user_private_key_id: Ids::Private,
current_user_signing_key_id: Ids::Signing,
) -> Result<RotatedUserKeys> {
#[expect(deprecated)]
crate::dangerous_get_v2_rotated_account_keys(
current_user_private_key_id,
current_user_signing_key_id,
self,
)
}
}
#[cfg(test)]
#[allow(deprecated)]
mod tests {
use serde::{Deserialize, Serialize};
use crate::{
CompositeEncryptable, CoseKeyBytes, CoseSerializable, CryptoError, Decryptable,
KeyDecryptable, Pkcs8PrivateKeyBytes, PrivateKey, PublicKey, PublicKeyEncryptionAlgorithm,
SignatureAlgorithm, SigningKey, SigningNamespace, SymmetricCryptoKey,
SymmetricKeyAlgorithm,
store::{
KeyStore,
tests::{Data, DataView},
},
traits::tests::{TestIds, TestSigningKey, TestSymmKey},
};
#[test]
fn test_set_signing_key() {
let store: KeyStore<TestIds> = KeyStore::default();
let key_a0_id = TestSigningKey::A(0);
let key_a0 = SigningKey::make(SignatureAlgorithm::Ed25519);
store
.context_mut()
.set_signing_key(key_a0_id, key_a0)
.unwrap();
}
#[test]
fn test_set_keys_for_encryption() {
let store: KeyStore<TestIds> = KeyStore::default();
let key_a0_id = TestSymmKey::A(0);
let mut ctx = store.context_mut();
let local_key_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
ctx.persist_symmetric_key(local_key_id, TestSymmKey::A(0))
.unwrap();
assert!(ctx.has_symmetric_key(key_a0_id));
let data = DataView("Hello, World!".to_string(), key_a0_id);
let _encrypted: Data = data.encrypt_composite(&mut ctx, key_a0_id).unwrap();
}
#[test]
fn test_key_encryption() {
let store: KeyStore<TestIds> = KeyStore::default();
let mut ctx = store.context();
let key_1_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
assert!(ctx.has_symmetric_key(key_1_id));
let key_2_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
assert!(ctx.has_symmetric_key(key_2_id));
let key_2_enc = ctx.wrap_symmetric_key(key_1_id, key_2_id).unwrap();
let new_key_id = ctx.unwrap_symmetric_key(key_1_id, &key_2_enc).unwrap();
let data = DataView("Hello, World!".to_string(), key_2_id);
let encrypted = data.encrypt_composite(&mut ctx, key_2_id).unwrap();
let decrypted1 = encrypted.decrypt(&mut ctx, key_2_id).unwrap();
let decrypted2 = encrypted.decrypt(&mut ctx, new_key_id).unwrap();
assert_eq!(decrypted1.0, decrypted2.0);
}
#[test]
fn test_wrap_unwrap() {
let store: KeyStore<TestIds> = KeyStore::default();
let mut ctx = store.context_mut();
let key_aes_1_id = TestSymmKey::A(1);
let local_key_1_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
ctx.persist_symmetric_key(local_key_1_id, key_aes_1_id)
.unwrap();
let key_aes_2_id = TestSymmKey::A(2);
let local_key_2_id = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
ctx.persist_symmetric_key(local_key_2_id, key_aes_2_id)
.unwrap();
let key_xchacha_3_id = TestSymmKey::A(3);
let key_xchacha_3 = SymmetricCryptoKey::make_xchacha20_poly1305_key();
ctx.set_symmetric_key(key_xchacha_3_id, key_xchacha_3.clone())
.unwrap();
let key_xchacha_4_id = TestSymmKey::A(4);
let key_xchacha_4 = SymmetricCryptoKey::make_xchacha20_poly1305_key();
ctx.set_symmetric_key(key_xchacha_4_id, key_xchacha_4.clone())
.unwrap();
let wrapped_key_1_2 = ctx.wrap_symmetric_key(key_aes_1_id, key_aes_2_id).unwrap();
let wrapped_key_1_3 = ctx
.wrap_symmetric_key(key_aes_1_id, key_xchacha_3_id)
.unwrap();
let wrapped_key_3_1 = ctx
.wrap_symmetric_key(key_xchacha_3_id, key_aes_1_id)
.unwrap();
let wrapped_key_3_4 = ctx
.wrap_symmetric_key(key_xchacha_3_id, key_xchacha_4_id)
.unwrap();
let _unwrapped_key_2 = ctx
.unwrap_symmetric_key(key_aes_1_id, &wrapped_key_1_2)
.unwrap();
let _unwrapped_key_3 = ctx
.unwrap_symmetric_key(key_aes_1_id, &wrapped_key_1_3)
.unwrap();
let _unwrapped_key_1 = ctx
.unwrap_symmetric_key(key_xchacha_3_id, &wrapped_key_3_1)
.unwrap();
let _unwrapped_key_4 = ctx
.unwrap_symmetric_key(key_xchacha_3_id, &wrapped_key_3_4)
.unwrap();
}
#[test]
fn test_signing() {
let store: KeyStore<TestIds> = KeyStore::default();
let key_a0_id = TestSigningKey::A(0);
let key_a0 = SigningKey::make(SignatureAlgorithm::Ed25519);
let verifying_key = key_a0.to_verifying_key();
store
.context_mut()
.set_signing_key(key_a0_id, key_a0)
.unwrap();
assert!(store.context().has_signing_key(key_a0_id));
#[derive(Serialize, Deserialize)]
struct TestData {
data: String,
}
let signed_object = store
.context()
.sign(
key_a0_id,
&TestData {
data: "Hello".to_string(),
},
&SigningNamespace::ExampleNamespace,
)
.unwrap();
let payload: Result<TestData, CryptoError> =
signed_object.verify_and_unwrap(&verifying_key, &SigningNamespace::ExampleNamespace);
assert!(payload.is_ok());
let (signature, serialized_message) = store
.context()
.sign_detached(
key_a0_id,
&TestData {
data: "Hello".to_string(),
},
&SigningNamespace::ExampleNamespace,
)
.unwrap();
assert!(signature.verify(
serialized_message.as_bytes(),
&verifying_key,
&SigningNamespace::ExampleNamespace
))
}
#[test]
fn test_account_key_rotation() {
let store: KeyStore<TestIds> = KeyStore::default();
let mut ctx = store.context_mut();
let current_user_signing_key_id = ctx.make_signing_key(SignatureAlgorithm::Ed25519);
let current_user_private_key_id =
ctx.make_private_key(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
let rotated_keys = ctx
.dangerous_get_v2_rotated_account_keys(
current_user_private_key_id,
current_user_signing_key_id,
)
.unwrap();
assert_eq!(
PublicKey::from_der(&rotated_keys.public_key)
.unwrap()
.to_der()
.unwrap(),
ctx.get_private_key(current_user_private_key_id)
.unwrap()
.to_public_key()
.to_der()
.unwrap()
);
let decrypted_private_key: Vec<u8> = rotated_keys
.private_key
.decrypt_with_key(&rotated_keys.user_key)
.unwrap();
let private_key =
PrivateKey::from_der(&Pkcs8PrivateKeyBytes::from(decrypted_private_key)).unwrap();
assert_eq!(
private_key.to_der().unwrap(),
ctx.get_private_key(current_user_private_key_id)
.unwrap()
.to_der()
.unwrap()
);
let decrypted_signing_key: Vec<u8> = rotated_keys
.signing_key
.decrypt_with_key(&rotated_keys.user_key)
.unwrap();
let signing_key =
SigningKey::from_cose(&CoseKeyBytes::from(decrypted_signing_key)).unwrap();
assert_eq!(
signing_key.to_cose(),
ctx.get_signing_key(current_user_signing_key_id)
.unwrap()
.to_cose(),
);
let signed_public_key = rotated_keys.signed_public_key;
let unwrapped_key = signed_public_key
.verify_and_unwrap(
&ctx.get_signing_key(current_user_signing_key_id)
.unwrap()
.to_verifying_key(),
)
.unwrap();
assert_eq!(
unwrapped_key.to_der().unwrap(),
ctx.get_private_key(current_user_private_key_id)
.unwrap()
.to_public_key()
.to_der()
.unwrap()
);
}
#[test]
fn test_encrypt_fails_when_operation_not_allowed() {
use coset::iana::KeyOperation;
let store = KeyStore::<TestIds>::default();
let mut ctx = store.context_mut();
let key_id = TestSymmKey::A(0);
let key = SymmetricCryptoKey::XChaCha20Poly1305Key(crate::XChaCha20Poly1305Key {
key_id: [0u8; 16].into(),
enc_key: Box::pin([0u8; 32].into()),
supported_operations: vec![KeyOperation::Decrypt],
});
ctx.set_symmetric_key(key_id, key).unwrap();
let data = DataView("should fail".to_string(), key_id);
let result = data.encrypt_composite(&mut ctx, key_id);
assert!(
matches!(
result,
Err(CryptoError::KeyOperationNotSupported(KeyOperation::Encrypt))
),
"Expected encrypt to fail with KeyOperationNotSupported",
);
}
#[test]
fn test_move_key() {
let store: KeyStore<TestIds> = KeyStore::default();
let mut ctx = store.context_mut();
let key = ctx.make_symmetric_key(SymmetricKeyAlgorithm::Aes256CbcHmac);
assert!(ctx.has_symmetric_key(key));
let new_key_id = TestSymmKey::A(1);
ctx.persist_symmetric_key(key, new_key_id).unwrap();
assert!(!ctx.has_symmetric_key(key));
assert!(ctx.has_symmetric_key(new_key_id));
}
}