GCM (Galois Counter Mode) is an encryption method with authentication for symmetric key encryption. It is fast and allows for parallel processing. For this, we can use the wonderful AES GCM (Galois Counter Mode). And, another great advantage is that it has the option of adding additional data that is not encrypted but can be used to authenticate the cipher. For this, we might allow the additional data to be bound to a specific session ID or with a given time. When we read the cipher, we can check it against the additional data. This option is named AEAD (Authenticated Encryption with Additional Data). In the code, we will use a 128-bit encryption key, but we could easily also use a 192-bit one or a 256-bit one. For 128 bits, we need 32 hex characters (16 bytes) to define the key. With the salt value (IV — Initialisation Vector) we have a 96-bit value, and which is 24 hex characters (12 bytes). For the additional data, we can have any length of data. This data is not encrypted.
AES GCM in Rust |
An outline of the Rust code is:
extern crate base64; extern crate hex; extern crate rustc_serialize; use crypto::aes_gcm::AesGcm; use crypto::aead::{AeadDecryptor, AeadEncryptor}; use rustc_serialize::hex::FromHex; use std::env; use core::str; use std::iter::repeat; fn hex_to_bytes(s: &str) -> Vec{ s.from_hex().unwrap() } fn main() { let mut mykey="00000000000000000000000000000000"; let mut msg=""; let myadd="Additional Data for authentication!"; let mut myiv="000000000000000000000000"; let args: Vec = env::args().collect(); if args.len() >1 { msg = args[1].as_str();} if args.len() >2 { mykey = args[2].as_str();} if args.len() >3 { myiv = args[3].as_str();} println!("== AES GCM =="); println!("Message: {:?}",msg); println!("Key: {:?}",mykey); println!("IV: {:?}",myiv); println!("Additional: {:?}",myadd); let key=&hex_to_bytes( mykey)[..]; let iv=&hex_to_bytes( myiv)[..]; let plain=msg.as_bytes(); let add=myadd.as_bytes(); let key_size=crypto::aes::KeySize::KeySize128; let mut cipher = AesGcm::new(key_size, key, iv, add); let mut out: Vec = repeat(0).take(plain.len()).collect(); let mut out_tag: Vec<u8> = repeat(0).take(16).collect(); cipher.encrypt(plain, &mut out[..],&mut out_tag[..]); let mut decipher = AesGcm::new(key_size, key, iv, add); let mut out2: Vec<u8> = repeat(0).take(plain.len()).collect(); let result = decipher.decrypt(&out[..], &mut out2[..], &out_tag[..]); println!("\nEncrypted: {}",hex::encode(out.clone())); if (result==true) { println!("Successful decryption");} println!("\nDecrypted {}",str::from_utf8(&out2[..]).unwrap()); } u8> = repeat(0).take(plain.len()).collect(); let mut out_tag: Vec<u8> = repeat(0).take(16).collect(); cipher.encrypt(plain, &mut out[..],&mut out_tag[..]); let mut decipher = AesGcm::new(key_size, key, iv, add); let mut out2: Vec<u8> = repeat(0).take(plain.len()).collect(); let result = decipher.decrypt(&out[..], &mut out2[..], &out_tag[..]); println!("\nEncrypted: {}",hex::encode(out.clone())); if (result==true) { println!("Successful decryption");} println!("\nDecrypted {}",str::from_utf8(&out2[..]).unwrap()); }
Finally we simply build with:
cargo build
A sample run is:
== AES GCM == Message: "This is a secret message!" Key: "00000000000000000000000000000004" IV: "100000000000000000000001" Additional: "Additional Data for authentication!" Encrypted: 1633b755176c88a2d1783769a9f6da08245fc3d05859ff3292 Successful decryption Decrypted This is a secret message!
The TOML file is:
[package] name = "gcm" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] rust-crypto="0.2.0" hex="0.4.3" base64="0.13.0" rustc-serialize = "0.3"