secret-vault 0.2.1

Library provides a secure vault to store application secrets in memory coming from Google/AWS/other secret managers
Documentation
use crate::common_types::*;
use crate::errors::*;
use crate::SecretVaultResult;

use ring::aead::{BoundKey, OpeningKey, SealingKey, UnboundKey};
use ring::rand::{SecureRandom, SystemRandom};
use rvstruct::ValueStruct;
use secret_vault_value::*;

use crate::encryption::*;

pub struct SecretVaultRingAeadEncryption {
    algo: &'static ring::aead::Algorithm,
    session_secret: SecretValue,
    nonce_data: SecretValue,
}

impl SecretVaultRingAeadEncryption {
    pub fn new() -> SecretVaultResult<Self> {
        Self::with_algorithm(&ring::aead::CHACHA20_POLY1305)
    }

    pub fn with_algorithm(algo: &'static ring::aead::Algorithm) -> SecretVaultResult<Self> {
        let secure_rand = SystemRandom::new();
        let session_secret = Self::generate_session_secret(&secure_rand, algo.key_len())?;

        let mut nonce_data: [u8; ring::aead::NONCE_LEN] = [0; ring::aead::NONCE_LEN];
        secure_rand.fill(&mut nonce_data).map_err(|e| {
            SecretVaultEncryptionError::create(
                "ENCRYPTION",
                format!("Unable to initialise random nonce: {:?}", e).as_str(),
            )
        })?;

        Ok(Self {
            algo,
            session_secret,
            nonce_data: SecretValue::new(nonce_data.to_vec()),
        })
    }

    fn generate_session_secret(
        secure_rand: &SystemRandom,
        key_len: usize,
    ) -> SecretVaultResult<SecretValue> {
        let mut rand_key_data: Vec<u8> = Vec::with_capacity(key_len);
        rand_key_data.resize(key_len, 0);
        secure_rand.fill(&mut rand_key_data).map_err(|e| {
            SecretVaultEncryptionError::create(
                "ENCRYPTION",
                format!("Unable to initialise random session key: {:?}", e).as_str(),
            )
        })?;
        Ok(SecretValue::new(Vec::from(rand_key_data)))
    }
}

impl SecretVaultEncryption for SecretVaultRingAeadEncryption {
    fn encrypt_value(
        &self,
        secret_name: &SecretName,
        secret_value: &SecretValue,
    ) -> SecretVaultResult<EncryptedSecretValue> {
        let mut encrypted_secret_value = secret_value.clone();

        let mut sealing_key = SealingKey::new(
            UnboundKey::new(self.algo, self.session_secret.ref_sensitive_value()).map_err(|e| {
                SecretVaultEncryptionError::create(
                    "ENCRYPT_KEY",
                    format!("Unable to create a sealing key: {:?}", e).as_str(),
                )
            })?,
            OneNonceSequence::new(
                ring::aead::Nonce::try_assume_unique_for_key(self.nonce_data.ref_sensitive_value())
                    .map_err(|e| {
                        SecretVaultEncryptionError::create(
                            "ENCRYPT_KEY",
                            format!("Unable to create a nonce for a sealing key: {:?}", e).as_str(),
                        )
                    })?,
            ),
        );

        sealing_key
            .seal_in_place_append_tag(
                ring::aead::Aad::from(secret_name),
                encrypted_secret_value.ref_sensitive_value_mut(),
            )
            .map_err(|e| {
                SecretVaultEncryptionError::create(
                    "ENCRYPT",
                    format!("Unable to encrypt data: {:?}", e).as_str(),
                )
            })?;
        Ok(encrypted_secret_value.into())
    }

    fn decrypt_value(
        &self,
        secret_name: &SecretName,
        encrypted_secret_value: &EncryptedSecretValue,
    ) -> SecretVaultResult<SecretValue> {
        let mut secret_value: SecretValue = encrypted_secret_value.value().clone();

        let mut opening_key = OpeningKey::new(
            UnboundKey::new(&self.algo, self.session_secret.ref_sensitive_value()).map_err(
                |e| {
                    SecretVaultEncryptionError::create(
                        "DECRYPT_KEY",
                        format!("Unable to create an opening key: {:?}", e).as_str(),
                    )
                },
            )?,
            OneNonceSequence::new(
                ring::aead::Nonce::try_assume_unique_for_key(self.nonce_data.ref_sensitive_value())
                    .map_err(|e| {
                        SecretVaultEncryptionError::create(
                            "DECRYPT_KEY",
                            format!("Unable to create an opening key: {:?}", e).as_str(),
                        )
                    })?,
            ),
        );

        opening_key
            .open_in_place(
                ring::aead::Aad::from(secret_name),
                secret_value.ref_sensitive_value_mut(),
            )
            .map_err(|e| {
                SecretVaultEncryptionError::create(
                    "DECRYPT",
                    format!("Unable to decrypt data: {:?}", e).as_str(),
                )
            })?;

        let len = secret_value.ref_sensitive_value().len();
        secret_value
            .ref_sensitive_value_mut()
            .truncate(len - ring::aead::MAX_TAG_LEN);
        Ok(secret_value)
    }
}

struct OneNonceSequence(Option<ring::aead::Nonce>);

impl OneNonceSequence {
    fn new(nonce: ring::aead::Nonce) -> Self {
        Self(Some(nonce))
    }
}

impl ring::aead::NonceSequence for OneNonceSequence {
    fn advance(&mut self) -> Result<ring::aead::Nonce, ring::error::Unspecified> {
        self.0.take().ok_or(ring::error::Unspecified)
    }
}

impl AsRef<[u8]> for &SecretName {
    fn as_ref(&self) -> &[u8] {
        self.value().as_bytes()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::source_tests::*;
    use proptest::prelude::*;

    fn encryption_test_for(mock_secret_value: SecretValue) {
        let mock_secret_name: SecretName = "test".to_string().into();
        let encryption = SecretVaultRingAeadEncryption::new().unwrap();

        let encrypted_value = encryption
            .encrypt_value(&mock_secret_name, &mock_secret_value)
            .unwrap();
        assert_ne!(*encrypted_value.value(), mock_secret_value);

        let decrypted_value = encryption
            .decrypt_value(&mock_secret_name, &encrypted_value)
            .unwrap();
        assert_eq!(
            decrypted_value.ref_sensitive_value(),
            mock_secret_value.ref_sensitive_value()
        );
    }

    proptest! {

        #[test]
        fn secret_encryption_test(mock_secret_value in generate_secret_value()) {
            encryption_test_for(mock_secret_value)
        }
    }

    #[test]
    fn big_secret_encryption_test() {
        for sz in vec![5000, 32768, 65535] {
            encryption_test_for(SecretValue::new("42".repeat(sz).as_bytes().to_vec()))
        }
    }

    #[test]
    fn wrong_secret_name_test_attest() {
        let mock_secret_name1: SecretName = "test1".to_string().into();
        let mock_secret_name2: SecretName = "test2".to_string().into();

        let mock_secret_value = SecretValue::new("42".repeat(1024).as_bytes().to_vec());

        let encryption = SecretVaultRingAeadEncryption::new().unwrap();
        let encrypted_value = encryption
            .encrypt_value(&mock_secret_name1, &mock_secret_value)
            .unwrap();
        encryption
            .decrypt_value(&mock_secret_name2, &encrypted_value)
            .expect_err("Unable to decrypt data");
    }
}