dcrypt_algorithms/aead/gcm/
mod.rs1#![cfg_attr(not(feature = "std"), no_std)]
22
23#[cfg(not(feature = "std"))]
25#[cfg(feature = "alloc")]
26use alloc::vec::Vec;
27
28#[cfg(feature = "std")]
29use std::vec::Vec;
30
31use byteorder::{BigEndian, ByteOrder};
32#[cfg(not(feature = "std"))]
33use portable_atomic::{compiler_fence, Ordering};
34#[cfg(feature = "std")]
35use std::sync::atomic::{compiler_fence, Ordering};
36use subtle::ConstantTimeEq;
37use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
38
39use dcrypt_common::security::{SecretBuffer, SecureZeroingType};
41
42use crate::block::BlockCipher;
44use dcrypt_api::traits::symmetric::{DecryptOperation, EncryptOperation, Operation};
45use dcrypt_api::traits::AuthenticatedCipher;
46use dcrypt_api::traits::SymmetricCipher;
47
48use crate::error::{validate, Error, Result};
49use crate::types::nonce::AesGcmCompatible; use crate::types::Nonce; use crate::types::SecretBytes;
52use dcrypt_api::error::Error as CoreError;
53use dcrypt_api::types::Ciphertext;
54
55mod ghash;
57use ghash::{process_ghash, GHash};
58
59const GCM_BLOCK_SIZE: usize = 16;
61const GCM_TAG_SIZE: usize = 16;
62
63#[derive(Clone, Zeroize, ZeroizeOnDrop)]
65pub struct Gcm<B: BlockCipher + Zeroize + ZeroizeOnDrop> {
66 cipher: B,
67 h: SecretBuffer<GCM_BLOCK_SIZE>, nonce: Zeroizing<Vec<u8>>,
69 tag_len: usize, }
71
72pub struct GcmEncryptOperation<'a, B: BlockCipher + Zeroize + ZeroizeOnDrop> {
74 cipher: &'a Gcm<B>,
75 nonce: Option<&'a Nonce<12>>, aad: Option<&'a [u8]>,
77}
78
79pub struct GcmDecryptOperation<'a, B: BlockCipher + Zeroize + ZeroizeOnDrop> {
81 cipher: &'a Gcm<B>,
82 nonce: Option<&'a Nonce<12>>, aad: Option<&'a [u8]>,
84}
85
86impl<B: BlockCipher + Zeroize + ZeroizeOnDrop> Gcm<B> {
87 pub fn new<const N: usize>(cipher: B, nonce: &Nonce<N>) -> Result<Self>
89 where
90 Nonce<N>: AesGcmCompatible,
91 {
92 Self::new_with_tag_len(cipher, nonce, GCM_TAG_SIZE)
93 }
94
95 pub fn new_with_tag_len<const N: usize>(
99 cipher: B,
100 nonce: &Nonce<N>,
101 tag_len: usize,
102 ) -> Result<Self>
103 where
104 Nonce<N>: AesGcmCompatible,
105 {
106 validate::parameter(
108 B::block_size() == GCM_BLOCK_SIZE,
109 "block_size",
110 "GCM only works with 128-bit block ciphers",
111 )?;
112
113 validate::parameter(
114 !nonce.is_empty() && nonce.len() <= 16,
115 "nonce_length",
116 "GCM nonce must be between 1 and 16 bytes",
117 )?;
118
119 validate::parameter(
120 (1..=GCM_TAG_SIZE).contains(&tag_len),
121 "tag_length",
122 "GCM tag length must be between 1 and 16 bytes",
123 )?;
124
125 let mut h_bytes = [0u8; GCM_BLOCK_SIZE];
127 cipher.encrypt_block(&mut h_bytes)?;
128
129 let h = SecretBuffer::new(h_bytes);
131
132 Ok(Self {
133 cipher,
134 h,
135 nonce: Zeroizing::new(nonce.as_ref().to_vec()),
136 tag_len,
137 })
138 }
139
140 fn generate_j0(&self) -> Result<[u8; GCM_BLOCK_SIZE]> {
142 let mut j0 = [0u8; GCM_BLOCK_SIZE];
143 if self.nonce.len() == 12 {
144 j0[..12].copy_from_slice(&self.nonce);
145 j0[15] = 1;
146 } else {
147 let h_array: &[u8; GCM_BLOCK_SIZE] = self
149 .h
150 .as_ref()
151 .try_into()
152 .expect("SecretBuffer has correct size");
153
154 let mut g = GHash::new(h_array);
155 g.update(&self.nonce)?;
156 let rem = self.nonce.len() % GCM_BLOCK_SIZE;
157 if rem != 0 {
158 g.update(&vec![0u8; GCM_BLOCK_SIZE - rem])?;
159 }
160 g.update_lengths(0, self.nonce.len() as u64)?;
161 j0 = g.finalize();
162 }
163 Ok(j0)
164 }
165
166 fn generate_keystream(
168 &self,
169 j0: &[u8; GCM_BLOCK_SIZE],
170 data_len: usize,
171 ) -> Result<Zeroizing<Vec<u8>>> {
172 let num_blocks = data_len.div_ceil(GCM_BLOCK_SIZE);
173 let mut keystream = Zeroizing::new(Vec::with_capacity(num_blocks * GCM_BLOCK_SIZE));
174
175 let mut counter = *j0;
176 let mut ctr_val = BigEndian::read_u32(&counter[12..16]).wrapping_add(1);
177 BigEndian::write_u32(&mut counter[12..16], ctr_val);
178
179 for _ in 0..num_blocks {
180 let mut block = counter;
181 self.cipher.encrypt_block(&mut block)?;
182 keystream.extend_from_slice(&block);
183 ctr_val = ctr_val.wrapping_add(1);
184 BigEndian::write_u32(&mut counter[12..16], ctr_val);
185 }
186
187 Ok(keystream)
188 }
189
190 fn generate_tag(
192 &self,
193 j0: &[u8; GCM_BLOCK_SIZE],
194 aad: &[u8],
195 ciphertext: &[u8],
196 ) -> Result<[u8; GCM_TAG_SIZE]> {
197 let h_array: &[u8; GCM_BLOCK_SIZE] = self
199 .h
200 .as_ref()
201 .try_into()
202 .expect("SecretBuffer has correct size");
203
204 let mut tag = process_ghash(h_array, aad, ciphertext)?;
206
207 let mut j0_copy = *j0;
209 self.cipher.encrypt_block(&mut j0_copy)?;
210
211 for i in 0..GCM_TAG_SIZE {
213 tag[i] ^= j0_copy[i];
214 }
215
216 Ok(tag)
217 }
218
219 pub fn internal_encrypt(
221 &self,
222 plaintext: &[u8],
223 associated_data: Option<&[u8]>,
224 ) -> Result<Vec<u8>> {
225 let aad = associated_data.unwrap_or(&[]);
226 let j0 = self.generate_j0()?;
227
228 let mut ciphertext = Vec::with_capacity(plaintext.len() + self.tag_len);
229 if !plaintext.is_empty() {
230 let keystream = self.generate_keystream(&j0, plaintext.len())?;
231 for i in 0..plaintext.len() {
232 ciphertext.push(plaintext[i] ^ keystream[i]);
233 }
234 }
235
236 let full_tag = self.generate_tag(&j0, aad, &ciphertext)?;
237 ciphertext.extend_from_slice(&full_tag[..self.tag_len]);
238 Ok(ciphertext)
239 }
240
241 pub fn internal_decrypt(
243 &self,
244 ciphertext: &[u8],
245 associated_data: Option<&[u8]>,
246 ) -> Result<Vec<u8>> {
247 validate::min_length("GCM ciphertext", ciphertext.len(), self.tag_len)?;
249
250 let aad = associated_data.unwrap_or(&[]);
251 let ciphertext_len = ciphertext.len() - self.tag_len;
252 let (ciphertext_data, received_tag) = ciphertext.split_at(ciphertext_len);
253
254 let j0 = self.generate_j0()?;
256 let full_expected = self.generate_tag(&j0, aad, ciphertext_data)?;
257 let expected_tag = &full_expected[..self.tag_len];
258
259 let keystream = self.generate_keystream(&j0, ciphertext_len)?;
261 let mut plaintext = Zeroizing::new(Vec::with_capacity(ciphertext_len));
262 for i in 0..ciphertext_len {
263 plaintext.push(ciphertext_data[i] ^ keystream[i]);
264 }
265
266 compiler_fence(Ordering::SeqCst);
268
269 let tag_matches = expected_tag.ct_eq(received_tag);
271
272 compiler_fence(Ordering::SeqCst);
274
275 if tag_matches.unwrap_u8() == 0 {
277 Err(Error::Authentication { algorithm: "GCM" })
281 } else {
282 Ok(plaintext.to_vec())
283 }
284 }
285}
286
287impl<B: BlockCipher + Clone + Zeroize + ZeroizeOnDrop> SecureZeroingType for Gcm<B> {
289 fn zeroed() -> Self {
290 panic!("Cannot create a zeroed GCM instance without a cipher")
293 }
294
295 fn secure_clone(&self) -> Self {
296 self.clone()
297 }
298}
299
300impl<B: BlockCipher + Zeroize + ZeroizeOnDrop> AuthenticatedCipher for Gcm<B> {
302 const TAG_SIZE: usize = GCM_TAG_SIZE;
303 const ALGORITHM_ID: &'static str = "GCM";
304}
305
306impl<B: BlockCipher + Zeroize + ZeroizeOnDrop> SymmetricCipher for Gcm<B> {
308 type Key = SecretBytes<32>; type Nonce = Nonce<12>; type Ciphertext = Ciphertext;
312 type EncryptOperation<'a>
313 = GcmEncryptOperation<'a, B>
314 where
315 Self: 'a;
316 type DecryptOperation<'a>
317 = GcmDecryptOperation<'a, B>
318 where
319 Self: 'a;
320
321 fn name() -> &'static str {
322 "GCM"
323 }
324
325 fn encrypt(&self) -> <Self as SymmetricCipher>::EncryptOperation<'_> {
326 GcmEncryptOperation {
327 cipher: self,
328 nonce: None,
329 aad: None,
330 }
331 }
332
333 fn decrypt(&self) -> <Self as SymmetricCipher>::DecryptOperation<'_> {
334 GcmDecryptOperation {
335 cipher: self,
336 nonce: None,
337 aad: None,
338 }
339 }
340
341 fn generate_key<R: rand::RngCore + rand::CryptoRng>(
342 rng: &mut R,
343 ) -> std::result::Result<<Self as SymmetricCipher>::Key, CoreError> {
344 let mut key_data = [0u8; 32]; rng.fill_bytes(&mut key_data);
346 Ok(SecretBytes::new(key_data))
347 }
348
349 fn generate_nonce<R: rand::RngCore + rand::CryptoRng>(
350 rng: &mut R,
351 ) -> std::result::Result<<Self as SymmetricCipher>::Nonce, CoreError> {
352 let mut nonce_data = [0u8; 12];
353 rng.fill_bytes(&mut nonce_data);
354 Ok(Nonce::<12>::new(nonce_data)) }
356
357 fn derive_key_from_bytes(
358 bytes: &[u8],
359 ) -> std::result::Result<<Self as SymmetricCipher>::Key, CoreError> {
360 if bytes.len() < 32 {
361 return Err(CoreError::InvalidLength {
363 context: "GCM key derivation",
364 expected: 32,
365 actual: bytes.len(),
366 });
367 }
368
369 let mut key_data = [0u8; 32]; key_data.copy_from_slice(&bytes[..32]);
371 Ok(SecretBytes::new(key_data))
372 }
373}
374
375impl<B: BlockCipher + Zeroize + ZeroizeOnDrop> Operation<Ciphertext>
377 for GcmEncryptOperation<'_, B>
378{
379 fn execute(self) -> std::result::Result<Ciphertext, CoreError> {
380 if self.nonce.is_none() {
381 return Err(CoreError::InvalidParameter {
382 context: "GCM encryption",
383 #[cfg(feature = "std")]
384 message: "Nonce is required for GCM encryption".to_string(),
385 });
386 }
387 let plaintext = b""; let ciphertext = self
390 .cipher
391 .internal_encrypt(plaintext, self.aad)
392 .map_err(CoreError::from)?;
393
394 Ok(Ciphertext::new(&ciphertext))
395 }
396}
397
398impl<'a, B: BlockCipher + Zeroize + ZeroizeOnDrop> EncryptOperation<'a, Gcm<B>>
400 for GcmEncryptOperation<'a, B>
401{
402 fn with_nonce(mut self, nonce: &'a <Gcm<B> as SymmetricCipher>::Nonce) -> Self {
403 self.nonce = Some(nonce);
404 self
405 }
406
407 fn with_aad(mut self, aad: &'a [u8]) -> Self {
408 self.aad = Some(aad);
409 self
410 }
411
412 fn encrypt(self, plaintext: &'a [u8]) -> std::result::Result<Ciphertext, CoreError> {
413 if self.nonce.is_none() {
414 return Err(CoreError::InvalidParameter {
415 context: "GCM encryption",
416 #[cfg(feature = "std")]
417 message: "Nonce is required for GCM encryption".to_string(),
418 });
419 }
420
421 let ciphertext = self
422 .cipher
423 .internal_encrypt(plaintext, self.aad)
424 .map_err(CoreError::from)?;
425
426 Ok(Ciphertext::new(&ciphertext))
427 }
428}
429
430impl<B: BlockCipher + Zeroize + ZeroizeOnDrop> Operation<Vec<u8>> for GcmDecryptOperation<'_, B> {
432 fn execute(self) -> std::result::Result<Vec<u8>, CoreError> {
433 Err(CoreError::InvalidParameter {
434 context: "GCM decryption",
435 #[cfg(feature = "std")]
436 message: "Use decrypt method instead".to_string(),
437 })
438 }
439}
440
441impl<'a, B: BlockCipher + Zeroize + ZeroizeOnDrop> DecryptOperation<'a, Gcm<B>>
443 for GcmDecryptOperation<'a, B>
444{
445 fn with_nonce(mut self, nonce: &'a <Gcm<B> as SymmetricCipher>::Nonce) -> Self {
446 self.nonce = Some(nonce);
447 self
448 }
449
450 fn with_aad(mut self, aad: &'a [u8]) -> Self {
451 self.aad = Some(aad);
452 self
453 }
454
455 fn decrypt(
456 self,
457 ciphertext: &'a <Gcm<B> as SymmetricCipher>::Ciphertext,
458 ) -> std::result::Result<Vec<u8>, CoreError> {
459 if self.nonce.is_none() {
460 return Err(CoreError::InvalidParameter {
461 context: "GCM decryption",
462 #[cfg(feature = "std")]
463 message: "Nonce is required for GCM decryption".to_string(),
464 });
465 }
466
467 self.cipher
468 .internal_decrypt(ciphertext.as_ref(), self.aad)
469 .map_err(CoreError::from)
470 }
471}
472
473#[cfg(test)]
474mod tests;