firecloud-crypto 0.1.0

Encryption and key management for FireCloud distributed storage
Documentation
//! Key management - Master Key, KEK, DEK hierarchy

use crate::{CryptoError, CryptoResult, KEY_SIZE};
use argon2::{password_hash::SaltString, Argon2, PasswordHasher};
use ed25519_dalek::{SigningKey, VerifyingKey};
use rand::rngs::OsRng;
use serde::{Deserialize, Serialize};
use zeroize::{Zeroize, ZeroizeOnDrop};

/// Master key derived from user password
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
pub struct MasterKey {
    key: [u8; KEY_SIZE],
}

impl MasterKey {
    /// Derive master key from password using Argon2id
    ///
    /// Uses secure defaults: 256MB memory, 4 iterations, 4 parallelism
    pub fn derive_from_password(password: &str, salt: &[u8]) -> CryptoResult<Self> {
        let argon2 = Argon2::default();

        // Use provided salt or generate one
        let salt_string = SaltString::encode_b64(salt)
            .map_err(|e| CryptoError::KeyDerivation(e.to_string()))?;

        let hash = argon2
            .hash_password(password.as_bytes(), &salt_string)
            .map_err(|e| CryptoError::KeyDerivation(e.to_string()))?;

        let hash_bytes = hash.hash.ok_or_else(|| {
            CryptoError::KeyDerivation("No hash output".to_string())
        })?;

        let mut key = [0u8; KEY_SIZE];
        key.copy_from_slice(&hash_bytes.as_bytes()[..KEY_SIZE]);

        Ok(Self { key })
    }

    /// Generate a random master key (for testing or key escrow)
    pub fn generate() -> CryptoResult<Self> {
        let mut key = [0u8; KEY_SIZE];
        rand::RngCore::fill_bytes(&mut OsRng, &mut key);
        Ok(Self { key })
    }

    /// Get the raw key bytes (use carefully!)
    pub fn as_bytes(&self) -> &[u8; KEY_SIZE] {
        &self.key
    }

    /// Generate a random salt for password derivation
    pub fn generate_salt() -> [u8; 16] {
        let mut salt = [0u8; 16];
        rand::RngCore::fill_bytes(&mut OsRng, &mut salt);
        salt
    }
}

/// Keys derived from master key using HKDF
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
pub struct DerivedKeys {
    /// Key Encryption Key - encrypts per-file DEKs
    pub kek: [u8; KEY_SIZE],
    /// Authentication key - for signing metadata
    pub auth_key: [u8; KEY_SIZE],
}

impl DerivedKeys {
    /// Derive KEK and auth key from master key using BLAKE3 KDF
    pub fn derive_from_master(master: &MasterKey) -> Self {
        // Use BLAKE3's keyed hash as a simple KDF
        let kek = *blake3::keyed_hash(master.as_bytes(), b"firecloud-kek-v1").as_bytes();
        let auth_key = *blake3::keyed_hash(master.as_bytes(), b"firecloud-auth-v1").as_bytes();

        Self { kek, auth_key }
    }
}

/// Ed25519 key pair for signing and identity
#[derive(Clone)]
pub struct KeyPair {
    signing_key: SigningKey,
}

impl KeyPair {
    /// Generate a new random key pair
    pub fn generate() -> Self {
        let signing_key = SigningKey::generate(&mut OsRng);
        Self { signing_key }
    }

    /// Create from seed bytes (deterministic)
    pub fn from_seed(seed: &[u8; 32]) -> Self {
        let signing_key = SigningKey::from_bytes(seed);
        Self { signing_key }
    }

    /// Get the public (verifying) key
    pub fn public_key(&self) -> VerifyingKey {
        self.signing_key.verifying_key()
    }

    /// Get the public key as bytes
    pub fn public_key_bytes(&self) -> [u8; 32] {
        self.public_key().to_bytes()
    }

    /// Sign a message
    pub fn sign(&self, message: &[u8]) -> [u8; 64] {
        use ed25519_dalek::Signer;
        self.signing_key.sign(message).to_bytes()
    }

    /// Verify a signature
    pub fn verify(&self, message: &[u8], signature: &[u8; 64]) -> CryptoResult<()> {
        use ed25519_dalek::{Signature, Verifier};
        let sig = Signature::from_bytes(signature);
        self.public_key()
            .verify(message, &sig)
            .map_err(|_| CryptoError::SignatureVerification)
    }

    /// Export the signing key bytes (for secure storage)
    pub fn to_bytes(&self) -> [u8; 32] {
        self.signing_key.to_bytes()
    }

    /// Import from signing key bytes
    pub fn from_bytes(bytes: &[u8; 32]) -> Self {
        Self {
            signing_key: SigningKey::from_bytes(bytes),
        }
    }
}

/// Generate a random Data Encryption Key (DEK) for a file
pub fn generate_dek() -> [u8; KEY_SIZE] {
    let mut dek = [0u8; KEY_SIZE];
    rand::RngCore::fill_bytes(&mut OsRng, &mut dek);
    dek
}

/// Encrypted DEK stored in file manifest
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EncryptedDek {
    /// Encrypted DEK bytes
    pub ciphertext: Vec<u8>,
    /// Nonce used for encryption
    pub nonce: [u8; 24],
}