Symmetric encryption in Rust

I'm working on a file encryption utility (endec) in Rust. Encrypting and decrypting doesn't need much code, but it took me several hours of fiddling to arrive at the final version, so I thought I'd share the result.

The crates I'm using (for the symmetric encryption part) are:

So let's get right down to the code:

use ::aes::Aes256;
use ::block_modes::Cbc;
use ::block_modes::BlockMode;
use ::block_modes::block_padding::Iso7816;
use ::secstr::SecVec;

// Set up the cipher.
// Cbc means each block affects the next, which is more secure than Ecb.
// Aes256 is the actual encryption/decryption algorithm.
// Iso7816 is the padding using if the data doesn't fit in the block size.
type Aes256Cbc = Cbc<Aes256, Iso7816>;

#[test]
fn demo() {
    // Key must be 32 bytes for Aes256. It should probably be the hashed
    // version of the input key, so is not limited to printable ascii.
    // SecVec has several advantages in preventing the key from leaking.
    let key = SecVec::from(b"RvzQW3Mwrc!_y5-DpPZl8rP3,=HsD1,!".to_vec());

    // The initialization vector (like salt or nonce) must be 16 bytes for
    // this block size. It could be generated using a secure randon generator,
    // and should be different each time. It is not a secret.
    let iv = vec![
        89, 63, 254, 34, 209, 155, 236, 158, 195, 104, 11, 16, 240, 4, 26, 76
    ];

    // This is the data that is to be encrypted.
    let plaintext: Vec<u8> = b"Hello world! This is the secret text...".to_vec();

    // Encryption.
    // Fails if the key or iv are the wrong length, so it is safe to unwrap
    // as we have the correct lengths. Key length depends on algorithm, iv length
    // depends on the block size. If it's not documented, experiment with 16 or 32.
    let cipher = Aes256Cbc::new_var(
        key.unsecure(), &iv).unwrap();
    let ciphertext = cipher.encrypt_vec(&plaintext);

    // Check that it worked.
    assert_eq!(&ciphertext, &vec![
        216, 56, 166, 254, 171, 163, 243, 167, 235, 179, 189, 132, 0, 202, 44, 73,
        10, 68, 229, 90, 69, 212, 24, 22, 87, 109, 34, 92, 254, 136, 141, 154, 57,
        189, 176, 221, 140, 8, 114, 141, 103, 248, 108, 182, 247, 156, 113, 127,
    ]);

    // Decryption.
    let cipher = Aes256Cbc::new_var(
        key.unsecure(), &iv).unwrap();
    let decrypted_ciphertext = cipher.decrypt_vec(&ciphertext).unwrap();

    // Check that we got the original input back.
    assert_eq!(decrypted_ciphertext, plaintext);
}

I struggled a bit with the key sizes, block vs stream mode, and somewhat sparse documentation. But in the end it's pretty easy, as you can see.

There's more to endec, like key stretching, compression, checksums and backwards compatibility headers. If you want those details, have a look at endec's source.

 

Topics: #coding #rust #security #example

Comments

No comments yet

You need to be logged in to comment.