askar-storage 0.2.4

Askar secure storage support
Documentation
//! Storage encryption

use std::{collections::HashMap, sync::Arc};

use async_lock::RwLock;

pub mod kdf;

pub mod hmac_key;

mod pass_key;
pub use self::pass_key::PassKey;

mod profile_key;
pub use self::profile_key::ProfileKey;

mod store_key;
pub use self::store_key::{generate_raw_store_key, StoreKey, StoreKeyMethod, StoreKeyReference};

use crate::{
    crypto::buffer::SecretBytes,
    entry::{EncEntryTag, EntryTag},
    error::Error,
    future::unblock,
};

pub type ProfileId = i64;

#[derive(Debug)]
pub struct KeyCache {
    profile_info: RwLock<HashMap<String, (ProfileId, Arc<ProfileKey>)>>,
    pub(crate) store_key: Arc<StoreKey>,
}

impl KeyCache {
    pub fn new(store_key: impl Into<Arc<StoreKey>>) -> Self {
        Self {
            profile_info: RwLock::new(HashMap::new()),
            store_key: store_key.into(),
        }
    }

    pub async fn load_key(&self, ciphertext: Vec<u8>) -> Result<ProfileKey, Error> {
        let store_key = self.store_key.clone();
        unblock(move || {
            let data = store_key
                .unwrap_data(ciphertext)
                .map_err(err_map!(Encryption, "Error decrypting profile key"))?;
            ProfileKey::from_slice(data.as_ref())
        })
        .await
    }

    pub fn add_profile_mut(&mut self, ident: String, pid: ProfileId, key: ProfileKey) {
        self.profile_info
            .get_mut()
            .insert(ident, (pid, Arc::new(key)));
    }

    pub async fn add_profile(&self, ident: String, pid: ProfileId, key: Arc<ProfileKey>) {
        self.profile_info.write().await.insert(ident, (pid, key));
    }

    pub async fn get_profile(&self, name: &str) -> Option<(ProfileId, Arc<ProfileKey>)> {
        self.profile_info.read().await.get(name).cloned()
    }

    pub async fn clear_profile(&self, name: &str) {
        self.profile_info.write().await.remove(name);
    }
}

pub(crate) trait EntryEncryptor {
    fn prepare_input(input: &[u8]) -> SecretBytes {
        SecretBytes::from(input)
    }

    fn encrypt_entry_category(&self, category: SecretBytes) -> Result<Vec<u8>, Error>;
    fn encrypt_entry_name(&self, name: SecretBytes) -> Result<Vec<u8>, Error>;
    fn encrypt_entry_value(
        &self,
        category: &[u8],
        name: &[u8],
        value: SecretBytes,
    ) -> Result<Vec<u8>, Error>;
    fn encrypt_entry_tags(&self, tags: Vec<EntryTag>) -> Result<Vec<EncEntryTag>, Error>;

    fn decrypt_entry_category(&self, enc_category: Vec<u8>) -> Result<String, Error>;
    fn decrypt_entry_name(&self, enc_name: Vec<u8>) -> Result<String, Error>;
    fn decrypt_entry_value(
        &self,
        category: &[u8],
        name: &[u8],
        enc_value: Vec<u8>,
    ) -> Result<SecretBytes, Error>;
    fn decrypt_entry_tags(&self, enc_tags: Vec<EncEntryTag>) -> Result<Vec<EntryTag>, Error>;
}

pub struct NullEncryptor;

impl EntryEncryptor for NullEncryptor {
    fn encrypt_entry_category(&self, category: SecretBytes) -> Result<Vec<u8>, Error> {
        Ok(category.into_vec())
    }
    fn encrypt_entry_name(&self, name: SecretBytes) -> Result<Vec<u8>, Error> {
        Ok(name.into_vec())
    }
    fn encrypt_entry_value(
        &self,
        _category: &[u8],
        _name: &[u8],
        value: SecretBytes,
    ) -> Result<Vec<u8>, Error> {
        Ok(value.into_vec())
    }
    fn encrypt_entry_tags(&self, tags: Vec<EntryTag>) -> Result<Vec<EncEntryTag>, Error> {
        Ok(tags
            .into_iter()
            .map(|tag| match tag {
                EntryTag::Encrypted(name, value) => EncEntryTag {
                    name: name.into_bytes(),
                    value: value.into_bytes(),
                    plaintext: false,
                },
                EntryTag::Plaintext(name, value) => EncEntryTag {
                    name: name.into_bytes(),
                    value: value.into_bytes(),
                    plaintext: true,
                },
            })
            .collect())
    }

    fn decrypt_entry_category(&self, enc_category: Vec<u8>) -> Result<String, Error> {
        String::from_utf8(enc_category).map_err(err_map!(Encryption))
    }
    fn decrypt_entry_name(&self, enc_name: Vec<u8>) -> Result<String, Error> {
        String::from_utf8(enc_name).map_err(err_map!(Encryption))
    }
    fn decrypt_entry_value(
        &self,
        _category: &[u8],
        _name: &[u8],
        enc_value: Vec<u8>,
    ) -> Result<SecretBytes, Error> {
        Ok(enc_value.into())
    }
    fn decrypt_entry_tags(&self, enc_tags: Vec<EncEntryTag>) -> Result<Vec<EntryTag>, Error> {
        enc_tags.into_iter().try_fold(vec![], |mut acc, tag| {
            let name = String::from_utf8(tag.name).map_err(err_map!(Encryption))?;
            let value = String::from_utf8(tag.value).map_err(err_map!(Encryption))?;
            acc.push(if tag.plaintext {
                EntryTag::Plaintext(name, value)
            } else {
                EntryTag::Encrypted(name, value)
            });
            Result::<_, Error>::Ok(acc)
        })
    }
}