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     gcm, shift, Aad, Block, Direction, Nonce, Tag, BLOCK_LEN,
18 };
19 use crate::{aead, cpu, endian::*, error, polyfill};
20 
21 /// AES-128 in GCM mode with 128-bit tags and 96 bit nonces.
22 pub static AES_128_GCM: aead::Algorithm = aead::Algorithm {
23     key_len: 16,
24     init: init_128,
25     seal: aes_gcm_seal,
26     open: aes_gcm_open,
27     id: aead::AlgorithmID::AES_128_GCM,
28     max_input_len: AES_GCM_MAX_INPUT_LEN,
29 };
30 
31 /// AES-256 in GCM mode with 128-bit tags and 96 bit nonces.
32 pub static AES_256_GCM: aead::Algorithm = aead::Algorithm {
33     key_len: 32,
34     init: init_256,
35     seal: aes_gcm_seal,
36     open: aes_gcm_open,
37     id: aead::AlgorithmID::AES_256_GCM,
38     max_input_len: AES_GCM_MAX_INPUT_LEN,
39 };
40 
41 pub struct Key {
42     gcm_key: gcm::Key, // First because it has a large alignment requirement.
43     aes_key: aes::Key,
44 }
45 
init_128(key: &[u8], cpu_features: cpu::Features) -> Result<aead::KeyInner, error::Unspecified>46 fn init_128(key: &[u8], cpu_features: cpu::Features) -> Result<aead::KeyInner, error::Unspecified> {
47     init(key, aes::Variant::AES_128, cpu_features)
48 }
49 
init_256(key: &[u8], cpu_features: cpu::Features) -> Result<aead::KeyInner, error::Unspecified>50 fn init_256(key: &[u8], cpu_features: cpu::Features) -> Result<aead::KeyInner, error::Unspecified> {
51     init(key, aes::Variant::AES_256, cpu_features)
52 }
53 
init( key: &[u8], variant: aes::Variant, cpu_features: cpu::Features, ) -> Result<aead::KeyInner, error::Unspecified>54 fn init(
55     key: &[u8],
56     variant: aes::Variant,
57     cpu_features: cpu::Features,
58 ) -> Result<aead::KeyInner, error::Unspecified> {
59     let aes_key = aes::Key::new(key, variant, cpu_features)?;
60     let gcm_key = gcm::Key::new(aes_key.encrypt_block(Block::zero()), cpu_features);
61     Ok(aead::KeyInner::AesGcm(Key { aes_key, gcm_key }))
62 }
63 
64 const CHUNK_BLOCKS: usize = 3 * 1024 / 16;
65 
aes_gcm_seal( key: &aead::KeyInner, nonce: Nonce, aad: Aad<&[u8]>, in_out: &mut [u8], cpu_features: cpu::Features, ) -> Tag66 fn aes_gcm_seal(
67     key: &aead::KeyInner,
68     nonce: Nonce,
69     aad: Aad<&[u8]>,
70     in_out: &mut [u8],
71     cpu_features: cpu::Features,
72 ) -> Tag {
73     aead(key, nonce, aad, in_out, Direction::Sealing, cpu_features)
74 }
75 
aes_gcm_open( key: &aead::KeyInner, nonce: Nonce, aad: Aad<&[u8]>, in_prefix_len: usize, in_out: &mut [u8], cpu_features: cpu::Features, ) -> Tag76 fn aes_gcm_open(
77     key: &aead::KeyInner,
78     nonce: Nonce,
79     aad: Aad<&[u8]>,
80     in_prefix_len: usize,
81     in_out: &mut [u8],
82     cpu_features: cpu::Features,
83 ) -> Tag {
84     aead(
85         key,
86         nonce,
87         aad,
88         in_out,
89         Direction::Opening { in_prefix_len },
90         cpu_features,
91     )
92 }
93 
94 #[inline(always)] // Avoid branching on `direction`.
aead( key: &aead::KeyInner, nonce: Nonce, aad: Aad<&[u8]>, in_out: &mut [u8], direction: Direction, cpu_features: cpu::Features, ) -> Tag95 fn aead(
96     key: &aead::KeyInner,
97     nonce: Nonce,
98     aad: Aad<&[u8]>,
99     in_out: &mut [u8],
100     direction: Direction,
101     cpu_features: cpu::Features,
102 ) -> Tag {
103     let Key { aes_key, gcm_key } = match key {
104         aead::KeyInner::AesGcm(key) => key,
105         _ => unreachable!(),
106     };
107 
108     let mut ctr = Counter::one(nonce);
109     let tag_iv = ctr.increment();
110 
111     let aad_len = aad.0.len();
112     let mut gcm_ctx = gcm::Context::new(gcm_key, aad, cpu_features);
113 
114     let in_prefix_len = match direction {
115         Direction::Opening { in_prefix_len } => in_prefix_len,
116         Direction::Sealing => 0,
117     };
118 
119     let total_in_out_len = in_out.len() - in_prefix_len;
120 
121     let in_out = integrated_aes_gcm(
122         aes_key,
123         &mut gcm_ctx,
124         in_out,
125         &mut ctr,
126         direction,
127         cpu_features,
128     );
129     let in_out_len = in_out.len() - in_prefix_len;
130 
131     // Process any (remaining) whole blocks.
132     let whole_len = in_out_len - (in_out_len % BLOCK_LEN);
133     {
134         let mut chunk_len = CHUNK_BLOCKS * BLOCK_LEN;
135         let mut output = 0;
136         let mut input = in_prefix_len;
137         loop {
138             if whole_len - output < chunk_len {
139                 chunk_len = whole_len - output;
140             }
141             if chunk_len == 0 {
142                 break;
143             }
144 
145             if let Direction::Opening { .. } = direction {
146                 gcm_ctx.update_blocks(&in_out[input..][..chunk_len]);
147             }
148 
149             aes_key.ctr32_encrypt_blocks(
150                 &mut in_out[output..][..(chunk_len + in_prefix_len)],
151                 direction,
152                 &mut ctr,
153             );
154 
155             if let Direction::Sealing = direction {
156                 gcm_ctx.update_blocks(&in_out[output..][..chunk_len]);
157             }
158 
159             output += chunk_len;
160             input += chunk_len;
161         }
162     }
163 
164     // Process any remaining partial block.
165     let remainder = &mut in_out[whole_len..];
166     shift::shift_partial((in_prefix_len, remainder), |remainder| {
167         let mut input = Block::zero();
168         input.overwrite_part_at(0, remainder);
169         if let Direction::Opening { .. } = direction {
170             gcm_ctx.update_block(input);
171         }
172         let mut output = aes_key.encrypt_iv_xor_block(ctr.into(), input);
173         if let Direction::Sealing = direction {
174             output.zero_from(remainder.len());
175             gcm_ctx.update_block(output);
176         }
177         output
178     });
179 
180     // Authenticate the final block containing the input lengths.
181     let aad_bits = polyfill::u64_from_usize(aad_len) << 3;
182     let ciphertext_bits = polyfill::u64_from_usize(total_in_out_len) << 3;
183     gcm_ctx.update_block(Block::from_u64_be(
184         BigEndian::from(aad_bits),
185         BigEndian::from(ciphertext_bits),
186     ));
187 
188     // Finalize the tag and return it.
189     gcm_ctx.pre_finish(|pre_tag| {
190         let block = tag_iv.into_block_less_safe();
191         let mut tag = aes_key.encrypt_block(block);
192         tag.bitxor_assign(pre_tag.into());
193         Tag(tag)
194     })
195 }
196 
197 // Returns the data that wasn't processed.
198 #[cfg(target_arch = "x86_64")]
199 #[inline] // Optimize out the match on `direction`.
integrated_aes_gcm<'a>( aes_key: &aes::Key, gcm_ctx: &mut gcm::Context, in_out: &'a mut [u8], ctr: &mut Counter, direction: Direction, cpu_features: cpu::Features, ) -> &'a mut [u8]200 fn integrated_aes_gcm<'a>(
201     aes_key: &aes::Key,
202     gcm_ctx: &mut gcm::Context,
203     in_out: &'a mut [u8],
204     ctr: &mut Counter,
205     direction: Direction,
206     cpu_features: cpu::Features,
207 ) -> &'a mut [u8] {
208     use crate::c;
209 
210     if !aes_key.is_aes_hw() || !gcm_ctx.is_avx2(cpu_features) {
211         return in_out;
212     }
213 
214     let processed = match direction {
215         Direction::Opening { in_prefix_len } => {
216             extern "C" {
217                 fn GFp_aesni_gcm_decrypt(
218                     input: *const u8,
219                     output: *mut u8,
220                     len: c::size_t,
221                     key: &aes::AES_KEY,
222                     ivec: &mut Counter,
223                     gcm: &mut gcm::ContextInner,
224                 ) -> c::size_t;
225             }
226             unsafe {
227                 GFp_aesni_gcm_decrypt(
228                     in_out[in_prefix_len..].as_ptr(),
229                     in_out.as_mut_ptr(),
230                     in_out.len() - in_prefix_len,
231                     aes_key.inner_less_safe(),
232                     ctr,
233                     gcm_ctx.inner(),
234                 )
235             }
236         }
237         Direction::Sealing => {
238             extern "C" {
239                 fn GFp_aesni_gcm_encrypt(
240                     input: *const u8,
241                     output: *mut u8,
242                     len: c::size_t,
243                     key: &aes::AES_KEY,
244                     ivec: &mut Counter,
245                     gcm: &mut gcm::ContextInner,
246                 ) -> c::size_t;
247             }
248             unsafe {
249                 GFp_aesni_gcm_encrypt(
250                     in_out.as_ptr(),
251                     in_out.as_mut_ptr(),
252                     in_out.len(),
253                     aes_key.inner_less_safe(),
254                     ctr,
255                     gcm_ctx.inner(),
256                 )
257             }
258         }
259     };
260 
261     &mut in_out[processed..]
262 }
263 
264 #[cfg(not(target_arch = "x86_64"))]
265 #[inline]
integrated_aes_gcm<'a>( _: &aes::Key, _: &mut gcm::Context, in_out: &'a mut [u8], _: &mut Counter, _: Direction, _: cpu::Features, ) -> &'a mut [u8]266 fn integrated_aes_gcm<'a>(
267     _: &aes::Key,
268     _: &mut gcm::Context,
269     in_out: &'a mut [u8],
270     _: &mut Counter,
271     _: Direction,
272     _: cpu::Features,
273 ) -> &'a mut [u8] {
274     in_out // This doesn't process any of the input so it all remains.
275 }
276 
277 const AES_GCM_MAX_INPUT_LEN: u64 = super::max_input_len(BLOCK_LEN, 2);
278 
279 #[cfg(test)]
280 mod tests {
281     #[test]
max_input_len_test()282     fn max_input_len_test() {
283         // [NIST SP800-38D] Section 5.2.1.1. Note that [RFC 5116 Section 5.1] and
284         // [RFC 5116 Section 5.2] have an off-by-one error in `P_MAX`.
285         //
286         // [NIST SP800-38D]:
287         //    http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
288         // [RFC 5116 Section 5.1]: https://tools.ietf.org/html/rfc5116#section-5.1
289         // [RFC 5116 Section 5.2]: https://tools.ietf.org/html/rfc5116#section-5.2
290         const NIST_SP800_38D_MAX_BITS: u64 = (1u64 << 39) - 256;
291         assert_eq!(NIST_SP800_38D_MAX_BITS, 549_755_813_632u64);
292         assert_eq!(
293             super::AES_128_GCM.max_input_len * 8,
294             NIST_SP800_38D_MAX_BITS
295         );
296         assert_eq!(
297             super::AES_256_GCM.max_input_len * 8,
298             NIST_SP800_38D_MAX_BITS
299         );
300     }
301 }
302