CryptoPals Crypto Challenges Using Rust: Detect AES in ECB mode

nvnx

Naveen ⚡

Posted on January 9, 2022

CryptoPals Crypto Challenges Using Rust: Detect AES in ECB mode

This is Challenge 8 of Cryptopals challenges implemented in Rust language.

Context

We're given a file which lists a bunch of hex-encoded ciphertexts. One of these texts is encrypted with AES-128 in ECB mode. We have to detect which one is it. I recommend to see Challenge 7 if you haven't yet.

Remember that AES-128 divides message into 16 byte blocks before encrypting. In ECB mode each block is encrypted seperately. Therefore - the same 16 byte plaintext block will always produce the same 16 byte ciphertext. We'll exploit this property of ECB mode.

Given the ciphertext, the more number of identical 16-byte blocks, the higher the probability of it having encrypted in ECB mode. Because that means (most probably) the text message contained some identical phrases/blocks that were repeated. And these repeated phrases/blocks encrypted to exactly same ciphertext because of nature of ECB.

...and there was yellow submarine...yellow submarine sank in...
                 |------ECB-----|   |------ECB-----|
              ...a733hj32hbczxsbv...a733hj32hbczxsbv....
Enter fullscreen mode Exit fullscreen mode

Code

In code we basically loop through each hex string. Convert it to bytes then divide these bytes into 16-byte blocks. Then count number of identical blocks among these. The hex-string that corresponds to max number of identical blocks is encrypted in ECB mode.

use hex;
use std::collections::HashSet;
use std::fs::File;
use std::io::{BufRead, BufReader};

pub fn detect_aes_ecb_encryption(path: &str) -> (usize, usize) {
    let file = File::open(path).expect("Error reading file!");
    let lines = BufReader::new(file).lines();
    let mut i_line: usize = 0;
    let mut max_identical_blocks: usize = 0;

    let mut n_identical_blocks: usize;
    for (i, line) in lines.enumerate() {
        let hex = line.unwrap();
        // Hex line to bytes vec
        let bytes = hex::decode(hex).unwrap();

        // Divide bytes into 16 byte blocks (&[u8] blocks)
        let blocks: Vec<_> = bytes.chunks_exact(16).collect();

        // Get unique blocks
        let unique_blocks: HashSet<_> = blocks.iter().cloned().collect();

        // No. of identical blocks detected
        n_identical_blocks = blocks.len() - unique_blocks.len();

        // Cipher containing most identical blocks is more likely to be
        // ECB mode encrypted
        if n_identical_blocks > max_identical_blocks {
            max_identical_blocks = n_identical_blocks;
            i_line = i;
        }
    }

    (i_line, max_identical_blocks)
}
Enter fullscreen mode Exit fullscreen mode

And this is it. We've concluded Set 1.

See code on Github

Find me on:
Twitter - @heyNvN

naveeen.com

💖 💪 🙅 🚩
nvnx
Naveen ⚡

Posted on January 9, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related