Skip to main content

firecloud_crypto/
keys.rs

1//! Key management - Master Key, KEK, DEK hierarchy
2
3use crate::{CryptoError, CryptoResult, KEY_SIZE};
4use argon2::{password_hash::SaltString, Argon2, PasswordHasher};
5use ed25519_dalek::{SigningKey, VerifyingKey};
6use rand::rngs::OsRng;
7use serde::{Deserialize, Serialize};
8use zeroize::{Zeroize, ZeroizeOnDrop};
9
10/// Master key derived from user password
11#[derive(Clone, Zeroize, ZeroizeOnDrop)]
12pub struct MasterKey {
13    key: [u8; KEY_SIZE],
14}
15
16impl MasterKey {
17    /// Derive master key from password using Argon2id
18    ///
19    /// Uses secure defaults: 256MB memory, 4 iterations, 4 parallelism
20    pub fn derive_from_password(password: &str, salt: &[u8]) -> CryptoResult<Self> {
21        let argon2 = Argon2::default();
22
23        // Use provided salt or generate one
24        let salt_string = SaltString::encode_b64(salt)
25            .map_err(|e| CryptoError::KeyDerivation(e.to_string()))?;
26
27        let hash = argon2
28            .hash_password(password.as_bytes(), &salt_string)
29            .map_err(|e| CryptoError::KeyDerivation(e.to_string()))?;
30
31        let hash_bytes = hash.hash.ok_or_else(|| {
32            CryptoError::KeyDerivation("No hash output".to_string())
33        })?;
34
35        let mut key = [0u8; KEY_SIZE];
36        key.copy_from_slice(&hash_bytes.as_bytes()[..KEY_SIZE]);
37
38        Ok(Self { key })
39    }
40
41    /// Generate a random master key (for testing or key escrow)
42    pub fn generate() -> CryptoResult<Self> {
43        let mut key = [0u8; KEY_SIZE];
44        rand::RngCore::fill_bytes(&mut OsRng, &mut key);
45        Ok(Self { key })
46    }
47
48    /// Get the raw key bytes (use carefully!)
49    pub fn as_bytes(&self) -> &[u8; KEY_SIZE] {
50        &self.key
51    }
52
53    /// Generate a random salt for password derivation
54    pub fn generate_salt() -> [u8; 16] {
55        let mut salt = [0u8; 16];
56        rand::RngCore::fill_bytes(&mut OsRng, &mut salt);
57        salt
58    }
59}
60
61/// Keys derived from master key using HKDF
62#[derive(Clone, Zeroize, ZeroizeOnDrop)]
63pub struct DerivedKeys {
64    /// Key Encryption Key - encrypts per-file DEKs
65    pub kek: [u8; KEY_SIZE],
66    /// Authentication key - for signing metadata
67    pub auth_key: [u8; KEY_SIZE],
68}
69
70impl DerivedKeys {
71    /// Derive KEK and auth key from master key using BLAKE3 KDF
72    pub fn derive_from_master(master: &MasterKey) -> Self {
73        // Use BLAKE3's keyed hash as a simple KDF
74        let kek = *blake3::keyed_hash(master.as_bytes(), b"firecloud-kek-v1").as_bytes();
75        let auth_key = *blake3::keyed_hash(master.as_bytes(), b"firecloud-auth-v1").as_bytes();
76
77        Self { kek, auth_key }
78    }
79}
80
81/// Ed25519 key pair for signing and identity
82#[derive(Clone)]
83pub struct KeyPair {
84    signing_key: SigningKey,
85}
86
87impl KeyPair {
88    /// Generate a new random key pair
89    pub fn generate() -> Self {
90        let signing_key = SigningKey::generate(&mut OsRng);
91        Self { signing_key }
92    }
93
94    /// Create from seed bytes (deterministic)
95    pub fn from_seed(seed: &[u8; 32]) -> Self {
96        let signing_key = SigningKey::from_bytes(seed);
97        Self { signing_key }
98    }
99
100    /// Get the public (verifying) key
101    pub fn public_key(&self) -> VerifyingKey {
102        self.signing_key.verifying_key()
103    }
104
105    /// Get the public key as bytes
106    pub fn public_key_bytes(&self) -> [u8; 32] {
107        self.public_key().to_bytes()
108    }
109
110    /// Sign a message
111    pub fn sign(&self, message: &[u8]) -> [u8; 64] {
112        use ed25519_dalek::Signer;
113        self.signing_key.sign(message).to_bytes()
114    }
115
116    /// Verify a signature
117    pub fn verify(&self, message: &[u8], signature: &[u8; 64]) -> CryptoResult<()> {
118        use ed25519_dalek::{Signature, Verifier};
119        let sig = Signature::from_bytes(signature);
120        self.public_key()
121            .verify(message, &sig)
122            .map_err(|_| CryptoError::SignatureVerification)
123    }
124
125    /// Export the signing key bytes (for secure storage)
126    pub fn to_bytes(&self) -> [u8; 32] {
127        self.signing_key.to_bytes()
128    }
129
130    /// Import from signing key bytes
131    pub fn from_bytes(bytes: &[u8; 32]) -> Self {
132        Self {
133            signing_key: SigningKey::from_bytes(bytes),
134        }
135    }
136}
137
138/// Generate a random Data Encryption Key (DEK) for a file
139pub fn generate_dek() -> [u8; KEY_SIZE] {
140    let mut dek = [0u8; KEY_SIZE];
141    rand::RngCore::fill_bytes(&mut OsRng, &mut dek);
142    dek
143}
144
145/// Encrypted DEK stored in file manifest
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct EncryptedDek {
148    /// Encrypted DEK bytes
149    pub ciphertext: Vec<u8>,
150    /// Nonce used for encryption
151    pub nonce: [u8; 24],
152}