encryption/encryptor/
aes256gcm_siv.rs1use aes_gcm_siv::aead::AeadInPlace;
10use aes_gcm_siv::{Aes256GcmSiv, KeyInit, Nonce, Tag};
11use rand_core::{OsRng, RngCore};
12
13use crate::contract::{Identified, SymmetricCipher};
14use crate::error::{Error, Result};
15use crate::key::Key;
16
17const LABEL: &str = "AES-256-GCM-SIV";
18const PREFIX: &[u8] = b"v1";
19const NONCE_LEN: usize = 12;
20const TAG_LEN: usize = 16;
21
22pub struct Aes256GcmSivEncryptor {
27 cipher: Aes256GcmSiv,
28}
29
30impl Aes256GcmSivEncryptor {
31 pub const IDENTIFIER: &'static str = "aes-256-gcm-siv";
32
33 pub fn new(key: [u8; 32]) -> Self {
34 Self {
35 cipher: Aes256GcmSiv::new_from_slice(&key).expect("key length is fixed at 32"),
36 }
37 }
38
39 pub fn from_slice(key: &[u8]) -> Result<Self> {
40 let key = Key::<32>::from_slice(key, LABEL)?;
42 Ok(Self {
43 cipher: Aes256GcmSiv::new_from_slice(key.as_bytes())
44 .expect("key length is fixed at 32"),
45 })
46 }
47
48 pub(crate) fn encrypt_with_nonce(
53 &self,
54 nonce: &[u8; NONCE_LEN],
55 plaintext: &[u8],
56 ) -> Result<Vec<u8>> {
57 let mut buffer = plaintext.to_vec();
58 let tag = self
59 .cipher
60 .encrypt_in_place_detached(Nonce::from_slice(nonce), b"", &mut buffer)
61 .map_err(|_| Error::EncryptionFailed { label: LABEL })?;
62
63 let mut blob = Vec::with_capacity(PREFIX.len() + NONCE_LEN + TAG_LEN + buffer.len());
64 blob.extend_from_slice(PREFIX);
65 blob.extend_from_slice(nonce);
66 blob.extend_from_slice(&tag);
67 blob.extend_from_slice(&buffer);
68 Ok(blob)
69 }
70}
71
72impl std::fmt::Debug for Aes256GcmSivEncryptor {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 f.debug_struct("Aes256GcmSivEncryptor")
77 .field("identifier", &Self::IDENTIFIER)
78 .finish_non_exhaustive()
79 }
80}
81
82impl Identified for Aes256GcmSivEncryptor {
83 fn identifier(&self) -> &str {
84 Self::IDENTIFIER
85 }
86}
87
88impl SymmetricCipher for Aes256GcmSivEncryptor {
89 fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>> {
90 let mut nonce = [0u8; NONCE_LEN];
91 OsRng.fill_bytes(&mut nonce);
92 self.encrypt_with_nonce(&nonce, plaintext)
93 }
94
95 fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
96 let body = ciphertext
97 .strip_prefix(PREFIX)
98 .ok_or(Error::InvalidPrefix { label: LABEL })?;
99 if body.len() < NONCE_LEN + TAG_LEN {
100 return Err(Error::TooShort { label: LABEL });
101 }
102
103 let (nonce, rest) = body.split_at(NONCE_LEN);
104 let (tag, ct) = rest.split_at(TAG_LEN);
105
106 let mut buffer = ct.to_vec();
107 self.cipher
108 .decrypt_in_place_detached(
109 Nonce::from_slice(nonce),
110 b"",
111 &mut buffer,
112 Tag::from_slice(tag),
113 )
114 .map_err(|_| Error::DecryptionFailed { label: LABEL })?;
115 Ok(buffer)
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use crate::internal::hex_decode;
123
124 const KEY: &str = "0100000000000000000000000000000000000000000000000000000000000000";
128 const NONCE: &str = "030000000000000000000000";
129
130 const VECTORS: &[(&str, &str, &str)] = &[
132 ("", "", "07f5f4169bbf55a8400cd47ea6fd400f"),
133 (
134 "0100000000000000",
135 "c2ef328e5c71c83b",
136 "843122130f7364b761e0b97427e3df28",
137 ),
138 (
139 "01000000000000000000000000000000",
140 "85a01b63025ba19b7fd3ddfc033b3e76",
141 "c9eac6fa700942702e90862383c6c366",
142 ),
143 (
144 "0100000000000000000000000000000002000000000000000000000000000000",
145 "4a6a9db4c8c6549201b9edb53006cba821ec9cf850948a7c86c68ac7539d027f",
146 "e819e63abcd020b006a976397632eb5d",
147 ),
148 ];
149
150 #[test]
151 fn kat_rfc_8452_appendix_c_2_is_byte_for_byte() {
152 let encryptor = Aes256GcmSivEncryptor::new(hex_decode(KEY).unwrap().try_into().unwrap());
153 let nonce: [u8; NONCE_LEN] = hex_decode(NONCE).unwrap().try_into().unwrap();
154
155 for &(pt_hex, ct_hex, tag_hex) in VECTORS {
156 let plaintext = hex_decode(pt_hex).unwrap();
157 let ct = hex_decode(ct_hex).unwrap();
158 let tag = hex_decode(tag_hex).unwrap();
159 assert_eq!(ct.len(), plaintext.len(), "plaintext {pt_hex}");
161
162 let mut expected = PREFIX.to_vec();
163 expected.extend_from_slice(&nonce);
164 expected.extend_from_slice(&tag);
165 expected.extend_from_slice(&ct);
166 let blob = encryptor.encrypt_with_nonce(&nonce, &plaintext).unwrap();
167 assert_eq!(blob, expected, "plaintext {pt_hex}");
168
169 assert_eq!(encryptor.decrypt(&blob).unwrap(), plaintext);
171 }
172 }
173
174 #[test]
175 fn roundtrip_and_tamper_detection() {
176 let encryptor = Aes256GcmSivEncryptor::new([7u8; 32]);
177 let blob = encryptor.encrypt(b"data").unwrap();
178 assert_eq!(blob.len(), 2 + NONCE_LEN + TAG_LEN + 4);
179 assert_eq!(encryptor.decrypt(&blob).unwrap(), b"data");
180
181 let mut tampered = blob.clone();
182 let last = tampered.len() - 1;
183 tampered[last] ^= 0x01;
184 assert_eq!(
185 encryptor.decrypt(&tampered).unwrap_err(),
186 Error::DecryptionFailed { label: LABEL }
187 );
188
189 let mut nonce_tampered = blob.clone();
191 nonce_tampered[2] ^= 0x01;
192 assert_eq!(
193 encryptor.decrypt(&nonce_tampered).unwrap_err(),
194 Error::DecryptionFailed { label: LABEL }
195 );
196 }
197
198 #[test]
199 fn every_encrypt_uses_a_fresh_nonce() {
200 let encryptor = Aes256GcmSivEncryptor::new([7u8; 32]);
201 let first = encryptor.encrypt(b"same").unwrap();
202 let second = encryptor.encrypt(b"same").unwrap();
203 assert_ne!(first, second);
204 assert_ne!(first[2..2 + NONCE_LEN], second[2..2 + NONCE_LEN]);
205 }
206
207 #[test]
208 fn wrong_key_and_bad_prefix_fail() {
209 let blob = Aes256GcmSivEncryptor::new([7u8; 32]).encrypt(b"x").unwrap();
210 let other = Aes256GcmSivEncryptor::new([8u8; 32]);
211 assert_eq!(
212 other.decrypt(&blob).unwrap_err(),
213 Error::DecryptionFailed { label: LABEL }
214 );
215 assert_eq!(
216 other.decrypt(b"v9....").unwrap_err(),
217 Error::InvalidPrefix { label: LABEL }
218 );
219 assert_eq!(
220 other.decrypt(b"v1short").unwrap_err(),
221 Error::TooShort { label: LABEL }
222 );
223 }
224
225 #[test]
226 fn key_length_is_checked() {
227 assert_eq!(
228 Aes256GcmSivEncryptor::from_slice(&[0u8; 16]).unwrap_err(),
229 Error::InvalidKeyLength {
230 label: LABEL,
231 expected: 32,
232 got: 16
233 }
234 );
235 }
236}