rust_keyvault/
lib.rs

1//! rust-keyvault: A secure key management library for Rust
2//!
3//! This crate provides foundational abstraction for cryptographic key management,,
4//! focusing on security, correctness and composability.
5
6#![forbid(unsafe_code)]
7#![warn(missing_docs)]
8
9use rand_chacha::ChaCha20Rng;
10use rand_core::{RngCore, SeedableRng};
11use serde::{Deserialize, Serialize};
12use std::fmt;
13use std::time::SystemTime;
14use zeroize::{Zeroize, ZeroizeOnDrop};
15
16pub mod audit;
17pub mod backup;
18pub mod crypto;
19pub mod error;
20pub mod export;
21pub mod key;
22pub mod storage;
23
24pub use error::{Error, Result};
25
26/// A unique identifier for a cryptographic key.
27#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
28pub struct KeyId([u8; 16]);
29
30impl KeyId {
31    /// Generate a new random KeyId
32    pub fn generate() -> Result<Self> {
33        let mut rng = ChaCha20Rng::from_entropy();
34        let mut bytes = [0u8; 16];
35        rng.fill_bytes(&mut bytes);
36
37        Ok(Self(bytes))
38    }
39
40    /// Create a KeyId from raw bytes.
41    pub const fn from_bytes(bytes: [u8; 16]) -> Self {
42        Self(bytes)
43    }
44
45    /// Get the raw bytes of the KeyId.
46    pub const fn as_bytes(&self) -> &[u8; 16] {
47        &self.0
48    }
49
50    /// Generate a versioned KeyId based on a base ID and version
51    pub fn generate_versioned(base_id: &KeyId, version: u32) -> Result<Self> {
52        use sha2::{Digest, Sha256};
53        let mut hasher = Sha256::new();
54        hasher.update(base_id.as_bytes());
55        hasher.update(version.to_le_bytes());
56        hasher.update(b"rust-keyvault-version");
57
58        let hash = hasher.finalize();
59        let mut id_bytes = [0u8; 16];
60        id_bytes.copy_from_slice(&hash[..16]);
61
62        Ok(Self(id_bytes))
63    }
64
65    /// Check for same base id for any two keys
66    pub fn same_base_id(_id1: &KeyId, _id2: &KeyId) -> bool {
67        false
68    }
69
70    /// Generate a new random base KeyId for a key family
71    pub fn generate_base() -> Result<Self> {
72        let mut rng = ChaCha20Rng::from_entropy();
73        let mut bytes = [0u8; 16];
74        rng.fill_bytes(&mut bytes);
75
76        Ok(Self(bytes))
77    }
78}
79
80impl fmt::Display for KeyId {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        write!(f, "{}", hex::encode(self.0))
83    }
84}
85
86/// Represents the lifecycle state of a key
87#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
88pub enum KeyState {
89    /// Key has been generated but is not yet active.
90    Pending,
91    /// Key is currently active for all operations.
92    Active,
93    /// Key is being rotated out (new key active, this key is still valid).
94    Rotating,
95    /// Key is deprecated (valid for verification only), should not be used for new operations.
96    Deprecated,
97    /// Key has been revoked (should not be used at all).
98    Revoked,
99}
100
101/// Metadata about a key
102#[derive(Clone, Debug, Serialize, Deserialize)]
103pub struct KeyMetadata {
104    /// Unique identifier for the key
105    pub id: KeyId,
106    /// Base identifier for the key
107    pub base_id: KeyId,
108    /// Current state in the key lifecycle
109    pub state: KeyState,
110    /// When this key was created
111    pub created_at: SystemTime,
112    /// When this key expires (if applicable)
113    pub expires_at: Option<SystemTime>,
114    /// Algorithm this key is used with
115    pub algorithm: Algorithm,
116    /// Version number for rotation tracking
117    pub version: u32,
118}
119
120/// Supported cryptographic algorithms
121#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
122pub enum Algorithm {
123    /// ChaCha20-Poly1305 AEAD (12-byte nonce)
124    ChaCha20Poly1305,
125    /// XChaCha20-Poly1305 AEAD (24-byte nonce - safer for random nonces)
126    XChaCha20Poly1305,
127    /// AES-256-GCM AEAD
128    Aes256Gcm,
129    /// Ed25519 signature
130    Ed25519,
131    /// X25519 key exchange
132    X25519,
133}
134
135impl Algorithm {
136    /// Get the key size in bytes for the algorithm
137    pub const fn key_size(&self) -> usize {
138        match self {
139            Self::ChaCha20Poly1305
140            | Self::XChaCha20Poly1305  // ← Add
141            | Self::Aes256Gcm => 32,
142            Self::Ed25519 | Self::X25519 => 32,
143        }
144    }
145
146    /// Check if this algorithm is for symmetric encrytion
147    pub const fn is_symmetric(&self) -> bool {
148        matches!(self, Self::ChaCha20Poly1305 | Self::Aes256Gcm)
149    }
150
151    /// Get the nonce size for AEAD algorithms
152    pub const fn nonce_size(&self) -> Option<usize> {
153        match self {
154            Self::ChaCha20Poly1305 | Self::Aes256Gcm => Some(12),
155            Self::XChaCha20Poly1305 => Some(24), // ← Safe for random nonces!
156            _ => None,
157        }
158    }
159}
160
161impl Zeroize for Algorithm {
162    fn zeroize(&mut self) {
163        *self = Algorithm::ChaCha20Poly1305;
164    }
165}
166
167impl ZeroizeOnDrop for Algorithm {}
168
169#[cfg(test)]
170mod tests {
171    use super::*;
172
173    #[test]
174    fn test_algorithm_properties() {
175        assert_eq!(Algorithm::ChaCha20Poly1305.key_size(), 32);
176        assert!(Algorithm::ChaCha20Poly1305.is_symmetric());
177        assert!(!Algorithm::Ed25519.is_symmetric());
178    }
179}