sentinel_crypto/encrypt/
aes_gcm_siv.rs

1use aes_gcm_siv::{
2    aead::{Aead, KeyInit},
3    Aes256GcmSiv,
4    Key,
5    Nonce,
6};
7use rand::RngCore;
8
9use crate::{encrypt_trait::EncryptionAlgorithm, error::CryptoError};
10
11/// AES-256-GCM-SIV encryption implementation.
12/// Uses AES-256 in GCM-SIV mode for authenticated encryption with nonce misuse resistance.
13/// Provides both confidentiality and integrity with better nonce handling than standard GCM.
14///
15/// Design choice: GCM-SIV provides nonce misuse resistance, making it safer than standard GCM
16/// for applications where nonce uniqueness cannot be guaranteed. It's a rustcrypto crate,
17/// preferred for consistency.
18pub struct Aes256GcmSivEncryptor;
19
20impl EncryptionAlgorithm for Aes256GcmSivEncryptor {
21    fn encrypt_data(data: &[u8], key: &[u8; 32]) -> Result<String, CryptoError> {
22        let cipher = Aes256GcmSiv::new(Key::<Aes256GcmSiv>::from_slice(key));
23        let mut nonce_bytes = [0u8; 12];
24        rand::rng().fill_bytes(&mut nonce_bytes);
25        let nonce = Nonce::from_slice(&nonce_bytes);
26
27        let ciphertext = cipher
28            .encrypt(nonce, data)
29            .map_err(|_| CryptoError::Encryption)?;
30        let mut result = nonce_bytes.to_vec();
31        result.extend_from_slice(&ciphertext);
32        Ok(hex::encode(result))
33    }
34
35    fn decrypt_data(encrypted_data: &str, key: &[u8; 32]) -> Result<Vec<u8>, CryptoError> {
36        let data = hex::decode(encrypted_data).map_err(|_| CryptoError::Decryption)?;
37        if data.len() < 12 {
38            return Err(CryptoError::Decryption);
39        }
40        let (nonce_bytes, ciphertext) = data.split_at(12);
41        let cipher = Aes256GcmSiv::new(Key::<Aes256GcmSiv>::from_slice(key));
42        let nonce = Nonce::from_slice(nonce_bytes);
43        cipher
44            .decrypt(nonce, ciphertext)
45            .map_err(|_| CryptoError::Decryption)
46    }
47}
48
49impl crate::encrypt_trait::private::Sealed for Aes256GcmSivEncryptor {}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    #[test]
56    fn test_encrypt_decrypt() {
57        let key = [0u8; 32];
58        let data = b"Hello, world!";
59        let encrypted = Aes256GcmSivEncryptor::encrypt_data(data, &key).unwrap();
60        let decrypted = Aes256GcmSivEncryptor::decrypt_data(&encrypted, &key).unwrap();
61        assert_eq!(decrypted, data);
62    }
63
64    #[test]
65    fn test_decrypt_invalid_length() {
66        let key = [0u8; 32];
67        // Short data that decodes to less than 12 bytes
68        let short_hex = hex::encode(&[0u8; 10]); // 20 hex chars, 10 bytes
69        let result = Aes256GcmSivEncryptor::decrypt_data(&short_hex, &key);
70        assert!(result.is_err());
71    }
72}