1 // Copyright 2015-2016 Brian Smith.
2 //
3 // Permission to use, copy, modify, and/or distribute this software for any
4 // purpose with or without fee is hereby granted, provided that the above
5 // copyright notice and this permission notice appear in all copies.
6 //
7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
10 // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 
15 use super::{
16     aes::{self, Counter},
17     block::{Block, BLOCK_LEN},
18     gcm, shift, Aad, Nonce, Tag,
19 };
20 use crate::{aead, cpu, error, polyfill};
21 use core::ops::RangeFrom;
22 
23 /// AES-128 in GCM mode with 128-bit tags and 96 bit nonces.
24 pub static AES_128_GCM: aead::Algorithm = aead::Algorithm {
25     key_len: 16,
26     init: init_128,
27     seal: aes_gcm_seal,
28     open: aes_gcm_open,
29     id: aead::AlgorithmID::AES_128_GCM,
30     max_input_len: AES_GCM_MAX_INPUT_LEN,
31 };
32 
33 /// AES-256 in GCM mode with 128-bit tags and 96 bit nonces.
34 pub static AES_256_GCM: aead::Algorithm = aead::Algorithm {
35     key_len: 32,
36     init: init_256,
37     seal: aes_gcm_seal,
38     open: aes_gcm_open,
39     id: aead::AlgorithmID::AES_256_GCM,
40     max_input_len: AES_GCM_MAX_INPUT_LEN,
41 };
42 
43 #[derive(Clone)]
44 pub struct Key {
45     gcm_key: gcm::Key, // First because it has a large alignment requirement.
46     aes_key: aes::Key,
47 }
48 
init_128(key: &[u8], cpu_features: cpu::Features) -> Result<aead::KeyInner, error::Unspecified>49 fn init_128(key: &[u8], cpu_features: cpu::Features) -> Result<aead::KeyInner, error::Unspecified> {
50     init(key, aes::Variant::AES_128, cpu_features)
51 }
52 
init_256(key: &[u8], cpu_features: cpu::Features) -> Result<aead::KeyInner, error::Unspecified>53 fn init_256(key: &[u8], cpu_features: cpu::Features) -> Result<aead::KeyInner, error::Unspecified> {
54     init(key, aes::Variant::AES_256, cpu_features)
55 }
56 
init( key: &[u8], variant: aes::Variant, cpu_features: cpu::Features, ) -> Result<aead::KeyInner, error::Unspecified>57 fn init(
58     key: &[u8],
59     variant: aes::Variant,
60     cpu_features: cpu::Features,
61 ) -> Result<aead::KeyInner, error::Unspecified> {
62     let aes_key = aes::Key::new(key, variant, cpu_features)?;
63     let gcm_key = gcm::Key::new(aes_key.encrypt_block(Block::zero()), cpu_features);
64     Ok(aead::KeyInner::AesGcm(Key { gcm_key, aes_key }))
65 }
66 
67 const CHUNK_BLOCKS: usize = 3 * 1024 / 16;
68 
aes_gcm_seal(key: &aead::KeyInner, nonce: Nonce, aad: Aad<&[u8]>, in_out: &mut [u8]) -> Tag69 fn aes_gcm_seal(key: &aead::KeyInner, nonce: Nonce, aad: Aad<&[u8]>, in_out: &mut [u8]) -> Tag {
70     let Key { gcm_key, aes_key } = match key {
71         aead::KeyInner::AesGcm(key) => key,
72         _ => unreachable!(),
73     };
74 
75     let mut ctr = Counter::one(nonce);
76     let tag_iv = ctr.increment();
77 
78     let total_in_out_len = in_out.len();
79     let aad_len = aad.0.len();
80     let mut auth = gcm::Context::new(gcm_key, aad);
81 
82     #[cfg(target_arch = "x86_64")]
83     let in_out = {
84         if !aes_key.is_aes_hw() || !auth.is_avx2() {
85             in_out
86         } else {
87             use crate::c;
88             let (htable, xi) = auth.inner();
89             prefixed_extern! {
90                 // `HTable` and `Xi` should be 128-bit aligned. TODO: Can we shrink `HTable`? The
91                 // assembly says it needs just nine values in that array.
92                 fn aesni_gcm_encrypt(
93                     input: *const u8,
94                     output: *mut u8,
95                     len: c::size_t,
96                     key: &aes::AES_KEY,
97                     ivec: &mut Counter,
98                     Htable: &gcm::HTable,
99                     Xi: &mut gcm::Xi) -> c::size_t;
100             }
101             let processed = unsafe {
102                 aesni_gcm_encrypt(
103                     in_out.as_ptr(),
104                     in_out.as_mut_ptr(),
105                     in_out.len(),
106                     aes_key.inner_less_safe(),
107                     &mut ctr,
108                     htable,
109                     xi,
110                 )
111             };
112 
113             &mut in_out[processed..]
114         }
115     };
116 
117     let (whole, remainder) = {
118         let in_out_len = in_out.len();
119         let whole_len = in_out_len - (in_out_len % BLOCK_LEN);
120         in_out.split_at_mut(whole_len)
121     };
122 
123     for chunk in whole.chunks_mut(CHUNK_BLOCKS * BLOCK_LEN) {
124         aes_key.ctr32_encrypt_within(chunk, 0.., &mut ctr);
125         auth.update_blocks(chunk);
126     }
127 
128     if !remainder.is_empty() {
129         let mut input = Block::zero();
130         input.overwrite_part_at(0, remainder);
131         let mut output = aes_key.encrypt_iv_xor_block(ctr.into(), input);
132         output.zero_from(remainder.len());
133         auth.update_block(output);
134         remainder.copy_from_slice(&output.as_ref()[..remainder.len()]);
135     }
136 
137     finish(aes_key, auth, tag_iv, aad_len, total_in_out_len)
138 }
139 
aes_gcm_open( key: &aead::KeyInner, nonce: Nonce, aad: Aad<&[u8]>, in_out: &mut [u8], src: RangeFrom<usize>, ) -> Tag140 fn aes_gcm_open(
141     key: &aead::KeyInner,
142     nonce: Nonce,
143     aad: Aad<&[u8]>,
144     in_out: &mut [u8],
145     src: RangeFrom<usize>,
146 ) -> Tag {
147     let Key { gcm_key, aes_key } = match key {
148         aead::KeyInner::AesGcm(key) => key,
149         _ => unreachable!(),
150     };
151 
152     let mut ctr = Counter::one(nonce);
153     let tag_iv = ctr.increment();
154 
155     let aad_len = aad.0.len();
156     let mut auth = gcm::Context::new(gcm_key, aad);
157 
158     let in_prefix_len = src.start;
159 
160     let total_in_out_len = in_out.len() - in_prefix_len;
161 
162     #[cfg(target_arch = "x86_64")]
163     let in_out = {
164         if !aes_key.is_aes_hw() || !auth.is_avx2() {
165             in_out
166         } else {
167             use crate::c;
168             let (htable, xi) = auth.inner();
169             prefixed_extern! {
170                 // `HTable` and `Xi` should be 128-bit aligned. TODO: Can we shrink `HTable`? The
171                 // assembly says it needs just nine values in that array.
172                 fn aesni_gcm_decrypt(
173                     input: *const u8,
174                     output: *mut u8,
175                     len: c::size_t,
176                     key: &aes::AES_KEY,
177                     ivec: &mut Counter,
178                     Htable: &gcm::HTable,
179                     Xi: &mut gcm::Xi) -> c::size_t;
180             }
181 
182             let processed = unsafe {
183                 aesni_gcm_decrypt(
184                     in_out[src.clone()].as_ptr(),
185                     in_out.as_mut_ptr(),
186                     in_out.len() - src.start,
187                     aes_key.inner_less_safe(),
188                     &mut ctr,
189                     htable,
190                     xi,
191                 )
192             };
193             &mut in_out[processed..]
194         }
195     };
196 
197     let whole_len = {
198         let in_out_len = in_out.len() - in_prefix_len;
199         in_out_len - (in_out_len % BLOCK_LEN)
200     };
201     {
202         let mut chunk_len = CHUNK_BLOCKS * BLOCK_LEN;
203         let mut output = 0;
204         let mut input = in_prefix_len;
205         loop {
206             if whole_len - output < chunk_len {
207                 chunk_len = whole_len - output;
208             }
209             if chunk_len == 0 {
210                 break;
211             }
212 
213             auth.update_blocks(&in_out[input..][..chunk_len]);
214             aes_key.ctr32_encrypt_within(
215                 &mut in_out[output..][..(chunk_len + in_prefix_len)],
216                 in_prefix_len..,
217                 &mut ctr,
218             );
219             output += chunk_len;
220             input += chunk_len;
221         }
222     }
223 
224     let remainder = &mut in_out[whole_len..];
225     shift::shift_partial((in_prefix_len, remainder), |remainder| {
226         let mut input = Block::zero();
227         input.overwrite_part_at(0, remainder);
228         auth.update_block(input);
229         aes_key.encrypt_iv_xor_block(ctr.into(), input)
230     });
231 
232     finish(aes_key, auth, tag_iv, aad_len, total_in_out_len)
233 }
234 
finish( aes_key: &aes::Key, mut gcm_ctx: gcm::Context, tag_iv: aes::Iv, aad_len: usize, in_out_len: usize, ) -> Tag235 fn finish(
236     aes_key: &aes::Key,
237     mut gcm_ctx: gcm::Context,
238     tag_iv: aes::Iv,
239     aad_len: usize,
240     in_out_len: usize,
241 ) -> Tag {
242     // Authenticate the final block containing the input lengths.
243     let aad_bits = polyfill::u64_from_usize(aad_len) << 3;
244     let ciphertext_bits = polyfill::u64_from_usize(in_out_len) << 3;
245     gcm_ctx.update_block(Block::from([aad_bits, ciphertext_bits]));
246 
247     // Finalize the tag and return it.
248     gcm_ctx.pre_finish(|pre_tag| {
249         let encrypted_iv = aes_key.encrypt_block(Block::from(tag_iv.as_bytes_less_safe()));
250         let tag = pre_tag ^ encrypted_iv;
251         Tag(*tag.as_ref())
252     })
253 }
254 
255 const AES_GCM_MAX_INPUT_LEN: u64 = super::max_input_len(BLOCK_LEN, 2);
256 
257 #[cfg(test)]
258 mod tests {
259     #[test]
max_input_len_test()260     fn max_input_len_test() {
261         // [NIST SP800-38D] Section 5.2.1.1. Note that [RFC 5116 Section 5.1] and
262         // [RFC 5116 Section 5.2] have an off-by-one error in `P_MAX`.
263         //
264         // [NIST SP800-38D]:
265         //    http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
266         // [RFC 5116 Section 5.1]: https://tools.ietf.org/html/rfc5116#section-5.1
267         // [RFC 5116 Section 5.2]: https://tools.ietf.org/html/rfc5116#section-5.2
268         const NIST_SP800_38D_MAX_BITS: u64 = (1u64 << 39) - 256;
269         assert_eq!(NIST_SP800_38D_MAX_BITS, 549_755_813_632u64);
270         assert_eq!(
271             super::AES_128_GCM.max_input_len * 8,
272             NIST_SP800_38D_MAX_BITS
273         );
274         assert_eq!(
275             super::AES_256_GCM.max_input_len * 8,
276             NIST_SP800_38D_MAX_BITS
277         );
278     }
279 }
280