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