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");
}