1use aes_gcm::aead::Aead;
2use aes_gcm::{AeadInOut, Aes256Gcm, Tag};
3use aes_gcm::{KeyInit, Nonce};
4use anyhow::Result;
5use rand::Rng;
6use x25519_dalek::{EphemeralSecret, PublicKey};
7
8pub const NONCE_LEN: usize = 12;
9pub const TAG_LEN: usize = 16;
10
11#[inline]
14pub fn encrypt_data(data: &[u8], key: &[u8; 32]) -> Result<Vec<u8>> {
15 let mut nonce = [0u8; NONCE_LEN];
17 rand::rng().fill_bytes(&mut nonce);
18
19 let cipher = Aes256Gcm::new(key.into());
20
21 let ciphertext = cipher
22 .encrypt(&nonce.into(), data)
23 .map_err(|e| anyhow::anyhow!("Encryption failed: {}", e))?;
24
25 let mut out = vec![0u8; NONCE_LEN + ciphertext.len()];
27 out[..NONCE_LEN].copy_from_slice(&nonce);
28 out[NONCE_LEN..].copy_from_slice(&ciphertext); Ok(out)
30}
31
32#[inline]
35pub fn decrypt_data(data: &[u8], key: &[u8; 32]) -> Result<Vec<u8>> {
36 if data.len() < NONCE_LEN + TAG_LEN {
37 anyhow::bail!("Ciphertext too short");
38 }
39
40 let (nonce, ciphertext) = data.split_at(NONCE_LEN);
41
42 let nonce = Nonce::try_from(nonce)?;
43 let cipher = Aes256Gcm::new(key.into());
44
45 let plaintext = cipher
46 .decrypt(&nonce, ciphertext)
47 .map_err(|e| anyhow::anyhow!("Decryption failed: {}", e))?;
48 Ok(plaintext)
49}
50
51#[inline(always)]
55pub fn encrypt_into(input: &[u8], output: &mut [u8], key: &[u8; 32]) -> Result<usize> {
56 let plaintext_len = input.len();
57
58 if output.len() < NONCE_LEN + plaintext_len + TAG_LEN {
59 anyhow::bail!(
60 "Output buffer too small, need at least {} bytes",
61 NONCE_LEN + plaintext_len + TAG_LEN
62 );
63 }
64
65 let (nonce_buf, data_tag) = output.split_at_mut(NONCE_LEN);
66 let (data, tag_buf) = data_tag.split_at_mut(plaintext_len);
67
68 rand::rng().fill_bytes(nonce_buf);
70 data.copy_from_slice(input);
72
73 let cipher = Aes256Gcm::new(key.into());
74 let nonce = Nonce::try_from(&*nonce_buf)?;
75
76 let auth_tag = cipher
77 .encrypt_inout_detached(&nonce, b"", data.into())
78 .map_err(|e| anyhow::anyhow!(e))?;
79
80 tag_buf.copy_from_slice(&auth_tag);
81
82 Ok(NONCE_LEN + plaintext_len + TAG_LEN)
83}
84
85#[inline(always)]
89pub fn decrypt_into(input: &[u8], output: &mut [u8], key: &[u8; 32]) -> Result<usize> {
90 if input.len() < NONCE_LEN + TAG_LEN {
91 anyhow::bail!("Ciphertext too short");
92 }
93 let ciphertext_len = input.len() - NONCE_LEN - TAG_LEN;
94
95 if output.len() < ciphertext_len {
96 anyhow::bail!(
97 "Output buffer too small, need at least {} bytes",
98 ciphertext_len
99 );
100 }
101 let cipher = Aes256Gcm::new(key.into());
102
103 let nonce = Nonce::try_from(&input[..NONCE_LEN])?;
105 let tag = Tag::try_from(&input[NONCE_LEN + ciphertext_len..])?;
106 let data = &mut output[..ciphertext_len];
108 data.copy_from_slice(&input[NONCE_LEN..NONCE_LEN + ciphertext_len]);
109
110 cipher
111 .decrypt_inout_detached(&nonce, b"", data.into(), &tag)
112 .map_err(|e| anyhow::anyhow!(e))?;
113
114 Ok(ciphertext_len)
115}
116
117#[inline(always)]
119pub fn generate_x25519_keypair() -> Result<(EphemeralSecret, PublicKey)> {
120 let private_key = EphemeralSecret::random_from_rng(&mut rand::rng());
121 let public_key = PublicKey::from(&private_key);
122 Ok((private_key, public_key))
123}
124
125#[test]
126fn crypto_test() -> Result<()> {
127 let mut key = [0u8; 32];
128 rand::rng().fill_bytes(&mut key);
129
130 let mut data = vec![0u8; 64 * 1024 * 1024];
131 rand::rng().fill_bytes(&mut data);
132
133 let encrypted = encrypt_data(&data, &key)?;
134 assert_eq!(encrypted.len(), data.len() + NONCE_LEN + TAG_LEN);
135
136 let decrypted = decrypt_data(&encrypted, &key)?;
137 assert_eq!(decrypted, data);
138
139 let data: &[u8] = b"";
140
141 let encrypted = encrypt_data(data, &key)?;
142 assert_eq!(encrypted.len(), NONCE_LEN + TAG_LEN);
143
144 let decrypted = decrypt_data(&encrypted, &key)?;
145 assert!(decrypted.is_empty());
146
147 Ok(())
148}
149
150#[test]
151fn crypto_in_place_test() -> Result<()> {
152 let mut key = [0u8; 32];
153 rand::rng().fill_bytes(&mut key);
154
155 let mut data = vec![0u8; 64 * 1024 * 1024];
156 rand::rng().fill_bytes(&mut data);
157
158 let mut encrypted_buf = vec![0u8; NONCE_LEN + data.len() + TAG_LEN];
159 encrypt_into(&data, &mut encrypted_buf, &key)?;
160 assert_eq!(encrypted_buf.len(), data.len() + NONCE_LEN + TAG_LEN);
161
162 let mut decrypted_buf = vec![0u8; data.len()];
163 decrypt_into(&encrypted_buf, &mut decrypted_buf, &key)?;
164 assert_eq!(decrypted_buf, data);
165
166 let empty = Vec::new();
167 let mut encrypted_buf = vec![0u8; NONCE_LEN + TAG_LEN];
168 encrypt_into(&empty, &mut encrypted_buf, &key)?;
169 assert_eq!(encrypted_buf.len(), NONCE_LEN + TAG_LEN);
170
171 let mut decrypted_buf = vec![0u8; encrypted_buf.len()];
172 let plaintext_len = decrypt_into(&encrypted_buf, &mut decrypted_buf, &key)?;
173 assert_eq!(plaintext_len, 0);
174
175 Ok(())
176}