xaes_gcm/
lib.rs

1use nonce_extension::nonce_extension_aes256;
2
3pub mod reexports {
4    pub use nonce_extension;
5}
6use aes_gcm::{
7    aead::{AeadInPlace, KeyInit},
8    Aes256Gcm, Nonce, Tag,
9};
10use getrandom::getrandom;
11
12/// A decryption error.
13#[derive(Debug)]
14pub struct Error;
15
16impl std::error::Error for Error {}
17
18impl std::fmt::Display for Error {
19    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
20        write!(f, "Error")
21    }
22}
23
24/// Double-Nonce-Derive-Key-AES-256-GCM: AES-256-GCM with a 192-bit nonce.
25///
26/// ```rust
27/// use xaes_gcm::*;
28///
29/// let key = XAes256Gcm::keygen();
30/// let plaintext = b"hello world";
31/// let ciphertext = XAes256Gcm::encrypt(&key, plaintext, None);
32/// let decrypted = XAes256Gcm::decrypt(&key, &ciphertext, None).unwrap();
33/// assert_eq!(plaintext, &decrypted[..]);
34/// ```
35pub struct XAes256Gcm;
36
37impl XAes256Gcm {
38    /// Generates a random AES-256 key.
39    ///
40    /// # Returns
41    ///
42    /// An array of 32 bytes representing the generated key.
43    pub fn keygen() -> [u8; 32] {
44        let mut key = [0u8; 32];
45        getrandom(&mut key).unwrap();
46        key
47    }
48
49    /// Encrypts a message with AES-256-GCM.
50    ///
51    /// # Arguments
52    ///
53    /// * `key` - A reference to a 32-byte array representing the AES-256 key.
54    /// * `plaintext` - The message to be encrypted.
55    /// * `associated_data` - Optional associated data to be authenticated but not encrypted.
56    ///
57    /// # Returns
58    ///
59    /// A vector containing the encrypted message, nonce, and tag.
60    pub fn encrypt(
61        key: &[u8; 32],
62        plaintext: impl AsRef<[u8]>,
63        associated_data: Option<&[u8]>,
64    ) -> Vec<u8> {
65        let plaintext = plaintext.as_ref();
66        let mut nonce = [0u8; 24];
67        getrandom(&mut nonce).unwrap();
68        let dk = nonce_extension_aes256(key, &nonce);
69        let ks = Aes256Gcm::new((&dk).into());
70        let mut out = Vec::with_capacity(nonce.len() + plaintext.len() + 16);
71        out.extend_from_slice(&nonce);
72        out.extend_from_slice(plaintext);
73        out.extend_from_slice(&[0u8; 16]);
74        let associated_data = associated_data.unwrap_or(&[]);
75        let zero_nonce = Nonce::from([0u8; 12]);
76        let tag = ks
77            .encrypt_in_place_detached(
78                &zero_nonce,
79                associated_data,
80                &mut out[nonce.len()..][0..plaintext.len()],
81            )
82            .unwrap();
83        out[nonce.len() + plaintext.len()..].copy_from_slice(tag.as_slice());
84        out
85    }
86
87    /// Decrypts a message with AES-256-GCM.
88    ///
89    /// # Arguments
90    ///
91    /// * `key` - A reference to a 32-byte array representing the AES-256 key.
92    /// * `ciphertext` - The message to be decrypted.
93    /// * `associated_data` - Optional associated data that was authenticated but not encrypted.
94    ///
95    /// # Returns
96    ///
97    /// - `Ok(Vec<u8>)` containing the decrypted plaintext if successful.
98    /// - `Err(Error)` if the ciphertext is too short or invalid.
99    pub fn decrypt(
100        key: &[u8; 32],
101        ciphertext: impl AsRef<[u8]>,
102        associated_data: Option<&[u8]>,
103    ) -> Result<Vec<u8>, Error> {
104        let ciphertext = ciphertext.as_ref();
105        if ciphertext.len() < 24 + 16 {
106            return Err(Error);
107        }
108        let nonce = &ciphertext[..24];
109        let dk = nonce_extension_aes256(key, nonce);
110        let ks = Aes256Gcm::new((&dk).into());
111        let plaintext_len = ciphertext.len() - (24 + 16);
112        let mut out = ciphertext[24..][0..plaintext_len].to_vec();
113        let associated_data = associated_data.unwrap_or(&[]);
114        let mut tag = Tag::default();
115        tag.as_mut_slice()
116            .copy_from_slice(&ciphertext[ciphertext.len() - 16..]);
117        let zero_nonce = Nonce::from([0u8; 12]);
118        ks.decrypt_in_place_detached(&zero_nonce, associated_data, &mut out, &tag)
119            .map_err(|_| Error)?;
120        Ok(out)
121    }
122}
123
124#[test]
125fn test() {
126    let key = XAes256Gcm::keygen();
127    let plaintext = b"hello world";
128    let ciphertext = XAes256Gcm::encrypt(&key, plaintext, None);
129    let decrypted = XAes256Gcm::decrypt(&key, ciphertext, None).unwrap();
130    assert_eq!(plaintext, &decrypted[..]);
131}