crypto/
aes_gcm.rs

1// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4// option. This file may not be copied, modified, or distributed
5// except according to those terms.
6
7use aes::{ctr, KeySize};
8use aead::{AeadEncryptor,AeadDecryptor};
9use cryptoutil::copy_memory;
10use symmetriccipher::SynchronousStreamCipher;
11use ghash::{Ghash};
12use util::fixed_time_eq;
13
14pub struct AesGcm<'a> {
15    cipher: Box<SynchronousStreamCipher + 'a>,
16    mac: Ghash,
17    finished: bool,
18    end_tag: [u8; 16]
19}
20
21impl<'a> AesGcm<'a> {
22    pub fn new (key_size: KeySize, key: &[u8], nonce: &[u8], aad: &[u8]) -> AesGcm<'a> {
23        assert!(key.len() == 16 || key.len() == 24 || key.len() == 32);
24        assert!(nonce.len() == 12);
25
26        // GCM technically differs from CTR mode in how role overs are handled
27        // GCM only touches the right most 4 bytes while CTR roles all 16 over
28        // when the iv is only 96 bits (12 bytes) then 4 bytes of zeros are
29        // appended to it meaning you have to encrypt 2^37 bytes (256 gigabytes)
30        // of data before a difference crops up.
31        // The GCM handles nonces of other lengths by hashing them once with ghash
32        // this would cause the roleover behavior to potentially be triggered much
33        // earlier preventing the use of generic CTR mode.
34
35        let mut iv = [0u8; 16];
36        copy_memory(nonce, &mut iv);
37        iv[15] = 1u8;
38        let mut cipher = ctr(key_size,key,&iv);
39        let temp_block = [0u8; 16];
40        let mut final_block = [0u8; 16];
41        cipher.process(&temp_block, &mut final_block);
42        let mut hash_key =  [0u8; 16];
43        let mut encryptor = ctr(key_size,key,&temp_block);
44        encryptor.process(&temp_block, &mut hash_key);
45        AesGcm {
46            cipher: cipher,
47            mac:  Ghash::new(&hash_key).input_a(aad),
48            finished: false,
49            end_tag: final_block
50        }
51    }
52}
53
54impl<'a> AeadEncryptor for AesGcm<'static> {
55    fn encrypt(&mut self, input: &[u8], output: &mut [u8], tag: &mut [u8]) {
56        assert!(input.len() == output.len());
57        assert!(!self.finished);
58        self.cipher.process(input, output);
59        let result = self.mac.input_c(output).result();
60        self.finished = true;
61        for i in 0..16 {
62            tag[i] = result[i] ^ self.end_tag[i];
63        }
64    }
65}
66
67impl<'a> AeadDecryptor for AesGcm<'static> {
68    fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8])  -> bool {
69        assert!(input.len() == output.len());
70        assert!(!self.finished);
71        self.finished = true;
72        let mut calc_tag = self.mac.input_c(input).result();
73        for i in 0..16 {
74            calc_tag[i] ^= self.end_tag[i];
75        }
76        if fixed_time_eq(&calc_tag, tag) {
77            self.cipher.process(input, output);
78            true
79        } else {
80            false
81        }
82    }
83}
84
85#[cfg(test)]
86mod test {
87    use aes::KeySize;
88    use aes_gcm::AesGcm;
89    use aead::{AeadEncryptor, AeadDecryptor};
90    use hex;
91    use std::iter::repeat;
92    fn hex_to_bytes(raw_hex: &str) -> Vec<u8> {
93        hex::FromHex::from_hex(raw_hex).ok().unwrap()
94    }
95    struct TestVector {
96                key:  Vec<u8>,
97                iv:  Vec<u8>,
98                plain_text: Vec<u8>,
99                cipher_text:  Vec<u8>,
100                aad: Vec<u8>,
101                tag:  Vec<u8>,
102            }
103
104    fn get_test_vectors()-> [TestVector; 5]{
105      [
106        TestVector {
107                key: hex_to_bytes("00000000000000000000000000000000"),
108                iv: hex_to_bytes("000000000000000000000000"),
109                plain_text: hex_to_bytes(""),
110                cipher_text: hex_to_bytes(""),
111                aad: hex_to_bytes(""),
112                tag: hex_to_bytes("58e2fccefa7e3061367f1d57a4e7455a")
113            },
114            TestVector {
115                key: hex_to_bytes("00000000000000000000000000000000"),
116                iv: hex_to_bytes("000000000000000000000000"),
117                plain_text: hex_to_bytes("00000000000000000000000000000000"),
118                cipher_text: hex_to_bytes("0388dace60b6a392f328c2b971b2fe78"),
119                aad: hex_to_bytes(""),
120                tag: hex_to_bytes("ab6e47d42cec13bdf53a67b21257bddf")
121            },
122            TestVector {
123                key: hex_to_bytes("feffe9928665731c6d6a8f9467308308"),
124                iv: hex_to_bytes("cafebabefacedbaddecaf888"),
125                plain_text: hex_to_bytes("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39"),
126                cipher_text: hex_to_bytes("42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091"),
127                aad: hex_to_bytes("feedfacedeadbeeffeedfacedeadbeefabaddad2"),
128                tag: hex_to_bytes("5bc94fbc3221a5db94fae95ae7121a47")
129            },
130            TestVector {
131                key: hex_to_bytes("feffe9928665731c6d6a8f9467308308feffe9928665731c"),
132                iv: hex_to_bytes("cafebabefacedbaddecaf888"),
133                plain_text: hex_to_bytes("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39"),
134                cipher_text: hex_to_bytes("3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710"),
135                aad: hex_to_bytes("feedfacedeadbeeffeedfacedeadbeefabaddad2"),
136                tag: hex_to_bytes("2519498e80f1478f37ba55bd6d27618c")
137            },
138            TestVector {
139                key: hex_to_bytes("feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308"),
140                iv: hex_to_bytes("cafebabefacedbaddecaf888"),
141                plain_text: hex_to_bytes("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39"),
142                cipher_text: hex_to_bytes("522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662"),
143                aad: hex_to_bytes("feedfacedeadbeeffeedfacedeadbeefabaddad2"),
144                tag: hex_to_bytes("76fc6ece0f4e1768cddf8853bb2d551b")
145            },
146    ]
147}
148    #[test]
149    fn aes_gcm_test() {
150            
151        for item in get_test_vectors().iter() {
152            let key_size = match item.key.len() {
153                16 => KeySize::KeySize128,
154                24 => KeySize::KeySize192,
155                32 => KeySize::KeySize256,
156                _ => unreachable!()
157            };
158            let mut cipher = AesGcm::new(key_size, &item.key[..], &item.iv[..], &item.aad[..]);
159            let mut out: Vec<u8> = repeat(0).take(item.plain_text.len()).collect();
160            
161            let mut out_tag: Vec<u8> = repeat(0).take(16).collect();
162            
163            cipher.encrypt(&item.plain_text[..], &mut out[..],&mut out_tag[..]);
164            assert_eq!(out, item.cipher_text);
165            assert_eq!(out_tag, item.tag);
166        }
167    }
168
169    #[test]
170    fn aes_gcm_decrypt_test() {
171            
172        for item in get_test_vectors().iter() {
173            let key_size = match item.key.len() {
174                16 => KeySize::KeySize128,
175                24 => KeySize::KeySize192,
176                32 => KeySize::KeySize256,
177                _ => unreachable!()
178            };
179            let mut decipher = AesGcm::new(key_size, &item.key[..], &item.iv[..], &item.aad[..]);
180            let mut out: Vec<u8> = repeat(0).take(item.plain_text.len()).collect();
181                        
182            let result = decipher.decrypt(&item.cipher_text[..], &mut out[..], &item.tag[..]);
183            assert_eq!(out, item.plain_text);
184            assert!(result);
185        }
186    }
187    #[test]
188    fn aes_gcm_decrypt_fail_test() {
189            
190        for item in get_test_vectors().iter() {
191            let key_size = match item.key.len() {
192                16 => KeySize::KeySize128,
193                24 => KeySize::KeySize192,
194                32 => KeySize::KeySize256,
195                _ => unreachable!()
196            };
197            let mut decipher = AesGcm::new(key_size, &item.key[..], &item.iv[..], &item.aad[..]);
198            let tag: Vec<u8> = repeat(0).take(16).collect();
199            let mut out1: Vec<u8> = repeat(0).take(item.plain_text.len()).collect();
200            let out2: Vec<u8> = repeat(0).take(item.plain_text.len()).collect();
201            let result = decipher.decrypt(&item.cipher_text[..], &mut out1[..], &tag[..]);
202            assert_eq!(out1, out2);
203            assert!(!result);
204        }
205    }
206
207}
208
209#[cfg(all(test, feature = "with-bench"))]
210mod bench {
211    use test::Bencher;
212    use aes::KeySize;
213    use aes_gcm::AesGcm;
214    use aead::{AeadEncryptor, AeadDecryptor};
215    use hex::FromHex;
216
217    #[bench]
218    pub fn gsm_10(bh: & mut Bencher) {
219    	let input = [1u8; 10];
220    	let aad = [3u8; 10];
221    	bh.iter( || {
222	        let mut cipher = AesGcm::new(KeySize::KeySize256, &[0; 32], &[0; 12], &aad);
223	        let mut decipher = AesGcm::new(KeySize::KeySize256, &[0; 32], &[0; 12], &aad);
224	        
225	        let mut output = [0u8; 10];
226	        let mut tag = [0u8; 16];
227	        let mut output2 = [0u8; 10];
228            cipher.encrypt(&input, &mut output, &mut tag);
229            decipher.decrypt(&output, &mut output2, &tag);
230            
231        });
232        bh.bytes = 10u64;
233    }
234        
235
236    #[bench]
237    pub fn gsm_1k(bh: & mut Bencher) {
238    	let input = [1u8; 1024];
239    	let aad = [3u8; 1024];
240    	bh.iter( || {
241        let mut cipher = AesGcm::new(KeySize::KeySize256, &[0; 32], &[0; 12], &aad);
242        let mut decipher = AesGcm::new(KeySize::KeySize256, &[0; 32], &[0; 12], &aad);
243        
244        let mut output = [0u8; 1024];
245        let mut tag = [0u8; 16];
246        let mut output2 = [0u8; 1024];
247        
248            cipher.encrypt(&input, &mut output, &mut tag);
249            decipher.decrypt(&output, &mut output2, &tag);
250        });
251    	bh.bytes = 1024u64;
252        
253    }
254
255    #[bench]
256    pub fn gsm_64k(bh: & mut Bencher) {
257    	let input = [1u8; 65536];
258    	let aad = [3u8; 65536];
259    	  bh.iter( || {
260        let mut cipher = AesGcm::new(KeySize::KeySize256, &[0; 32], &[0; 12], &aad);
261        let mut decipher = AesGcm::new(KeySize::KeySize256, &[0; 32], &[0; 12], &aad);
262        
263        let mut output = [0u8; 65536];
264        let mut tag = [0u8; 16];
265        let mut output2 = [0u8; 65536];
266      
267            cipher.encrypt(&input, &mut output, &mut tag);
268            decipher.decrypt(&output, &mut output2, &tag);
269
270        });
271    	   bh.bytes = 65536u64;
272        
273    }
274}