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