From 956d75ab4eaaad271a3e82ffc821f420fb9cbb66 Mon Sep 17 00:00:00 2001 From: Felix Martin Date: Tue, 28 Jun 2022 15:56:31 -0400 Subject: [PATCH] Finish challenge 16 and problem set 2. --- src/bytes.rs | 19 ++++++++++++++++++- src/parser.rs | 8 +++++++- src/set2.rs | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 021c275..ec74ada 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] use rand::Rng; -use std::fmt::Write; // need to import this trait +use std::fmt::Write; #[derive(PartialEq, PartialOrd, Debug)] pub struct Bytes(pub Vec); @@ -117,6 +117,23 @@ impl Bytes { return true; } + pub fn remove_pkcs7(&mut self, block_size: usize) -> () { + if !self.has_valid_pkcs7(block_size) { + return; + } + let Bytes(v) = self; + let pad_byte_count = v[v.len() - 1]; + for _ in 0..(pad_byte_count as usize) { + v.pop(); + } + } + + pub fn flip_bit(&mut self, byte_index: usize, bit_index: usize) -> () { + let Bytes(v) = self; + let flip_mask: u8 = 0b1 << bit_index; + v[byte_index] ^= flip_mask; + } + pub fn xor(Bytes(a): &Bytes, Bytes(b): &Bytes) -> Bytes { Bytes( Iterator::zip(a.iter(), b.iter()) diff --git a/src/parser.rs b/src/parser.rs index 1df0516..a6de9dd 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -5,6 +5,7 @@ pub enum Token { Identifier(String), Equal, Ampersand, + Semicolon, } pub type Tokens = Vec; @@ -19,6 +20,9 @@ pub fn parse_key_value(text: &str) -> HashMap { [Token::Identifier(key), Token::Equal, Token::Identifier(value), Token::Ampersand] => { result.insert(key.to_string(), value.to_string()); } + [Token::Identifier(key), Token::Equal, Token::Identifier(value), Token::Semicolon] => { + result.insert(key.to_string(), value.to_string()); + } [Token::Identifier(key), Token::Equal, Token::Identifier(value)] => { result.insert(key.to_string(), value.to_string()); } @@ -40,6 +44,8 @@ fn scan(code: &str, mut ix: usize, mut tokens: Tokens) -> Tokens { tokens.push(Token::Ampersand); } else if c == '=' { tokens.push(Token::Equal); + } else if c == ';' { + tokens.push(Token::Semicolon); } else { panic!("Unexpected char '{}' at index {}", c, ix); } @@ -62,4 +68,4 @@ fn scan_identifier(code: &str, mut ix: usize, mut tokens: Tokens) -> Tokens { scan(code, ix, tokens) } -const SPECIAL_CHARS: &[char] = &['.', '@']; +const SPECIAL_CHARS: &[char] = &['.', '@', '%']; diff --git a/src/set2.rs b/src/set2.rs index 9855e6b..cc28324 100644 --- a/src/set2.rs +++ b/src/set2.rs @@ -411,5 +411,37 @@ pub fn challenge15() { } pub fn challenge16() { - println!("[xxxx] Challenge 16: TBD"); + fn encrypt(input: &str, key: &Bytes, iv: &Bytes) -> Bytes { + let mut r = String::new(); + for c in input.chars() { + if c == ';' || c == '=' { + panic!("encrypt: invalid char {}", c); + } + } + r.push_str("comment1=cooking%20MCs;userdata="); + r.push_str(input); + r.push_str(";comment2=%20like%20a%20pound%20of%20bacon"); + let mut cleartext = Bytes(r.as_bytes().to_vec()); + cleartext.pad_pkcs7(16); + cbc::encrypt(&key, &iv, &cleartext) + } + + let iv = Bytes::random(16); + let key = Bytes::random(16); + + // 0 16 32 48 64 + // 0..34..78..bc..f0..34..78..bc..f0..34..78..bc..f0..34..78..bc..f0..34..78..bc..f + // comment1=cooking%20MCs;userdata=xxx=x;admin=true;comment2=%20like%20a%20pound%20of%20bacon + // flip_bit(2, '9') = '='; flip_bit(1, '9') = ';' + let mut cipher = encrypt("xxx9x9admin9true", &key, &iv); + cipher.flip_bit(19, 2); // results in flipping same bit in byte of the following block + cipher.flip_bit(21, 1); + cipher.flip_bit(27, 2); + let mut cleartext = cbc::decrypt(&key, &iv, &cipher); + cleartext.remove_pkcs7(16); + let cleartext_stripped = Bytes(cleartext.0[32..].to_vec()); + let dict = parser::parse_key_value(&cleartext_stripped.to_utf8()); + let admin_status = dict.get("admin").unwrap(); + assert_eq!(admin_status, "true"); + println!("[okay] Challenge 16: admin={}", admin_status); }