~shreyasminocha/cryptopals

5928b2153535bfc9ee7915e8340f86ffe72c4a2a — Shreyas Minocha 1 year, 2 months ago 7f538ec main
Almost complete challenge 17
4 files changed, 89 insertions(+), 5 deletions(-)

M examples/10.rs
M examples/16.rs
A examples/17.rs
M src/aes.rs
M examples/10.rs => examples/10.rs +1 -1
@@ 7,7 7,7 @@ fn main() -> Result<(), Box<dyn Error>> {
    let bytes = base64_to_bytes(base64).unwrap();

    let decrypted = cbc_decrypt(bytes, b"YELLOW SUBMARINE".to_vec(), b"\x00".repeat(16));
    println!("{}", String::from_utf8(decrypted)?);
    println!("{}", String::from_utf8(decrypted.unwrap())?);

    Ok(())
}

M examples/16.rs => examples/16.rs +1 -1
@@ 51,7 51,7 @@ fn construct_and_encrypt(string: String, key: Vec<u8>, iv: Vec<u8>) -> Vec<u8> {
}

fn is_admin(ciphertext: Vec<u8>, key: Vec<u8>, iv: Vec<u8>) -> bool {
    let plaintext = cbc_decrypt(ciphertext, key, iv);
    let plaintext = cbc_decrypt(ciphertext, key, iv).unwrap();
    let plaintext_string = String::from_utf8_lossy(&plaintext);

    let parsed = plaintext_string.split(';').map(|s| {

A examples/17.rs => examples/17.rs +84 -0
@@ 0,0 1,84 @@
use std::{error::Error, fs::read_to_string};

use cryptopals::{
    aes::{cbc_decrypt, cbc_encrypt, random_iv, random_key},
    decoding::base64_to_bytes,
};
use rand::Rng;

fn main() -> Result<(), Box<dyn Error>> {
    let key = random_key();
    let (ciphertext, iv) = generate_ciphertext(key.clone());

    let plaintext = attack(ciphertext, &|ct| {
        padding_oracle(ct, key.clone(), iv.clone())
    });
    println!("{}", String::from_utf8_lossy(plaintext.as_slice()));

    Ok(())
}

fn attack(bytes: Vec<u8>, has_valid_padding: &dyn Fn(Vec<u8>) -> bool) -> Vec<u8> {
    let mut plaintext: Vec<u8> = vec![];

    let mut bytes = bytes;
    let mut ciphertext = bytes.clone();
    let num_blocks = ciphertext.len() / 16;

    for _ in 0..num_blocks - 1 {
        let mut ccccc = ciphertext.clone();
        let mut plaintext_block: Vec<u8> = vec![0; 16];

        for position in 1..=16 {
            let len = ccccc.len();

            let og = ccccc[len - 16 - position];
            let mut magic_byte = 0;
            for byte in u8::MIN..u8::MAX {
                let mut ct = ccccc.clone();
                ct[len - 16 - position] = byte;

                if has_valid_padding(ct) {
                    magic_byte = byte;
                    break;
                }
            }

            let pt_byte = ((position as u8) ^ magic_byte) ^ og;
            plaintext_block[16 - position] = pt_byte;

            for i in (1..=position).rev() {
                ccccc[len - 16 - i] =
                    (bytes[len - 16 - i] ^ (position + 1) as u8) ^ plaintext_block[16 - i];
            }
        }

        plaintext.extend(plaintext_block.iter().rev());

        ciphertext = ciphertext[..ciphertext.len() - 16].to_vec();
        bytes = bytes[..bytes.len() - 16].to_vec();
    }

    plaintext.reverse();
    plaintext
}

fn padding_oracle(ciphertext: Vec<u8>, key: Vec<u8>, iv: Vec<u8>) -> bool {
    cbc_decrypt(ciphertext, key, iv).is_ok()
}

fn generate_ciphertext(key: Vec<u8>) -> (Vec<u8>, Vec<u8>) {
    let mut rng = rand::thread_rng();

    let file = read_to_string("challenge-data/17.txt").unwrap();
    let plaintexts = file
        .split('\n')
        .map(|l| base64_to_bytes(l.to_string()).unwrap())
        .collect::<Vec<_>>();
    let chosen_plaintext = plaintexts[rng.gen_range(0..plaintexts.len())].to_vec();

    let iv = random_iv();
    let ciphertext = cbc_encrypt(chosen_plaintext, key, iv.clone());

    (ciphertext, iv)
}

M src/aes.rs => src/aes.rs +3 -3
@@ 60,7 60,7 @@ pub fn cbc_encrypt(plaintext: Vec<u8>, key: Vec<u8>, iv: Vec<u8>) -> Vec<u8> {
    ciphertext
}

pub fn cbc_decrypt(ciphertext: Vec<u8>, key: Vec<u8>, iv: Vec<u8>) -> Vec<u8> {
pub fn cbc_decrypt<'a>(ciphertext: Vec<u8>, key: Vec<u8>, iv: Vec<u8>) -> Result<Vec<u8>, &'a str> {
    let cipher = Cipher::aes_128_ecb();

    let mut plaintext = vec![];


@@ 83,7 83,7 @@ pub fn cbc_decrypt(ciphertext: Vec<u8>, key: Vec<u8>, iv: Vec<u8>) -> Vec<u8> {
        plaintext.append(&mut xored_block);
    }

    pkcs7_unpad(plaintext, 16).unwrap()
    pkcs7_unpad(plaintext, 16)
}

pub fn pkcs7_pad(bytes: Vec<u8>, block_size: usize) -> Vec<u8> {


@@ 104,7 104,7 @@ pub fn pkcs7_unpad<'a>(bytes: Vec<u8>, block_size: usize) -> Result<Vec<u8>, &'a
    let final_block = bytes[bytes.len() - block_size..].to_vec();
    let final_byte = final_block[final_block.len() - 1];

    if final_byte > 16 {
    if final_byte == 0 || final_byte > 16 {
        return Err("invalid padding");
    }