trelent-hyok 0.1.12

A Rust library implementing Hold Your Own Key (HYOK) encryption patterns with support for multiple cloud providers
Documentation
//! Hold Your Own Key (HYOK) encryption service.
//!
//! This library provides a flexible system for managing encrypted data using
//! customer-managed encryption keys. It supports:
//!
//! - Multiple key management services (AWS KMS, Azure Key Vault)
//! - Custom encryption strategies
//! - Key caching and persistence
//! - Type-safe data handling
//!
//! The library follows the envelope encryption pattern where data is encrypted
//! with Data Encryption Keys (DEKs) which are themselves protected by
//! Customer Managed Keys (CMKs).

pub mod cache;
pub mod cmk;
pub mod dek;
pub mod error;
mod held_data;
mod builder;
pub use builder::*;

pub use held_data::*;

use crate::dek::generator::DEKGenerator;
use crate::dek::persistence::DEKPersistService;
use crate::dek::scope::WithScope;
use crate::dek::DEK;
use crate::encryption::EncryptionStrategy;
use crate::error::encryption::EncryptionError;
use crate::error::generator::PersistError;
use futures_util::TryFutureExt;
use std::sync::Arc;

/// Main service for managing encrypted data using the HYOK pattern.
///
/// This service coordinates:
/// - Data Encryption Key (DEK) generation
/// - Key persistence and caching
/// - Encryption/decryption operations
/// - Scope-based key management
///
/// # Type Parameters
///
/// * `S` - The encryption strategy used for data protection
///
/// # Example
/// ```no_run
/// use hyokashi::{HYOKServiceBuilder, EncryptionStrategy};
///
///
///     let service = HYOKServiceBuilder::new()
///         .with_fixed_length_generator(32)
///         .with_aws_cmk(kms_client, "alias/my-key", algorithm)
///         .with_aws_persistence(secrets_client)
///         .build(encryption_strategy)?;
///
///     // Encrypt some data
///     let held = service.hold_value(data, "my-scope".into(), metadata).await?;
///
///     // Decrypt the data
///     let plain = service.release_value(held, "my-scope".into(), metadata).await?;
///
/// ```
pub struct HYOKService<S: EncryptionStrategy> {
    pub(crate) persist_service: Arc<DEKPersistService>,
    pub(crate) generator: DEKGenerator,
    pub(crate) strategy: S,
}

impl<S: EncryptionStrategy<EncryptionData = ED> + Send + Sync, ED: Send> HYOKService<S> {
    /// Creates a new HYOK service instance.
    ///
    /// # Arguments
    ///
    /// * `persist_service` - Service for key persistence and caching
    /// * `generator` - Strategy for generating new DEKs
    /// * `strategy` - Strategy for encrypting/decrypting data
    pub fn new(
        persist_service: Arc<DEKPersistService>,
        generator: DEKGenerator,
        strategy: S
    ) -> Self {
        Self {
            persist_service,
            generator,
            strategy,
        }
    }

    /// Decrypts an object using its scope to locate the appropriate DEK.
    ///
    /// # Arguments
    ///
    /// * `obj` - The encrypted object to decrypt
    ///
    /// # Type Parameters
    ///
    /// * `T` - The encrypted object type
    /// * `C` - The decrypted object type
    ///
    /// # Errors
    ///
    /// Returns an `EncryptionError` if:
    /// - DEK retrieval fails
    /// - Decryption fails
    /// - Type conversion fails
    pub async fn release_object<T, C>(&self, obj: &T) -> Result<C, EncryptionError>
        where T: ReleaseHeldObject<C, S> + WithScope
    {
        let scope = obj.get_scope();
        let dek = self
            .prep_dek(scope)
            .map_err(|e|
                EncryptionError::new(format!("Could not prep DEK, Error: {:?}", e))
            ).await?;
        let result = obj.release_object(dek.into(), &self.strategy).await;
        result
    }

    /// Decrypts a held value using the provided scope and metadata.
    ///
    /// # Arguments
    ///
    /// * `val` - The encrypted value to decrypt
    /// * `scope` - The scope identifier for locating the DEK
    /// * `encryption_data` - Additional metadata needed for decryption
    ///
    /// # Errors
    ///
    /// Returns an `EncryptionError` if:
    /// - DEK retrieval fails
    /// - Decryption fails
    pub async fn release_value(
        &self,
        val: HeldValue,
        scope: String,
        encryption_data: ED
    ) -> Result<Vec<u8>, EncryptionError> {
        let dek = self
            .prep_dek(scope)
            .map_err(|e|
                EncryptionError::new(format!("Could not prep DEK, Error: {:?}", e))
            ).await?;
        let result = val.release(dek.into(), &self.strategy, encryption_data).await;
        result
    }

    /// Encrypts data using a DEK associated with the provided scope.
    ///
    /// # Arguments
    ///
    /// * `val` - The data to encrypt
    /// * `scope` - The scope identifier for key management
    /// * `encryption_data` - Additional metadata needed for encryption
    ///
    /// # Errors
    ///
    /// Returns an `EncryptionError` if:
    /// - DEK retrieval or generation fails
    /// - Encryption fails
    pub async fn hold_value(
        &self,
        val: Vec<u8>,
        scope: String,
        encryption_data: ED
    ) -> Result<HeldValue, EncryptionError> {
        let dek = self
            .prep_dek(scope)
            .map_err(|e|
                EncryptionError::new(format!("Could not prep DEK, Error: {:?}", e))
            ).await?;
        Held(val, dek.into(), &self.strategy, encryption_data).await
    }

    /// Retrieves or generates a DEK for the specified scope.
    ///
    /// # Arguments
    ///
    /// * `scope` - The scope identifier for the DEK
    ///
    /// # Returns
    ///
    /// The DEK associated with the scope, generating and persisting
    /// a new one if none exists.
    ///
    /// # Errors
    ///
    /// Returns a `PersistError` if:
    /// - Key retrieval fails
    /// - Key generation fails
    /// - Key persistence fails
    pub async fn prep_dek(&self, scope: String) -> Result<DEK, PersistError> {
        let fetch_result = self.persist_service.fetch_dek(scope.clone()).await;
        let final_result = match fetch_result {
            Ok(dek) => Ok(dek.into()),
            _ => {
                let dek = self.generator
                    .new_dek()
                    .map_err(|e|
                        PersistError::Error(format!("Could not generate key, Error: {:?}", e))
                    )?;
                self.persist_service.save_dek(scope.clone(), dek.clone()).await?;
                Ok(dek.into())
            }
        };
        final_result
    }
}