The (Near) Perfect Encryption Method and The Best Programming Language: Meet AES-GCM and Rust

There’s a bit of a barrier to Rust, but I can assure you that once you are over the barrier your days of slow and sloppy code are behind…

Photo by Angela M. on Unsplash

The (Near) Perfect Encryption Method and The Best Programming Language: Meet AES-GCM and Rust

There’s a bit of a barrier to Rust, but I can assure you that once you are over the barrier your days of slow and sloppy code are behind you. For someone used to C, it gives you all the power that you had before, but fit for a modern world of software. To me, Rust is the perfect programming language for cybersecurity. I think that it teaches me as I go along, and scolds me for bad practice, and tells me to go to the back of the class, and improve.

And what about the perfect encryption method? Well, for symmetric key encryption there is few better than AES. But, it’s a block cipher, and needs paper, and also the chaining of blocks. The encryption thus has to be done sequentially — a block at a time. With the stream ciphers — such as RC4 and ChaCha20 — we can whizz along with parallel processing of the cipher streams, and without the need for that horrible padding of the blocks. But, wait! AES can be covered into a stream cipher with the addition of a counter mode.

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).

AES GCM and Rust

And so the perfect cipher is AES GCM, and the perfect language is Rust, so let’s go. 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.

An outline of the Rust code is given next. In this case, the additional data will be defined as the static string of “Additional Data for authentication!” [here]:

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 [here]:

== 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!

And here is the running example:

https://asecuritysite.com/rust/rust_gcm

Conclusion

The (near) perfect encryption mode (AES-CGM) and the (near) perfect programming language (Rust). If you are into cybersecurity (and we all should be) and have learnt Python, there’s no better language to learn than Rust. It will force you to be “secure-by-design”, and get rid of your sloppy coding habits. Once you get over the learning of the environment and language, your productivity will increase massively (I think). If you are interested, here are some examples:

https://asecuritysite.com/rust