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}
54
55impl<'a> AeadEncryptor for AesGcm<'static> {
56    fn encrypt(&mut self, input: &[u8], output: &mut [u8], tag: &mut [u8]) {
57        assert!(input.len() == output.len());
58        assert!(!self.finished);
59        self.cipher.process(input, output);
60        let result = self.mac.input_c(output).result();
61        self.finished = true;
62        for i in 0..16 {
63            tag[i] = result[i] ^ self.end_tag[i];
64        }
65    }
66}
67
68impl<'a> AeadDecryptor for AesGcm<'static> {
69    fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8])  -> bool {
70        assert!(input.len() == output.len());
71        assert!(!self.finished);
72        self.finished = true;
73        let mut calc_tag = self.mac.input_c(input).result();
74        for i in 0..16 {
75            calc_tag[i] ^= self.end_tag[i];
76        }
77        if fixed_time_eq(&calc_tag, tag) {
78            self.cipher.process(input, output);
79            true
80        } else {
81            false
82        }
83    }
84}
85
86#[cfg(test)]
87mod test {
88    use aes::KeySize;
89    use aes_gcm::AesGcm;
90    use aead::{AeadEncryptor, AeadDecryptor};
91    use serialize::hex::FromHex;
92    use std::iter::repeat;
93    fn hex_to_bytes(raw_hex: &str) -> Vec<u8> {
94        raw_hex.from_hex().ok().unwrap()
95    }
96    struct TestVector {
97                key:  Vec<u8>,
98                iv:  Vec<u8>,
99                plain_text: Vec<u8>,
100                cipher_text:  Vec<u8>,
101                aad: Vec<u8>,
102                tag:  Vec<u8>,
103            }
104
105    fn get_test_vectors()-> [TestVector; 5]{
106      [
107        TestVector {
108                key: hex_to_bytes("00000000000000000000000000000000"),
109                iv: hex_to_bytes("000000000000000000000000"),
110                plain_text: hex_to_bytes(""),
111                cipher_text: hex_to_bytes(""),
112                aad: hex_to_bytes(""),
113                tag: hex_to_bytes("58e2fccefa7e3061367f1d57a4e7455a")
114            },
115            TestVector {
116                key: hex_to_bytes("00000000000000000000000000000000"),
117                iv: hex_to_bytes("000000000000000000000000"),
118                plain_text: hex_to_bytes("00000000000000000000000000000000"),
119                cipher_text: hex_to_bytes("0388dace60b6a392f328c2b971b2fe78"),
120                aad: hex_to_bytes(""),
121                tag: hex_to_bytes("ab6e47d42cec13bdf53a67b21257bddf")
122            },
123            TestVector {
124                key: hex_to_bytes("feffe9928665731c6d6a8f9467308308"),
125                iv: hex_to_bytes("cafebabefacedbaddecaf888"),
126                plain_text: hex_to_bytes("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39"),
127                cipher_text: hex_to_bytes("42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091"),
128                aad: hex_to_bytes("feedfacedeadbeeffeedfacedeadbeefabaddad2"),
129                tag: hex_to_bytes("5bc94fbc3221a5db94fae95ae7121a47")
130            },
131            TestVector {
132                key: hex_to_bytes("feffe9928665731c6d6a8f9467308308feffe9928665731c"),
133                iv: hex_to_bytes("cafebabefacedbaddecaf888"),
134                plain_text: hex_to_bytes("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39"),
135                cipher_text: hex_to_bytes("3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710"),
136                aad: hex_to_bytes("feedfacedeadbeeffeedfacedeadbeefabaddad2"),
137                tag: hex_to_bytes("2519498e80f1478f37ba55bd6d27618c")
138            },
139            TestVector {
140                key: hex_to_bytes("feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308"),
141                iv: hex_to_bytes("cafebabefacedbaddecaf888"),
142                plain_text: hex_to_bytes("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39"),
143                cipher_text: hex_to_bytes("522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662"),
144                aad: hex_to_bytes("feedfacedeadbeeffeedfacedeadbeefabaddad2"),
145                tag: hex_to_bytes("76fc6ece0f4e1768cddf8853bb2d551b")
146            },
147    ]
148}
149    #[test]
150    fn aes_gcm_test() {
151            
152        for item in get_test_vectors().iter() {
153            let key_size = match item.key.len() {
154                16 => KeySize::KeySize128,
155                24 => KeySize::KeySize192,
156                32 => KeySize::KeySize256,
157                _ => unreachable!()
158            };
159            let mut cipher = AesGcm::new(key_size, &item.key[..], &item.iv[..], &item.aad[..]);
160            let mut out: Vec<u8> = repeat(0).take(item.plain_text.len()).collect();
161            
162            let mut out_tag: Vec<u8> = repeat(0).take(16).collect();
163            
164            cipher.encrypt(&item.plain_text[..], &mut out[..],&mut out_tag[..]);
165            assert_eq!(out, item.cipher_text);
166            assert_eq!(out_tag, item.tag);
167        }
168    }
169
170    #[test]
171    fn aes_gcm_decrypt_test() {
172            
173        for item in get_test_vectors().iter() {
174            let key_size = match item.key.len() {
175                16 => KeySize::KeySize128,
176                24 => KeySize::KeySize192,
177                32 => KeySize::KeySize256,
178                _ => unreachable!()
179            };
180            let mut decipher = AesGcm::new(key_size, &item.key[..], &item.iv[..], &item.aad[..]);
181            let mut out: Vec<u8> = repeat(0).take(item.plain_text.len()).collect();
182                        
183            let result = decipher.decrypt(&item.cipher_text[..], &mut out[..], &item.tag[..]);
184            assert_eq!(out, item.plain_text);
185            assert!(result);
186        }
187    }
188    #[test]
189    fn aes_gcm_decrypt_fail_test() {
190            
191        for item in get_test_vectors().iter() {
192            let key_size = match item.key.len() {
193                16 => KeySize::KeySize128,
194                24 => KeySize::KeySize192,
195                32 => KeySize::KeySize256,
196                _ => unreachable!()
197            };
198            let mut decipher = AesGcm::new(key_size, &item.key[..], &item.iv[..], &item.aad[..]);
199            let tag: Vec<u8> = repeat(0).take(16).collect();
200            let mut out1: Vec<u8> = repeat(0).take(item.plain_text.len()).collect();
201            let out2: Vec<u8> = repeat(0).take(item.plain_text.len()).collect();
202            let result = decipher.decrypt(&item.cipher_text[..], &mut out1[..], &tag[..]);
203            assert_eq!(out1, out2);
204            assert!(!result);
205        }
206    }
207
208}
209
210#[cfg(all(test, feature = "with-bench"))]
211mod bench {
212    use test::Bencher;
213    use aes::KeySize;
214    use aes_gcm::AesGcm;
215    use aead::{AeadEncryptor, AeadDecryptor};
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}