aes gcm Algorithm
The AES GCM (Advanced Encryption Standard Galois/Counter Mode) algorithm is a symmetric encryption technique that combines the security of the AES block cipher with the efficiency and parallelism of the Galois/Counter Mode of operation. It is widely used for providing confidentiality, integrity, and authentication of data, making it a popular choice for securing communication channels in various applications, such as secure network protocols, cloud storage, and digital rights management. The AES GCM algorithm is standardized by NIST (National Institute of Standards and Technology) and is considered to be highly secure, efficient, and widely supported.
In the AES GCM algorithm, data is encrypted in blocks using the AES cipher, with each block being processed in parallel, enhancing the speed of encryption and decryption. The algorithm also incorporates a unique initialization vector (IV) for each encryption operation, ensuring the same plaintext input results in different ciphertext outputs, thereby providing greater security. Additionally, AES GCM employs a dedicated authentication mechanism called GMAC (Galois Message Authentication Code) which is based on the Galois Field multiplication, providing data integrity and authentication checks. This combination of encryption and authentication in a single pass makes AES GCM an efficient and secure choice for modern cryptographic applications.
// 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 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;
}
}