trelent-hyok 0.1.12

A Rust library implementing Hold Your Own Key (HYOK) encryption patterns with support for multiple cloud providers
Documentation
//! Encrypted value management and type conversion.
//!
//! This module provides functionality for managing encrypted values and
//! releasing them into different types. It supports:
//!
//! - Multiple value types (bytes, strings, JSON)
//! - Type-safe conversion
//! - Custom encryption strategies
//! - Flexible metadata handling

use crate::dek::DEK;
use crate::encryption::EncryptionStrategy;
use crate::error::encryption::EncryptionError;
use async_trait::async_trait;

/// A container for encrypted data that can be released into different types.
///
/// This struct holds encrypted data and provides methods to decrypt and
/// convert it into various types like bytes, strings, or JSON values.
///
/// # Example
/// ```no_run
/// use hyokashi::{HeldValue, ReleaseHeldValue};
///
///     let encrypted_data = vec![/* ... */];
///     let held = HeldValue::new(encrypted_data);
///
///     // Release as different types
///     let bytes: Vec<u8> = held.release(dek.clone(), &strategy, metadata.clone()).await?;
///     let text: String = held.release(dek.clone(), &strategy, metadata.clone()).await?;
///
/// ```
#[derive(Debug, Clone)]
pub struct HeldValue {
    pub(crate) ciphertext: Vec<u8>,
}

impl HeldValue {
    /// Creates a new `HeldValue` containing the encrypted data.
    ///
    /// # Arguments
    ///
    /// * `ciphertext` - The encrypted data to store
    pub fn new(ciphertext: Vec<u8>) -> Self {
        Self { ciphertext }
    }

    /// Returns a copy of the encrypted data.
    pub fn value(&self) -> Vec<u8> {
        self.ciphertext.clone()
    }
}

/// A trait for releasing encrypted values into specific types.
///
/// This trait enables type-safe decryption and conversion of encrypted
/// data into various target types. Implementations handle both decryption
/// and type conversion.
///
/// # Type Parameters
///
/// * `T` - The type to convert the decrypted data into
///
/// # Example
/// ```no_run
/// use async_trait::async_trait;
/// use hyokashi::{ReleaseHeldValue, EncryptionError};
///
/// struct MyEncryptedValue(Vec<u8>);
///
/// #[async_trait]
/// impl ReleaseHeldValue<u64> for MyEncryptedValue {
///     async fn release<S, ED>(&self, dek: DEK, strategy: &S, ed: ED)
///         -> Result<u64, EncryptionError>
///     where
///         S: EncryptionStrategy<EncryptionData = ED> + Send + Sync,
///         ED: Send
///     {
///         // Implement decryption and conversion to u64...
///         # Ok(0)
///     }
/// }
/// ```
#[async_trait]
pub trait ReleaseHeldValue<T> {
    /// Releases the encrypted value as the specified type.
    ///
    /// # Type Parameters
    ///
    /// * `S` - The encryption strategy type
    /// * `ED` - The encryption metadata type
    ///
    /// # Arguments
    ///
    /// * `dek` - Data Encryption Key to use
    /// * `strategy` - Encryption strategy for decryption
    /// * `encryption_data` - Metadata needed for decryption
    async fn release<S: EncryptionStrategy<EncryptionData = ED> + Send + Sync, ED: Send>(
        &self,
        dek: DEK,
        strategy: &S,
        encryption_data: ED
    ) -> Result<T, EncryptionError>;
}

/// Helper function to create a new encrypted value.
///
/// This function encrypts the provided data using the specified strategy
/// and wraps it in a `HeldValue`.
///
/// # Arguments
///
/// * `val` - Data to encrypt
/// * `dek` - Data Encryption Key to use
/// * `strategy` - Encryption strategy to use
/// * `encryption_data` - Metadata for encryption
///
/// # Example
/// ```no_run
/// use hyokashi::Held;
///
/// async {
///     let data = b"secret data".to_vec();
///     let held = Held(data, dek, &strategy, metadata).await?;
///     # Ok::<(), Box<dyn std::error::Error>>(())
/// };
/// ```
#[allow(non_snake_case)]
pub async fn Held<S: EncryptionStrategy<EncryptionData=ED> + Send, ED: Send>(
    val: Vec<u8>,
    dek: DEK,
    strategy: &S,
    encryption_data: ED,
) -> Result<HeldValue, EncryptionError> {
    strategy.encrypt(dek, val, encryption_data).await.map(|ciphertext| HeldValue::new(ciphertext))
}

// Implementations for common types...
#[async_trait]
impl ReleaseHeldValue<Vec<u8>> for HeldValue {
    async fn release<S: EncryptionStrategy<EncryptionData = ED> + Send + Sync, ED: Send>(
        &self,
        dek: DEK,
        strategy: &S,
        encryption_data: ED
    ) -> Result<Vec<u8>, EncryptionError> {
        strategy.decrypt(dek, self.ciphertext.clone(), encryption_data).await
    }
}

#[async_trait]
impl ReleaseHeldValue<String> for HeldValue {
    async fn release<S: EncryptionStrategy<EncryptionData = ED> + Send + Sync, ED: Send>(
        &self,
        dek: DEK,
        strategy: &S,
        encryption_data: ED
    ) -> Result<String, EncryptionError> {
        let res = strategy.decrypt(dek, self.ciphertext.clone(), encryption_data).await?;
        String::from_utf8(res).map_err(|_|
            EncryptionError::new("Error translating response to utf8".to_string())
        )
    }
}

#[cfg(feature = "serde")]
#[async_trait]
impl ReleaseHeldValue<serde_json::Value> for HeldValue {
    async fn release<S: EncryptionStrategy<EncryptionData = ED> + Send + Sync, ED: Send>(
        &self,
        dek: DEK,
        strategy: &S,
        encryption_data: ED
    ) -> Result<serde_json::Value, EncryptionError> {
        let res = strategy.decrypt(dek, self.ciphertext.clone(), encryption_data).await?;
        serde_json
            ::from_slice(res.as_slice())
            .map_err(|e|
                EncryptionError(
                    format!("Could not convert from slice to json value, Error: {:?}", e)
                )
            )
    }
}