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