DEV Community

Cover image for CryptoPals Crypto Challenges Using Rust: Single-byte xor cipher
Naveen ⚑
Naveen ⚑

Posted on

2 2

CryptoPals Crypto Challenges Using Rust: Single-byte xor cipher

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

Context πŸ’‘

So, things are starting to get interesting now.
We're given a hex encoded string. This string has been XORed against a single character. We don't know what character. We have to find this character (aka "key" of this encryption) & decrypt the message.

Let's say the message was somesecret (length 10) then it was XORed against let's say char k, then:

somesecret ^ kkkkkkkkkk ('k' repeated 10 times) = somecypher
Enter fullscreen mode Exit fullscreen mode

Now, if we want to decipher the given encrypted message - somecypher, we can XOR it with the same key:

somecipher ^ kkkkkkkkkk ('k' repeated 10 times) = somesecret
Enter fullscreen mode Exit fullscreen mode

We're going to exploit this property of XOR encryption to decipher the message here.

Crack πŸ•Ά

The way we're going to break is by brute-forcing with all chars. By looping through each char - c & using repeated c as key, we'll get a message. Now none of the decrypted messages would make sense. Except only one - the one with the actual key! All other would spit out some stupid random sequence of characters.

somecipher ^ aaaaaaaaaa = axow&)!923 (nah!)
somecipher ^ bbbbbbbbbb = cgow*&=8u2 (nah!)
somecipher ^ cccccccccc = yz!6w+)#93 (nah!)
.
.
somecipher ^ kkkkkkkkkk = somesecret (looks like a secret!)
Enter fullscreen mode Exit fullscreen mode

We'd need to devise some method to programmatically detect sense-making English text by scoring each message corressponding to each char c as key. Scoring by letter frequency scoring would suffice for our purpose. You can learn more about it here.

Here's one implementation in Rust:

const LETTER_FREQ: [f64; 27] = [
    0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015, // A-G
    0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749, // H-N
    0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758, // O-U
    0.00978, 0.02360, 0.00150, 0.01974, 0.00074, 0.19181, // V-Z & space char
];

pub fn calc_letter_freq_score(s: &str) -> f64 {
    let mut counts = vec![0_u32; 27];
    let mut score: f64 = 0_f64;

    s.chars().for_each(|c| match c {
        'a'..='z' => {
            counts[c as usize - 97] += 1;
        }
        'A'..='Z' => {
            counts[c as usize - 65] += 1;
        }
        ' ' => counts[26] += 1,
        _ => {}
    });

    for i in 0..27 {
        score += (counts[i] as f64) * LETTER_FREQ[i];
    }

    score
}
Enter fullscreen mode Exit fullscreen mode

Note that for the challenge I've also included a frequency of space character at the very end.

Now to the core problem. We loop through every char as byte, xor this byte with every byte of given cipher. Convert XORed bytes to a text (String), then calculate this text's (potentially message) letter frequency score. Retain the deciphered message producing the best score & corresponding key char.

use hex;

pub fn decipher_message(hex_cipher: &str) -> (String, f64) {
    let cipher_bytes = hex::decode(hex_cipher).unwrap();
    let mut key_byte: u8;

    let mut message = String::new();
    let mut best_score = f64::MIN;
    for c in 0..=255 {
        key_byte = c as u8;

        let msg_bytes: Vec<u8> = cipher_bytes.iter().map(|&b| b ^ key_byte).collect();

        let msg = String::from_utf8_lossy(&msg_bytes);
        let score = calc_letter_freq_score(&msg);

        if score > best_score {
            best_score = score;
            message = String::from(msg);
        }
    }

    (message, best_score)
}

Enter fullscreen mode Exit fullscreen mode

And in a snap, cipher is broken! 😎

See code on Github

Find me on:
Twitter - @heyNvN

naveeen.com

Image of Bright Data

Feed Your Models Real-Time Data – Enhance your AI with up-to-the-minute data.

Utilize our live data feeds for dynamic model training, ensuring your AI systems are always ahead.

Access Real-Time Data

Top comments (0)

Image of AssemblyAI

Automatic Speech Recognition with AssemblyAI

Experience near-human accuracy, low-latency performance, and advanced Speech AI capabilities with AssemblyAI's Speech-to-Text API. Sign up today and get $50 in API credit. No credit card required.

Try the API

πŸ‘‹ Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay