| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| |
| use std::prelude::v1::*; |
| use aes::{ctr, KeySize}; |
| use aead::{AeadEncryptor,AeadDecryptor}; |
| use cryptoutil::copy_memory; |
| use symmetriccipher::SynchronousStreamCipher; |
| use ghash::{Ghash}; |
| use util::fixed_time_eq; |
| |
| pub struct AesGcm<'a> { |
| cipher: Box<SynchronousStreamCipher + 'a>, |
| mac: Ghash, |
| finished: bool, |
| end_tag: [u8; 16] |
| } |
| |
| impl<'a> AesGcm<'a> { |
| pub fn new (key_size: KeySize, key: &[u8], nonce: &[u8], aad: &[u8]) -> AesGcm<'a> { |
| assert!(key.len() == 16 || key.len() == 24 || key.len() == 32); |
| assert!(nonce.len() == 12); |
| |
| // GCM technically differs from CTR mode in how role overs are handled |
| // GCM only touches the right most 4 bytes while CTR roles all 16 over |
| // when the iv is only 96 bits (12 bytes) then 4 bytes of zeros are |
| // appended to it meaning you have to encrypt 2^37 bytes (256 gigabytes) |
| // of data before a difference crops up. |
| // The GCM handles nonces of other lengths by hashing them once with ghash |
| // this would cause the roleover behavior to potentially be triggered much |
| // earlier preventing the use of generic CTR mode. |
| |
| let mut iv = [0u8; 16]; |
| copy_memory(nonce, &mut iv); |
| iv[15] = 1u8; |
| let mut cipher = ctr(key_size,key,&iv); |
| let temp_block = [0u8; 16]; |
| let mut final_block = [0u8; 16]; |
| cipher.process(&temp_block, &mut final_block); |
| let mut hash_key = [0u8; 16]; |
| let mut encryptor = ctr(key_size,key,&temp_block); |
| encryptor.process(&temp_block, &mut hash_key); |
| AesGcm { |
| cipher: cipher, |
| mac: Ghash::new(&hash_key).input_a(aad), |
| finished: false, |
| end_tag: final_block |
| } |
| } |
| |
| } |
| |
| impl<'a> AeadEncryptor for AesGcm<'static> { |
| fn encrypt(&mut self, input: &[u8], output: &mut [u8], tag: &mut [u8]) { |
| assert!(input.len() == output.len()); |
| assert!(!self.finished); |
| self.cipher.process(input, output); |
| let result = self.mac.input_c(output).result(); |
| self.finished = true; |
| for i in 0..16 { |
| tag[i] = result[i] ^ self.end_tag[i]; |
| } |
| } |
| } |
| |
| impl<'a> AeadDecryptor for AesGcm<'static> { |
| fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool { |
| assert!(input.len() == output.len()); |
| assert!(!self.finished); |
| self.finished = true; |
| let mut calc_tag = self.mac.input_c(input).result(); |
| for i in 0..16 { |
| calc_tag[i] ^= self.end_tag[i]; |
| } |
| if fixed_time_eq(&calc_tag, tag) { |
| self.cipher.process(input, output); |
| true |
| } else { |
| false |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use aes::KeySize; |
| use aes_gcm::AesGcm; |
| use aead::{AeadEncryptor, AeadDecryptor}; |
| use serialize::hex::FromHex; |
| use std::iter::repeat; |
| fn hex_to_bytes(raw_hex: &str) -> Vec<u8> { |
| raw_hex.from_hex().ok().unwrap() |
| } |
| struct TestVector { |
| key: Vec<u8>, |
| iv: Vec<u8>, |
| plain_text: Vec<u8>, |
| cipher_text: Vec<u8>, |
| aad: Vec<u8>, |
| tag: Vec<u8>, |
| } |
| |
| fn get_test_vectors()-> [TestVector; 5]{ |
| [ |
| TestVector { |
| key: hex_to_bytes("00000000000000000000000000000000"), |
| iv: hex_to_bytes("000000000000000000000000"), |
| plain_text: hex_to_bytes(""), |
| cipher_text: hex_to_bytes(""), |
| aad: hex_to_bytes(""), |
| tag: hex_to_bytes("58e2fccefa7e3061367f1d57a4e7455a") |
| }, |
| TestVector { |
| key: hex_to_bytes("00000000000000000000000000000000"), |
| iv: hex_to_bytes("000000000000000000000000"), |
| plain_text: hex_to_bytes("00000000000000000000000000000000"), |
| cipher_text: hex_to_bytes("0388dace60b6a392f328c2b971b2fe78"), |
| aad: hex_to_bytes(""), |
| tag: hex_to_bytes("ab6e47d42cec13bdf53a67b21257bddf") |
| }, |
| TestVector { |
| key: hex_to_bytes("feffe9928665731c6d6a8f9467308308"), |
| iv: hex_to_bytes("cafebabefacedbaddecaf888"), |
| plain_text: hex_to_bytes("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39"), |
| cipher_text: hex_to_bytes("42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091"), |
| aad: hex_to_bytes("feedfacedeadbeeffeedfacedeadbeefabaddad2"), |
| tag: hex_to_bytes("5bc94fbc3221a5db94fae95ae7121a47") |
| }, |
| TestVector { |
| key: hex_to_bytes("feffe9928665731c6d6a8f9467308308feffe9928665731c"), |
| iv: hex_to_bytes("cafebabefacedbaddecaf888"), |
| plain_text: hex_to_bytes("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39"), |
| cipher_text: hex_to_bytes("3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710"), |
| aad: hex_to_bytes("feedfacedeadbeeffeedfacedeadbeefabaddad2"), |
| tag: hex_to_bytes("2519498e80f1478f37ba55bd6d27618c") |
| }, |
| TestVector { |
| key: hex_to_bytes("feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308"), |
| iv: hex_to_bytes("cafebabefacedbaddecaf888"), |
| plain_text: hex_to_bytes("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39"), |
| cipher_text: hex_to_bytes("522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662"), |
| aad: hex_to_bytes("feedfacedeadbeeffeedfacedeadbeefabaddad2"), |
| tag: hex_to_bytes("76fc6ece0f4e1768cddf8853bb2d551b") |
| }, |
| ] |
| } |
| #[test] |
| fn aes_gcm_test() { |
| |
| for item in get_test_vectors().iter() { |
| let key_size = match item.key.len() { |
| 16 => KeySize::KeySize128, |
| 24 => KeySize::KeySize192, |
| 32 => KeySize::KeySize256, |
| _ => unreachable!() |
| }; |
| let mut cipher = AesGcm::new(key_size, &item.key[..], &item.iv[..], &item.aad[..]); |
| let mut out: Vec<u8> = repeat(0).take(item.plain_text.len()).collect(); |
| |
| let mut out_tag: Vec<u8> = repeat(0).take(16).collect(); |
| |
| cipher.encrypt(&item.plain_text[..], &mut out[..],&mut out_tag[..]); |
| assert_eq!(out, item.cipher_text); |
| assert_eq!(out_tag, item.tag); |
| } |
| } |
| |
| #[test] |
| fn aes_gcm_decrypt_test() { |
| |
| for item in get_test_vectors().iter() { |
| let key_size = match item.key.len() { |
| 16 => KeySize::KeySize128, |
| 24 => KeySize::KeySize192, |
| 32 => KeySize::KeySize256, |
| _ => unreachable!() |
| }; |
| let mut decipher = AesGcm::new(key_size, &item.key[..], &item.iv[..], &item.aad[..]); |
| let mut out: Vec<u8> = repeat(0).take(item.plain_text.len()).collect(); |
| |
| let result = decipher.decrypt(&item.cipher_text[..], &mut out[..], &item.tag[..]); |
| assert_eq!(out, item.plain_text); |
| assert!(result); |
| } |
| } |
| #[test] |
| fn aes_gcm_decrypt_fail_test() { |
| |
| for item in get_test_vectors().iter() { |
| let key_size = match item.key.len() { |
| 16 => KeySize::KeySize128, |
| 24 => KeySize::KeySize192, |
| 32 => KeySize::KeySize256, |
| _ => unreachable!() |
| }; |
| let mut decipher = AesGcm::new(key_size, &item.key[..], &item.iv[..], &item.aad[..]); |
| let tag: Vec<u8> = repeat(0).take(16).collect(); |
| let mut out1: Vec<u8> = repeat(0).take(item.plain_text.len()).collect(); |
| let out2: Vec<u8> = repeat(0).take(item.plain_text.len()).collect(); |
| let result = decipher.decrypt(&item.cipher_text[..], &mut out1[..], &tag[..]); |
| assert_eq!(out1, out2); |
| assert!(!result); |
| } |
| } |
| |
| } |
| |
| #[cfg(all(test, feature = "with-bench"))] |
| mod bench { |
| use test::Bencher; |
| use aes::KeySize; |
| use aes_gcm::AesGcm; |
| use aead::{AeadEncryptor, AeadDecryptor}; |
| |
| #[bench] |
| pub fn gsm_10(bh: & mut Bencher) { |
| let input = [1u8; 10]; |
| let aad = [3u8; 10]; |
| bh.iter( || { |
| let mut cipher = AesGcm::new(KeySize::KeySize256, &[0; 32], &[0; 12], &aad); |
| let mut decipher = AesGcm::new(KeySize::KeySize256, &[0; 32], &[0; 12], &aad); |
| |
| let mut output = [0u8; 10]; |
| let mut tag = [0u8; 16]; |
| let mut output2 = [0u8; 10]; |
| cipher.encrypt(&input, &mut output, &mut tag); |
| decipher.decrypt(&output, &mut output2, &tag); |
| |
| }); |
| bh.bytes = 10u64; |
| } |
| |
| |
| #[bench] |
| pub fn gsm_1k(bh: & mut Bencher) { |
| let input = [1u8; 1024]; |
| let aad = [3u8; 1024]; |
| bh.iter( || { |
| let mut cipher = AesGcm::new(KeySize::KeySize256, &[0; 32], &[0; 12], &aad); |
| let mut decipher = AesGcm::new(KeySize::KeySize256, &[0; 32], &[0; 12], &aad); |
| |
| let mut output = [0u8; 1024]; |
| let mut tag = [0u8; 16]; |
| let mut output2 = [0u8; 1024]; |
| |
| cipher.encrypt(&input, &mut output, &mut tag); |
| decipher.decrypt(&output, &mut output2, &tag); |
| }); |
| bh.bytes = 1024u64; |
| |
| } |
| |
| #[bench] |
| pub fn gsm_64k(bh: & mut Bencher) { |
| let input = [1u8; 65536]; |
| let aad = [3u8; 65536]; |
| bh.iter( || { |
| let mut cipher = AesGcm::new(KeySize::KeySize256, &[0; 32], &[0; 12], &aad); |
| let mut decipher = AesGcm::new(KeySize::KeySize256, &[0; 32], &[0; 12], &aad); |
| |
| let mut output = [0u8; 65536]; |
| let mut tag = [0u8; 16]; |
| let mut output2 = [0u8; 65536]; |
| |
| cipher.encrypt(&input, &mut output, &mut tag); |
| decipher.decrypt(&output, &mut output2, &tag); |
| |
| }); |
| bh.bytes = 65536u64; |
| |
| } |
| } |