artisan_keystore 2.1.1

A keystore server designed for AH
Documentation
//! Encryption helpers used by the keystore.
//!
//! The functions in this module provide symmetric encryption using AES-256-GCM
//! with randomly generated keys.  The encryption routines embed the key and
//! nonce in the resulting ciphertext so that the same buffer can be used for
//! decryption.

use aes_gcm::aead::{Aead, KeyInit};
use aes_gcm::{Aes256Gcm, Key, Nonce};
use dusa_collection_utils::core::types::stringy::Stringy;
// AES-GCM with a 256-bit key
use rand::Rng;
use simple_comms::protocol::encryption::generate_key;

#[allow(unused_assignments)]
/// Size in bytes of the generated nonce used for AES-GCM.
const NONCE_SIZE: usize = 12; // GCM Nonce size
/// Size in bytes of the ephemeral encryption key.
const KEY_SIZE: usize = 32; // 256-bit key

/// Encrypt data and return a hex encoded string containing the random key,
/// nonce and ciphertext. The key and nonce are prepended to the ciphertext so
/// that [`decrypt_with_embedded_key`] can recover the plain text.
pub fn encrypt_with_embedded_key(data: &[u8]) -> Result<Stringy, String> {
    // Generate a random key and nonce
    let mut key: [u8; 32] = [0u8; 32];
    generate_key(&mut key);
    let cipher = Aes256Gcm::new(&key.into());
    let nonce_bytes = rand::thread_rng().r#gen::<[u8; NONCE_SIZE]>();
    let nonce = Nonce::from_slice(&nonce_bytes);

    // Encrypt the data
    let ciphertext = cipher.encrypt(nonce, data).map_err(|e| e.to_string())?;

    // Combine the key, nonce, and ciphertext into a single byte stream
    let mut result = Vec::with_capacity(KEY_SIZE + NONCE_SIZE + ciphertext.len());
    result.extend_from_slice(&key);
    result.extend_from_slice(nonce);
    result.extend_from_slice(&ciphertext);

    let cipher_text = Stringy::from(hex::encode(result));

    Ok(cipher_text)
}

/// Decrypt a buffer created by [`encrypt_with_embedded_key`].
pub fn decrypt_with_embedded_key(encrypted_cipher_data: &[u8]) -> Result<Vec<u8>, String> {
    let encrypted_data: Vec<u8> =
        hex::decode(encrypted_cipher_data).map_err(|err| err.to_string())?;

    // Extract the key, nonce, and ciphertext
    if encrypted_data.len() <= KEY_SIZE + NONCE_SIZE {
        return Err("Encrypted data is too short".to_string());
    }

    let key = Key::<Aes256Gcm>::from_slice(&encrypted_data[..KEY_SIZE]);
    let cipher = Aes256Gcm::new(key);
    let nonce = Nonce::from_slice(&encrypted_data[KEY_SIZE..KEY_SIZE + NONCE_SIZE]);
    let ciphertext = &encrypted_data[KEY_SIZE + NONCE_SIZE..];

    // Decrypt the data
    cipher.decrypt(nonce, ciphertext).map_err(|e| e.to_string())
}