sentinel_crypto/
lib.rs

1//! # Sentinel Crypto
2//!
3//! A modular, secure cryptographic library for the Sentinel document database.
4//! This crate provides hashing and digital signature operations with a focus
5//! on maintainability, security, and performance.
6//!
7//! ## Design Principles
8//!
9//! - **Modular Architecture**: Traits are separated from implementations, allowing easy algorithm
10//!   switching and testing.
11//! - **Security First**: All sensitive data is automatically zeroized. Sealed traits prevent
12//!   external insecure implementations.
13//! - **Unified Error Handling**: Single `CryptoError` enum for consistent error handling across all
14//!   operations.
15//! - **RustCrypto Only**: Uses only audited rustcrypto crates (blake3, ed25519-dalek) for
16//!   cryptographic primitives.
17//! - **Parallel Performance**: BLAKE3 supports parallel computation for large inputs.
18//!
19//! ## Security Features
20//!
21//! - **Memory Protection**: `SigningKey` and other sensitive types automatically zeroize memory
22//!   when dropped.
23//! - **Sealed Traits**: Prevents external implementations that might bypass security.
24//! - **Type Safety**: Associated types ensure compile-time correctness.
25//! - **Error Abstraction**: Errors don't leak sensitive information.
26//!
27//! ## Performance
28//!
29//! - BLAKE3: High-performance hash function with parallel support.
30//! - Ed25519: Fast elliptic curve signatures with 128-bit security.
31//!
32//! ## Usage
33//!
34//! ```rust
35//! use sentinel_crypto::{hash_data, sign_hash, verify_signature, SigningKey};
36//!
37//! let data = serde_json::json!({"key": "value"});
38//! let hash = hash_data(&data).unwrap();
39//!
40//! let key = SigningKey::from_bytes(&rand::random::<[u8; 32]>());
41//! let signature = sign_hash(&hash, &key).unwrap();
42//!
43//! let public_key = key.verifying_key();
44//! assert!(verify_signature(&hash, &signature, &public_key).unwrap());
45//! ```
46
47pub mod crypto_config;
48pub mod encrypt;
49pub mod encrypt_trait;
50pub mod error;
51pub mod hash;
52pub mod hash_trait;
53pub mod key_derivation;
54pub mod key_derivation_trait;
55pub mod sign;
56pub mod sign_trait;
57
58// Re-export crypto types for convenience
59pub use crypto_config::*;
60pub use ed25519_dalek::{Signature, SigningKey, VerifyingKey};
61pub use encrypt::{Aes256GcmSivEncryptor, Ascon128Encryptor, EncryptionKeyManager, XChaCha20Poly1305Encryptor};
62pub use encrypt_trait::EncryptionAlgorithm;
63pub use error::CryptoError;
64pub use hash_trait::HashFunction;
65pub use key_derivation::{Argon2KeyDerivation, Pbkdf2KeyDerivation};
66pub use key_derivation_trait::KeyDerivationFunction;
67// Convenience functions using default implementations
68use serde_json::Value;
69pub use sign::{Ed25519Signer, SigningKeyManager};
70pub use sign_trait::SignatureAlgorithm;
71use tracing::{debug, trace};
72
73/// Computes the hash of the given JSON data using the globally configured algorithm.
74pub fn hash_data(data: &Value) -> Result<String, CryptoError> {
75    trace!("Hashing data using global config");
76    let result = match get_global_crypto_config().hash_algorithm {
77        HashAlgorithmChoice::Blake3 => crate::hash::Blake3Hasher::hash_data(data),
78    };
79    if let Ok(ref hash) = result {
80        debug!("Data hashed successfully: {}", hash);
81    }
82    result
83}
84
85/// Signs the given hash using the globally configured algorithm.
86pub fn sign_hash(hash: &str, private_key: &SigningKey) -> Result<String, CryptoError> {
87    trace!("Signing hash using global config");
88    let result = match get_global_crypto_config().signature_algorithm {
89        SignatureAlgorithmChoice::Ed25519 => Ed25519Signer::sign_hash(hash, private_key),
90    };
91    if let Ok(ref sig) = result {
92        debug!("Hash signed successfully: {}", sig);
93    }
94    result
95}
96
97/// Verifies the signature of the given hash using the globally configured algorithm.
98pub fn verify_signature(hash: &str, signature: &str, public_key: &VerifyingKey) -> Result<bool, CryptoError> {
99    trace!("Verifying signature using global config");
100    let result = match get_global_crypto_config().signature_algorithm {
101        SignatureAlgorithmChoice::Ed25519 => Ed25519Signer::verify_signature(hash, signature, public_key),
102    };
103    debug!("Signature verification result: {:?}", result);
104    result
105}
106
107/// Encrypts data using the globally configured algorithm.
108pub fn encrypt_data(data: &[u8], key: &[u8; 32]) -> Result<String, CryptoError> {
109    trace!(
110        "Encrypting data using global config, data length: {}",
111        data.len()
112    );
113    let result = match get_global_crypto_config().encryption_algorithm {
114        EncryptionAlgorithmChoice::XChaCha20Poly1305 => XChaCha20Poly1305Encryptor::encrypt_data(data, key),
115        EncryptionAlgorithmChoice::Aes256GcmSiv => Aes256GcmSivEncryptor::encrypt_data(data, key),
116        EncryptionAlgorithmChoice::Ascon128 => Ascon128Encryptor::encrypt_data(data, key),
117    };
118    if let Ok(ref encrypted) = result {
119        debug!(
120            "Data encrypted successfully, encrypted length: {}",
121            encrypted.len()
122        );
123    }
124    result
125}
126
127/// Decrypts data using the globally configured algorithm.
128pub fn decrypt_data(encrypted_data: &str, key: &[u8; 32]) -> Result<Vec<u8>, CryptoError> {
129    trace!(
130        "Decrypting data using global config, encrypted length: {}",
131        encrypted_data.len()
132    );
133    let result = match get_global_crypto_config().encryption_algorithm {
134        EncryptionAlgorithmChoice::XChaCha20Poly1305 => XChaCha20Poly1305Encryptor::decrypt_data(encrypted_data, key),
135        EncryptionAlgorithmChoice::Aes256GcmSiv => Aes256GcmSivEncryptor::decrypt_data(encrypted_data, key),
136        EncryptionAlgorithmChoice::Ascon128 => Ascon128Encryptor::decrypt_data(encrypted_data, key),
137    };
138    if let Ok(ref decrypted) = result {
139        debug!(
140            "Data decrypted successfully, plaintext length: {}",
141            decrypted.len()
142        );
143    }
144    result
145}
146
147/// Derives a 32-byte key from a passphrase using the globally configured algorithm.
148/// Returns the randomly generated salt and the derived key.
149pub fn derive_key_from_passphrase(passphrase: &str) -> Result<(Vec<u8>, [u8; 32]), CryptoError> {
150    trace!("Deriving key from passphrase using global config");
151    let result = match get_global_crypto_config().key_derivation_algorithm {
152        KeyDerivationAlgorithmChoice::Argon2id => Argon2KeyDerivation::derive_key_from_passphrase(passphrase),
153        KeyDerivationAlgorithmChoice::Pbkdf2 => Pbkdf2KeyDerivation::derive_key_from_passphrase(passphrase),
154    };
155    if result.is_ok() {
156        debug!("Key derivation completed successfully");
157    }
158    result
159}
160
161/// Derives a 32-byte key from a passphrase using the provided salt and the globally configured
162/// algorithm.
163pub fn derive_key_from_passphrase_with_salt(passphrase: &str, salt: &[u8]) -> Result<[u8; 32], CryptoError> {
164    trace!("Deriving key from passphrase with salt using global config");
165    let result = match get_global_crypto_config().key_derivation_algorithm {
166        KeyDerivationAlgorithmChoice::Argon2id => {
167            Argon2KeyDerivation::derive_key_from_passphrase_with_salt(passphrase, salt)
168        },
169        KeyDerivationAlgorithmChoice::Pbkdf2 => {
170            Pbkdf2KeyDerivation::derive_key_from_passphrase_with_salt(passphrase, salt)
171        },
172    };
173    if result.is_ok() {
174        debug!("Key derivation with salt completed successfully");
175    }
176    result
177}
178
179#[cfg(test)]
180mod tests {
181    use super::*;
182
183    #[test]
184    fn test_hash_data() {
185        let data = serde_json::json!({"key": "value", "number": 42});
186        let hash = hash_data(&data).unwrap();
187        assert_eq!(hash.len(), 64);
188        let hash2 = hash_data(&data).unwrap();
189        assert_eq!(hash, hash2);
190    }
191
192    #[test]
193    fn test_global_config() {
194        // Test that global config is accessible
195        let config = get_global_crypto_config();
196        // Just check that it's set, don't assert specific values since other tests may change it
197        assert!(matches!(config.hash_algorithm, HashAlgorithmChoice::Blake3));
198        assert!(matches!(
199            config.signature_algorithm,
200            SignatureAlgorithmChoice::Ed25519
201        ));
202        // Encryption and key derivation may be changed by other tests
203        // assert!(matches!(
204        //     config.encryption_algorithm,
205        //     EncryptionAlgorithmChoice::XChaCha20Poly1305
206        // ));
207        // assert!(matches!(
208        //     config.key_derivation_algorithm,
209        //     KeyDerivationAlgorithmChoice::Argon2id
210        // ));
211
212        // Test that default functions work
213        let data = serde_json::json!({"test": "data"});
214        let hash = hash_data(&data).unwrap();
215        assert_eq!(hash.len(), 64); // Blake3 hash
216
217        let key = [0u8; 32];
218        let test_data = b"test data";
219        let encrypted = encrypt_data(test_data, &key).unwrap();
220        let decrypted = decrypt_data(&encrypted, &key).unwrap();
221        assert_eq!(test_data, decrypted.as_slice());
222
223        let derived = derive_key_from_passphrase("test passphrase").unwrap();
224        assert_eq!(derived.1.len(), 32);
225        assert_eq!(derived.0.len(), 32);
226    }
227
228    #[test]
229    fn test_aes256gcm_siv_encryption() {
230        let config = CryptoConfig {
231            hash_algorithm:           HashAlgorithmChoice::Blake3,
232            signature_algorithm:      SignatureAlgorithmChoice::Ed25519,
233            encryption_algorithm:     EncryptionAlgorithmChoice::Aes256GcmSiv,
234            key_derivation_algorithm: KeyDerivationAlgorithmChoice::Argon2id,
235        };
236        let _ = set_global_crypto_config(config); // Ignore if already set
237
238        let key = [0u8; 32];
239        let test_data = b"test data";
240        let encrypted = encrypt_data(test_data, &key).unwrap();
241        let decrypted = decrypt_data(&encrypted, &key).unwrap();
242        assert_eq!(test_data, decrypted.as_slice());
243    }
244
245    #[test]
246    fn test_ascon128_encryption() {
247        let config = CryptoConfig {
248            hash_algorithm:           HashAlgorithmChoice::Blake3,
249            signature_algorithm:      SignatureAlgorithmChoice::Ed25519,
250            encryption_algorithm:     EncryptionAlgorithmChoice::Ascon128,
251            key_derivation_algorithm: KeyDerivationAlgorithmChoice::Argon2id,
252        };
253        let _ = set_global_crypto_config(config); // Ignore if already set
254
255        let key = [0u8; 32];
256        let test_data = b"test data";
257        let encrypted = encrypt_data(test_data, &key).unwrap();
258        let decrypted = decrypt_data(&encrypted, &key).unwrap();
259        assert_eq!(test_data, decrypted.as_slice());
260    }
261
262    #[test]
263    fn test_pbkdf2_key_derivation() {
264        let config = CryptoConfig {
265            hash_algorithm:           HashAlgorithmChoice::Blake3,
266            signature_algorithm:      SignatureAlgorithmChoice::Ed25519,
267            encryption_algorithm:     EncryptionAlgorithmChoice::XChaCha20Poly1305,
268            key_derivation_algorithm: KeyDerivationAlgorithmChoice::Pbkdf2,
269        };
270        let _ = set_global_crypto_config(config); // Ignore if already set
271
272        let derived = derive_key_from_passphrase("test passphrase").unwrap();
273        assert_eq!(derived.1.len(), 32);
274        assert_eq!(derived.0.len(), 32);
275    }
276
277    #[test]
278    fn test_sign_and_verify_hash() {
279        let data = serde_json::json!({"key": "value"});
280        let hash = hash_data(&data).unwrap();
281        assert_eq!(hash.len(), 64);
282
283        let key_bytes = [0u8; 32];
284        let key = SigningKey::from_bytes(&key_bytes);
285        let signature = sign_hash(&hash, &key).unwrap();
286
287        let public_key = key.verifying_key();
288        let verified = verify_signature(&hash, &signature, &public_key).unwrap();
289        assert!(verified);
290
291        // Test invalid signature (wrong hash)
292        let invalid_verified = verify_signature("wrong_hash", &signature, &public_key).unwrap();
293        assert!(!invalid_verified);
294
295        // Test invalid hex
296        let hex_error = verify_signature(&hash, "invalid", &public_key);
297        assert!(hex_error.is_err());
298    }
299}