use crate::{
crypto::{generic, generic::AssociatedData, Encrypted, KeyId, RemoteStorageKey},
types::database::account::UserId,
LockKeeperError,
};
use serde::{Deserialize, Serialize};
use tracing::{error, instrument};
use utilities::crypto::error::CryptoError;
use zeroize::ZeroizeOnDrop;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ZeroizeOnDrop)]
pub struct DataBlob(pub(super) generic::Secret);
impl DataBlob {
const GENERATION_TYPE: &'static str = "data blob";
pub fn create(
data: Vec<u8>,
user_id: &UserId,
key_id: &KeyId,
) -> Result<Self, LockKeeperError> {
let context = AssociatedData::new()
.with_bytes(user_id.clone())
.with_bytes(key_id.clone())
.with_str(Self::GENERATION_TYPE);
Ok(DataBlob(generic::Secret::from_parts(data, context)))
}
pub fn blob_data(self) -> Vec<u8> {
self.0.borrow_material().to_vec()
}
fn domain_separator() -> &'static str {
"data blob"
}
pub(super) fn context(&self) -> &AssociatedData {
self.0.context()
}
}
impl TryFrom<DataBlob> for Vec<u8> {
type Error = CryptoError;
fn try_from(secret: DataBlob) -> Result<Self, Self::Error> {
let mut bytes = Vec::new();
bytes.append(&mut DataBlob::domain_separator().into());
bytes.append(&mut secret.0.to_owned().try_into()?);
Ok(bytes)
}
}
impl TryFrom<Vec<u8>> for DataBlob {
type Error = CryptoError;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
let expected_ds: Vec<u8> = DataBlob::domain_separator().into();
if expected_ds.len() > value.len() {
return Err(CryptoError::ConversionError);
}
let (actual_ds, secret) = value.split_at(expected_ds.len());
if expected_ds != actual_ds {
error!("Incorrect domain separator found: {:?}", actual_ds);
return Err(CryptoError::ConversionError);
}
Ok(DataBlob(secret.to_vec().try_into()?))
}
}
impl Encrypted<DataBlob> {
#[instrument(skip_all, err(Debug))]
pub fn decrypt_data_blob(
self,
remote_storage_key: &RemoteStorageKey,
) -> Result<DataBlob, LockKeeperError> {
let decrypted = self.decrypt_inner(&remote_storage_key.0)?;
Ok(decrypted)
}
}
#[cfg(test)]
mod test {
use crate::{
crypto::{CryptoError, DataBlob, KeyId, RemoteStorageKey},
types::database::account::UserId,
LockKeeperError,
};
use rand::Rng;
#[test]
fn encrypt_decrypt_data_blob() -> Result<(), LockKeeperError> {
let mut rng = rand::thread_rng();
let data: Vec<u8> = std::iter::repeat_with(|| rng.gen()).take(512).collect();
let uid = UserId::new(&mut rng)?;
let key_id = KeyId::generate(&mut rng, &uid)?;
let blob = DataBlob::create(data, &uid, &key_id)?;
let key = RemoteStorageKey::generate(&mut rng);
let encrypted = key.encrypt_data_blob(&mut rng, blob.clone())?;
let decrypted = encrypted.decrypt_data_blob(&key)?;
assert_eq!(decrypted, blob);
Ok(())
}
#[test]
fn too_large_blob_fails_to_encode() -> Result<(), LockKeeperError> {
let mut rng = rand::thread_rng();
let too_large = u16::MAX as usize + 1;
let data: Vec<u8> = std::iter::repeat_with(|| rng.gen())
.take(too_large)
.collect();
let uid = UserId::new(&mut rng)?;
let key_id = KeyId::generate(&mut rng, &uid)?;
let blob = DataBlob::create(data, &uid, &key_id)?;
let key = RemoteStorageKey::generate(&mut rng);
let decrypt = key.encrypt_data_blob(&mut rng, blob);
assert!(matches!(
decrypt,
Err(LockKeeperError::Crypto(CryptoError::CannotEncodeDataLength))
));
Ok(())
}
}