Resolve more linter warnings

This commit is contained in:
2022-08-27 14:47:56 -04:00
parent 0951a6ab3e
commit bac75acd2c
14 changed files with 175 additions and 172 deletions

View File

@@ -45,19 +45,16 @@ impl Bytes {
} }
pub fn from_hex(s: &str) -> Bytes { pub fn from_hex(s: &str) -> Bytes {
if s.len() % 2 != 0 { assert!(
panic!("Input string has uneven number of characters"); s.len() % 2 == 0,
} "Input string has uneven number of characters"
);
let bytes_result: Result<Vec<u8>, std::num::ParseIntError> = (0..s.len()) let bytes_result: Result<Vec<u8>, std::num::ParseIntError> = (0..s.len())
.step_by(2) .step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16)) .map(|i| u8::from_str_radix(&s[i..i + 2], 16))
.collect(); .collect();
match bytes_result { Bytes(bytes_result.expect("Invalid hex string"))
Ok(b) => Bytes(b),
Err(_) => panic!("Could not convert all digit pairs to hex."),
}
} }
pub fn to_hex(&self) -> String { pub fn to_hex(&self) -> String {
@@ -84,12 +81,9 @@ impl Bytes {
let mut r = 0; let mut r = 0;
for &c in v.iter() { for &c in v.iter() {
match c { match c {
32 => r += 2, b'!'..=b'@' | b'['..=b'`' | b'{'..=b'~' => r += 1,
33..=64 => r += 1, b' ' => r += 2,
65..=90 => r += 3, b'A'..=b'Z' | b'a'..=b'z' => r += 3,
91..=96 => r += 1,
97..=122 => r += 3,
123..=127 => r += 1,
_ => (), _ => (),
} }
} }
@@ -108,7 +102,9 @@ impl Bytes {
pub fn pad_pkcs7(&mut self, block_size: usize) { pub fn pad_pkcs7(&mut self, block_size: usize) {
let Bytes(v) = self; let Bytes(v) = self;
let padding_value = (block_size - v.len() % block_size) as u8; let padding_value = (block_size - v.len() % block_size)
.try_into()
.expect("Padding value has to be an u8");
for _ in 0..padding_value { for _ in 0..padding_value {
v.push(padding_value); v.push(padding_value);
} }
@@ -180,7 +176,6 @@ impl Bytes {
false false
} }
#[allow(dead_code)]
pub fn hemming(Bytes(a): &Bytes, Bytes(b): &Bytes) -> u32 { pub fn hemming(Bytes(a): &Bytes, Bytes(b): &Bytes) -> u32 {
let v: Vec<u32> = Iterator::zip(a.iter(), b.iter()) let v: Vec<u32> = Iterator::zip(a.iter(), b.iter())
.map(|z| (*(z.0) ^ *(z.1)).count_ones()) .map(|z| (*(z.0) ^ *(z.1)).count_ones())

View File

@@ -43,10 +43,10 @@ impl BytesBase64 {
v.push(0); v.push(0);
} }
let mut result = vec![ let mut result = vec![
(v[0] & 0b11111100) >> 2, (v[0] & 0b1111_1100) >> 2,
(v[0] & 0b00000011) << 4 | (v[1] & 0b11110000) >> 4, (v[0] & 0b0000_0011) << 4 | (v[1] & 0b1111_0000) >> 4,
(v[1] & 0b00001111) << 2 | (v[2] & 0b11000000) >> 6, (v[1] & 0b0000_1111) << 2 | (v[2] & 0b1100_0000) >> 6,
(v[2] & 0b00111111), (v[2] & 0b0011_1111),
]; ];
// removed padded bytes // removed padded bytes
for _ in c.len()..3 { for _ in c.len()..3 {
@@ -58,7 +58,6 @@ impl BytesBase64 {
} }
pub fn to_bytes(&self) -> Bytes { pub fn to_bytes(&self) -> Bytes {
let BytesBase64(v) = self;
fn to_bytes(c: &[u8]) -> Vec<u8> { fn to_bytes(c: &[u8]) -> Vec<u8> {
let mut v = c.to_vec(); let mut v = c.to_vec();
// pad with bytes for conversion // pad with bytes for conversion
@@ -66,9 +65,9 @@ impl BytesBase64 {
v.push(0); v.push(0);
} }
let mut result: Vec<u8> = vec![ let mut result: Vec<u8> = vec![
((v[0] & 0b00111111) << 2) | ((v[1] & 0b00110000) >> 4), ((v[0] & 0b0011_1111) << 2) | ((v[1] & 0b0011_0000) >> 4),
((v[1] & 0b00001111) << 4) | ((v[2] & 0b00111100) >> 2), ((v[1] & 0b0000_1111) << 4) | ((v[2] & 0b0011_1100) >> 2),
((v[2] & 0b00000011) << 6) | (v[3] & 0b00111111), ((v[2] & 0b0000_0011) << 6) | (v[3] & 0b0011_1111),
]; ];
// removed padded bytes // removed padded bytes
for _ in c.len()..4 { for _ in c.len()..4 {
@@ -76,6 +75,7 @@ impl BytesBase64 {
} }
result result
} }
let BytesBase64(v) = self;
Bytes(v.chunks(4).flat_map(to_bytes).collect()) Bytes(v.chunks(4).flat_map(to_bytes).collect())
} }
@@ -88,9 +88,7 @@ impl BytesBase64 {
'0'..='9' => r.push((c as u8) - b'0' + 52), '0'..='9' => r.push((c as u8) - b'0' + 52),
'+' => r.push(62), '+' => r.push(62),
'/' => r.push(63), '/' => r.push(63),
'\n' => (), '\n' | '=' | ' ' => (),
'=' => (),
' ' => (),
_ => { _ => {
return Err(format!("Unexpected character '{}'", c)); return Err(format!("Unexpected character '{}'", c));
} }

View File

@@ -27,11 +27,11 @@ pub fn decrypt_aes_128_ecb_block(key: &[u8], data: &[u8]) -> Vec<u8> {
pub fn encrypt(Bytes(key): &Bytes, Bytes(iv): &Bytes, Bytes(data): &Bytes) -> Bytes { pub fn encrypt(Bytes(key): &Bytes, Bytes(iv): &Bytes, Bytes(data): &Bytes) -> Bytes {
let block_size = 16; let block_size = 16;
let mut result: Vec<u8> = vec![]; let mut result: Vec<u8> = vec![];
let mut prev_cipher: Vec<u8> = iv.to_vec(); // first xor input is IV let mut prev_cipher: Vec<u8> = iv.clone(); // first xor input is IV
for data in data.chunks(block_size) { for data in data.chunks(block_size) {
let xored = crate::utils::xor(&prev_cipher, data); let xored = crate::utils::xor(&prev_cipher, data);
let mut cipher = enrypt_aes_128_ecb_block(key, &xored); let mut cipher = enrypt_aes_128_ecb_block(key, &xored);
prev_cipher = cipher.to_vec(); // cipher is input for next xor prev_cipher = cipher.clone(); // cipher is input for next xor
result.append(&mut cipher); result.append(&mut cipher);
} }
Bytes(result) Bytes(result)
@@ -42,11 +42,11 @@ pub fn decrypt(Bytes(key): &Bytes, Bytes(iv): &Bytes, Bytes(data): &Bytes) -> By
let cipher_type = symm::Cipher::aes_128_ecb(); let cipher_type = symm::Cipher::aes_128_ecb();
let block_size = cipher_type.block_size(); let block_size = cipher_type.block_size();
let mut result: Vec<u8> = vec![]; let mut result: Vec<u8> = vec![];
let mut prev_cipher: Vec<u8> = iv.to_vec(); // first xor input is IV let mut prev_cipher: Vec<u8> = iv.clone(); // first xor input is IV
for cipher in data.chunks(block_size) { for cipher in data.chunks(block_size) {
let xored = decrypt_aes_128_ecb_block(key, cipher); let xored = decrypt_aes_128_ecb_block(key, cipher);
let data = crate::utils::xor(&xored, &prev_cipher); let data = crate::utils::xor(&xored, &prev_cipher);
result.extend(data.to_vec()); result.extend(data.clone());
prev_cipher = cipher.to_vec(); prev_cipher = cipher.to_vec();
} }
Bytes(result) Bytes(result)

View File

@@ -47,8 +47,6 @@ fn main() {
set4::challenge28(); set4::challenge28();
set4::challenge29(); set4::challenge29();
set4::challenge30(); set4::challenge30();
set4::challenge31();
} else {
set4::challenge31();
} }
set4::challenge31();
} }

View File

@@ -30,8 +30,7 @@ impl Md4Core {
while (buffer.len() + padding.len()) % BLOCK_BYTES != (BLOCK_BYTES - 8) { while (buffer.len() + padding.len()) % BLOCK_BYTES != (BLOCK_BYTES - 8) {
padding.push(0x0); padding.push(0x0);
} }
padding.append(&mut (bit_len as u32).to_le_bytes().to_vec()); padding.append(&mut bit_len.to_le_bytes().to_vec());
padding.append(&mut ((bit_len >> 32) as u32).to_le_bytes().to_vec());
padding padding
} }
@@ -141,8 +140,8 @@ pub fn hash(bytes: &Bytes) -> Bytes {
pub fn authenticate(message: &Bytes, key: &Bytes) -> Bytes { pub fn authenticate(message: &Bytes, key: &Bytes) -> Bytes {
let mut c = vec![]; let mut c = vec![];
c.append(&mut key.0.to_vec()); c.append(&mut key.0.clone());
c.append(&mut message.0.to_vec()); c.append(&mut message.0.clone());
hash(&Bytes(c)) hash(&Bytes(c))
} }

View File

@@ -24,7 +24,7 @@ impl MT19937 {
} }
pub fn seed(&mut self, seed: u32) { pub fn seed(&mut self, seed: u32) {
const F: u32 = 1812433253; const F: u32 = 1_812_433_253;
self.mt[0] = seed; self.mt[0] = seed;
for i in 1..N { for i in 1..N {
self.mt[i] = (Wrapping(F) * Wrapping(self.mt[i - 1] ^ (self.mt[i - 1] >> 30)) self.mt[i] = (Wrapping(F) * Wrapping(self.mt[i - 1] ^ (self.mt[i - 1] >> 30))
@@ -49,9 +49,9 @@ impl MT19937 {
const S: u32 = 7; const S: u32 = 7;
const T: u32 = 15; const T: u32 = 15;
const U: u32 = 11; const U: u32 = 11;
const B: u32 = 0x9D2C5680; const B: u32 = 0x9D2C_5680;
const C: u32 = 0xEFC60000; const C: u32 = 0xEFC6_0000;
const D: u32 = 0xFFFFFFFF; const D: u32 = 0xFFFF_FFFF;
const L: u32 = 18; const L: u32 = 18;
let mut y = self.mt[self.index]; let mut y = self.mt[self.index];

View File

@@ -11,7 +11,7 @@ pub fn encrypt(key: u16, Bytes(data): &Bytes) -> Bytes {
// Write the function that does this for MT19937 using a 16-bit seed. // Write the function that does this for MT19937 using a 16-bit seed.
let mut mt = mt19937::MT19937::new(); let mut mt = mt19937::MT19937::new();
mt.seed(key as u32); mt.seed(key.into());
let mut result: Vec<u8> = vec![]; let mut result: Vec<u8> = vec![];
for chunk in data.chunks(4) { for chunk in data.chunks(4) {

View File

@@ -33,7 +33,7 @@ fn scan(code: &str, mut ix: usize, mut tokens: Tokens) -> Tokens {
return tokens; return tokens;
} }
let c: char = code[ix..ix + 1].chars().next().unwrap(); let c: char = code[ix..].chars().next().unwrap();
if c.is_ascii_alphanumeric() || SPECIAL_CHARS.contains(&c) { if c.is_ascii_alphanumeric() || SPECIAL_CHARS.contains(&c) {
return scan_identifier(code, ix, tokens); return scan_identifier(code, ix, tokens);
} }

View File

@@ -60,7 +60,7 @@ pub fn challenge5() {
if enc == exp { if enc == exp {
println!("[okay] Challenge 5: {}", enc.to_hex()); println!("[okay] Challenge 5: {}", enc.to_hex());
} else { } else {
println!("[fail] Challenge 5") println!("[fail] Challenge 5");
} }
} }
@@ -72,7 +72,7 @@ pub fn challenge6() {
for e in Iterator::zip(bytes.as_bytes().iter(), 0..bytes.len()) { for e in Iterator::zip(bytes.as_bytes().iter(), 0..bytes.len()) {
if e.1 % width == 0 && e.1 > 0 { if e.1 % width == 0 && e.1 > 0 {
output_vec.push(b'\n') output_vec.push(b'\n');
} }
output_vec.push(*e.0); output_vec.push(*e.0);
} }
@@ -167,13 +167,8 @@ pub fn challenge8() {
let index = ratings.iter().position(|e| e == min_rating).unwrap(); let index = ratings.iter().position(|e| e == min_rating).unwrap();
let average = ratings.iter().sum::<u32>() as f32 / ratings.len() as f32; let average = ratings.iter().sum::<u32>() as f32 / ratings.len() as f32;
let partial_cipher = bytes_vector[index].to_hex()[..10].to_string(); let partial_cipher = bytes_vector[index].to_hex()[..10].to_string();
if index != expected_index { assert!(index == expected_index, "Regression in challenge 8.");
panic!("Regression in challenge 8."); println!("[okay] Challenge 8: Cipher {index} [{partial_cipher}...] with rating {min_rating} (average = {average}) is the solution.");
}
println!(
"[okay] Challenge 8: Cipher {} [{}...] with rating {} (average = {}) is the solution.",
index, partial_cipher, min_rating, average
);
// More elegant solution. // More elegant solution.
let bytes_vector = utils::read_hex_lines("data/8.txt"); let bytes_vector = utils::read_hex_lines("data/8.txt");

View File

@@ -50,7 +50,7 @@ pub fn challenge11() {
let key = Bytes::random(16); let key = Bytes::random(16);
// Under the hood, have the function append 5-10 bytes (count chosen randomly) // Under the hood, have the function append 5-10 bytes (count chosen randomly)
// before the plaintext and 5-10 bytes after the plaintext. // before the plaintext and 5-10 bytes after the plaintext.
let padded_data = pad_data(data.to_vec()); let padded_data = pad_data(data.clone());
// Now, have the function choose to encrypt under ECB 1/2 the time, and under CBC // Now, have the function choose to encrypt under ECB 1/2 the time, and under CBC
// the other half (just use random IVs each time for CBC). Use rand(2) to decide // the other half (just use random IVs each time for CBC). Use rand(2) to decide
// which to use. // which to use.
@@ -103,7 +103,7 @@ pub fn challenge12() {
fn encryption_oracle(key: &Bytes, Bytes(data): &Bytes) -> Bytes { fn encryption_oracle(key: &Bytes, Bytes(data): &Bytes) -> Bytes {
// Copy your oracle function to a new function that encrypts buffers under ECB mode using a consistent but unknown key // Copy your oracle function to a new function that encrypts buffers under ECB mode using a consistent but unknown key
// Now take that same function and have it append to the plaintext, BEFORE ENCRYPTING, the following string (from 12.txt): // Now take that same function and have it append to the plaintext, BEFORE ENCRYPTING, the following string (from 12.txt):
let mut data = data.to_vec(); let mut data = data.clone();
let mut string = utils::read_base64("data/12.txt"); let mut string = utils::read_base64("data/12.txt");
data.append(&mut string.0); data.append(&mut string.0);
ecb::encrypt(key, &Bytes(data)) ecb::encrypt(key, &Bytes(data))
@@ -112,11 +112,11 @@ pub fn challenge12() {
fn get_block_size(key: &Bytes) -> usize { fn get_block_size(key: &Bytes) -> usize {
// Detect cipher block size // Detect cipher block size
let mut v = vec![]; let mut v = vec![];
let initial_cipher_len = encryption_oracle(key, &Bytes(v.to_vec())).0.len(); let initial_cipher_len = encryption_oracle(key, &Bytes(v.clone())).0.len();
let mut new_cipher_len = initial_cipher_len; let mut new_cipher_len = initial_cipher_len;
while initial_cipher_len == new_cipher_len { while initial_cipher_len == new_cipher_len {
v.push(b'A'); v.push(b'A');
let cipher = encryption_oracle(key, &Bytes(v.to_vec())); let cipher = encryption_oracle(key, &Bytes(v.clone()));
new_cipher_len = cipher.0.len(); new_cipher_len = cipher.0.len();
} }
new_cipher_len - initial_cipher_len new_cipher_len - initial_cipher_len
@@ -137,20 +137,20 @@ pub fn challenge12() {
let mut cleartext_block = vec![]; let mut cleartext_block = vec![];
for padding_length in (0..block_size).rev() { for padding_length in (0..block_size).rev() {
let padding_text = vec![b'-'; padding_length]; let padding_text = vec![b'-'; padding_length];
let expected = encryption_oracle(key, &Bytes(padding_text.to_vec())); let expected = encryption_oracle(key, &Bytes(padding_text.clone()));
let expected_block = expected.get_block(block_index, block_size); let expected_block = expected.get_block(block_index, block_size);
let mut known_text = if block_index == 0 { let mut known_text = if block_index == 0 {
padding_text.to_vec() padding_text.clone()
} else { } else {
let cleartext_offset = let cleartext_offset =
((block_index - 1) * block_size) + (block_size - padding_length); ((block_index - 1) * block_size) + (block_size - padding_length);
cleartext[cleartext_offset..(cleartext_offset + padding_length)].to_vec() cleartext[cleartext_offset..(cleartext_offset + padding_length)].to_vec()
}; };
known_text.append(&mut cleartext_block.to_vec()); known_text.append(&mut cleartext_block.clone());
for i in 0..255 { for i in 0..255 {
let mut guess_text = known_text.to_vec(); let mut guess_text = known_text.clone();
guess_text.push(i); guess_text.push(i);
let cipher_block = let cipher_block =
encryption_oracle(key, &Bytes(guess_text)).get_block(0, block_size); encryption_oracle(key, &Bytes(guess_text)).get_block(0, block_size);
@@ -183,9 +183,11 @@ pub fn challenge13() {
fn profile_for(input: &str, key: &Bytes) -> Bytes { fn profile_for(input: &str, key: &Bytes) -> Bytes {
let mut r = String::new(); let mut r = String::new();
for c in input.chars() { for c in input.chars() {
if !(c.is_ascii_alphabetic() || c == '.' || c == '@') { assert!(
panic!("profile_for: invalid char {}", c); c.is_ascii_alphabetic() || c == '.' || c == '@',
} "profile_for: invalid char {}",
c
);
} }
r.push_str("email="); r.push_str("email=");
r.push_str(input); r.push_str(input);
@@ -246,8 +248,8 @@ pub fn challenge14() {
// know how to decode it because I cannot really run experiments. If I generate // know how to decode it because I cannot really run experiments. If I generate
// a single random prefix it becomes rather trivial. I just have to find out how // a single random prefix it becomes rather trivial. I just have to find out how
// long it is and then adjust the decoding routine. // long it is and then adjust the decoding routine.
let mut plaintext = random_prefix.to_vec(); let mut plaintext = random_prefix.clone();
plaintext.append(&mut attacker_controlled.to_vec()); plaintext.append(&mut attacker_controlled.clone());
let mut target_bytes = utils::read_base64("data/12.txt").0; let mut target_bytes = utils::read_base64("data/12.txt").0;
plaintext.append(&mut target_bytes); plaintext.append(&mut target_bytes);
ecb::encrypt(random_key, &Bytes(plaintext)) ecb::encrypt(random_key, &Bytes(plaintext))
@@ -285,7 +287,7 @@ pub fn challenge14() {
for i in 0..block_size { for i in 0..block_size {
let mut padding = vec![b'a'; i]; let mut padding = vec![b'a'; i];
padding.append(&mut duplicated_text.to_vec()); padding.append(&mut duplicated_text.clone());
let cipher = encryption_oracle(prefix, key, &Bytes(padding)); let cipher = encryption_oracle(prefix, key, &Bytes(padding));
if let Some((first_block, _)) = get_duplicated_block_indices(&cipher, block_size) { if let Some((first_block, _)) = get_duplicated_block_indices(&cipher, block_size) {
return block_size * first_block - i; return block_size * first_block - i;
@@ -313,11 +315,11 @@ pub fn challenge14() {
let mut cleartext_block = vec![]; let mut cleartext_block = vec![];
for padding_length in (0..block_size).rev() { for padding_length in (0..block_size).rev() {
let full_padding_text = vec![b'-'; prefix_padding_size + padding_length]; let full_padding_text = vec![b'-'; prefix_padding_size + padding_length];
let expected = encryption_oracle(prefix, key, &Bytes(full_padding_text.to_vec())); let expected = encryption_oracle(prefix, key, &Bytes(full_padding_text.clone()));
let expected_block = expected.get_block(block_index, block_size); let expected_block = expected.get_block(block_index, block_size);
let mut known_text = if block_index == first_block_index { let mut known_text = if block_index == first_block_index {
full_padding_text.to_vec() full_padding_text.clone()
} else { } else {
let mut prefix_padding = vec![b'-'; prefix_padding_size]; let mut prefix_padding = vec![b'-'; prefix_padding_size];
let cleartext_offset = ((block_index - first_block_index - 1) * block_size) let cleartext_offset = ((block_index - first_block_index - 1) * block_size)
@@ -328,10 +330,10 @@ pub fn challenge14() {
); );
prefix_padding prefix_padding
}; };
known_text.append(&mut cleartext_block.to_vec()); known_text.append(&mut cleartext_block.clone());
for i in 0..255 { for i in 0..255 {
let mut guess_text = known_text.to_vec(); let mut guess_text = known_text.clone();
guess_text.push(i); guess_text.push(i);
let cipher_block = encryption_oracle(prefix, key, &Bytes(guess_text)) let cipher_block = encryption_oracle(prefix, key, &Bytes(guess_text))
.get_block(first_block_index, block_size); .get_block(first_block_index, block_size);
@@ -373,9 +375,7 @@ pub fn challenge16() {
fn encrypt(input: &str, key: &Bytes, iv: &Bytes) -> Bytes { fn encrypt(input: &str, key: &Bytes, iv: &Bytes) -> Bytes {
let mut r = String::new(); let mut r = String::new();
for c in input.chars() { for c in input.chars() {
if c == ';' || c == '=' { assert!(c != ';' && c != '=', "encrypt: invalid char {}", c);
panic!("encrypt: invalid char {}", c);
}
} }
r.push_str("comment1=cooking%20MCs;userdata="); r.push_str("comment1=cooking%20MCs;userdata=");
r.push_str(input); r.push_str(input);

View File

@@ -16,7 +16,7 @@ pub fn challenge17() {
// The first function should select at random one of the ten strings // The first function should select at random one of the ten strings
let cleartexts = utils::read_base64_lines("data/17.txt"); let cleartexts = utils::read_base64_lines("data/17.txt");
let index: usize = rand::thread_rng().gen_range(0..cleartexts.len()); let index: usize = rand::thread_rng().gen_range(0..cleartexts.len());
let mut cleartext = Bytes(cleartexts[index].0.to_vec()); let mut cleartext = Bytes(cleartexts[index].0.clone());
// pad the string out to the 16-byte AES block size and // pad the string out to the 16-byte AES block size and
cleartext.pad_pkcs7(16); cleartext.pad_pkcs7(16);
@@ -39,22 +39,29 @@ pub fn challenge17() {
let attack_block = |previous_block: &Bytes, cipher_block: &Bytes| -> Bytes { let attack_block = |previous_block: &Bytes, cipher_block: &Bytes| -> Bytes {
// Good explanation: https://robertheaton.com/2013/07/29/padding-oracle-attack/ // Good explanation: https://robertheaton.com/2013/07/29/padding-oracle-attack/
let block_size = cipher_block.len(); let block_size: u8 = cipher_block
let mut attack_vector = Bytes::random(block_size); .len()
.try_into()
.expect("block size should be less than 255");
let mut attack_vector = Bytes::random(block_size.into());
let mut intermittent_result = vec![]; let mut intermittent_result = vec![];
for pad_byte in 1..=block_size { for pad_byte in 1_u8..=block_size {
// preset attack vector so that paddinig is [1], [2, 2], [3, 3, 3], and so on. // preset attack vector so that paddinig is [1], [2, 2], [3, 3, 3], and so on.
attack_vector.0[block_size - pad_byte] = pad_byte as u8; let pad_byte_index: usize = (block_size - pad_byte).into();
for (i, intermittent_byte) in intermittent_result.iter().enumerate().take(pad_byte - 1) attack_vector.0[pad_byte_index] = pad_byte;
for (i, intermittent_byte) in intermittent_result
.iter()
.enumerate()
.take(pad_byte as usize - 1)
{ {
attack_vector.0[block_size - 1 - i] = (pad_byte as u8) ^ intermittent_byte; attack_vector.0[block_size as usize - 1 - i] = (pad_byte as u8) ^ intermittent_byte;
} }
// guess attack vector so that padding is valid // guess attack vector so that padding is valid
let guess_index = block_size - pad_byte; let guess_index = block_size - pad_byte;
for guess in 0..=255 { for guess in 0..=255 {
attack_vector.0[guess_index] = guess; attack_vector.0[guess_index as usize] = guess;
if decryption_oracle(&attack_vector, cipher_block) { if decryption_oracle(&attack_vector, cipher_block) {
// println!("{guess:#016b}"); // println!("{guess:#016b}");
let c = (guess as u8) ^ (pad_byte as u8); let c = (guess as u8) ^ (pad_byte as u8);
@@ -68,7 +75,7 @@ pub fn challenge17() {
let xored: Vec<u8> = Iterator::zip(previous_block.0.iter(), intermittent_result) let xored: Vec<u8> = Iterator::zip(previous_block.0.iter(), intermittent_result)
.map(|z| z.0 ^ z.1) .map(|z| z.0 ^ z.1)
.collect(); .collect();
assert_eq!(xored.len(), block_size); assert_eq!(xored.len(), block_size.into());
Bytes(xored) Bytes(xored)
}; };
@@ -88,7 +95,7 @@ pub fn challenge17() {
} }
roundtrip.remove_pkcs7(16); roundtrip.remove_pkcs7(16);
let cleartexts = utils::read_base64_lines("data/17.txt"); let cleartexts = utils::read_base64_lines("data/17.txt");
let cleartext = Bytes(cleartexts[cleartext_index].0.to_vec()); let cleartext = Bytes(cleartexts[cleartext_index].0.clone());
assert_eq!(roundtrip, cleartext); assert_eq!(roundtrip, cleartext);
println!("[okay] Challenge 17: {}", roundtrip.to_utf8()); println!("[okay] Challenge 17: {}", roundtrip.to_utf8());
} }
@@ -96,9 +103,10 @@ pub fn challenge17() {
pub fn challenge18() { pub fn challenge18() {
let key = Bytes::from_utf8("YELLOW SUBMARINE"); let key = Bytes::from_utf8("YELLOW SUBMARINE");
let nonce = 1337;
let cleartext = Bytes::from_utf8("Let's see if we can get the party started hard my friends."); let cleartext = Bytes::from_utf8("Let's see if we can get the party started hard my friends.");
let cipher = ctr::encrypt(&key, 42351234, &cleartext); let cipher = ctr::encrypt(&key, nonce, &cleartext);
let roundtrip = ctr::decrypt(&key, 42351234, &cipher); let roundtrip = ctr::decrypt(&key, nonce, &cipher);
assert_eq!(cleartext, roundtrip); assert_eq!(cleartext, roundtrip);
let cipher = BytesBase64::from_base64( let cipher = BytesBase64::from_base64(
@@ -110,7 +118,7 @@ pub fn challenge18() {
println!("[okay] Challenge 18: {cleartext}"); println!("[okay] Challenge 18: {cleartext}");
} }
pub fn challenge19() { fn challenge19_attack(ciphers: &[Bytes]) -> Vec<RefCell<Vec<u8>>> {
fn xor_to_char_set(letters: &Vec<u8>) -> HashMap<u8, RefCell<HashSet<u8>>> { fn xor_to_char_set(letters: &Vec<u8>) -> HashMap<u8, RefCell<HashSet<u8>>> {
let mut h = HashMap::new(); let mut h = HashMap::new();
for i in 0..255_u8 { for i in 0..255_u8 {
@@ -144,83 +152,83 @@ pub fn challenge19() {
letters letters
} }
fn attack(ciphers: &[Bytes]) -> Vec<RefCell<Vec<u8>>> { let ciphers_len = ciphers.len();
let ciphers_len = ciphers.len(); let deciphered = vec![RefCell::new(vec![]); ciphers_len];
let deciphered = vec![RefCell::new(vec![]); ciphers_len]; let max_cipher_len = ciphers.iter().map(Bytes::len).max().unwrap_or(0);
let max_cipher_len = ciphers.iter().map(Bytes::len).max().unwrap_or(0);
for byte_index in 0..max_cipher_len { for byte_index in 0..max_cipher_len {
let letters = match byte_index { let letters = match byte_index {
// chars that work for 10 and 20 found via trial and error // chars that work for 10 and 20 found via trial and error
10 => ascii_letters(" _-.,;:'"), 10 => ascii_letters(" _-.,;:'"),
20 => ascii_letters(" _-.,;:?"), 20 => ascii_letters(" _-.,;:?"),
_ => ascii_letters(" _-.,;:"), _ => ascii_letters(" _-.,;:"),
}; };
let lookup = xor_to_char_set(&letters); let lookup = xor_to_char_set(&letters);
let target_bytes: Vec<Option<u8>> = ciphers let target_bytes: Vec<Option<u8>> = ciphers
.iter() .iter()
.map(|c| { .map(|c| {
if c.len() > byte_index { if c.len() > byte_index {
Some(c.0[byte_index]) Some(c.0[byte_index])
} else { } else {
None None
}
})
.collect();
let mut possible_chars: Vec<HashSet<u8>> = ciphers
.iter()
.map(|_| letters.iter().cloned().collect())
.collect();
for i in 0..ciphers_len {
for j in i..ciphers_len {
if target_bytes[i] == None || target_bytes[j] == None {
continue;
}
let xored = target_bytes[i].unwrap() ^ target_bytes[j].unwrap();
let chars = lookup.get(&xored).unwrap().borrow();
possible_chars[i] = possible_chars[i].intersection(&chars).copied().collect();
possible_chars[j] = possible_chars[j].intersection(&chars).copied().collect();
} }
} })
.collect();
let mut possible_chars: Vec<HashSet<u8>> = ciphers
.iter()
.map(|_| letters.iter().copied().collect())
.collect();
for cipher_index in 0..ciphers_len { for i in 0..ciphers_len {
if ciphers[cipher_index].len() <= byte_index { for j in i..ciphers_len {
if target_bytes[i] == None || target_bytes[j] == None {
continue; continue;
} }
let chars: Vec<u8> = possible_chars[cipher_index].iter().cloned().collect(); let xored = target_bytes[i].unwrap() ^ target_bytes[j].unwrap();
match chars.len() { let chars = lookup.get(&xored).unwrap().borrow();
0 => { possible_chars[i] = possible_chars[i].intersection(&chars).copied().collect();
// println!("No chars for {cipher_index} {byte_index}"); possible_chars[j] = possible_chars[j].intersection(&chars).copied().collect();
deciphered[cipher_index].borrow_mut().push(b'?'); }
} }
1 => {
for cipher_index in 0..ciphers_len {
if ciphers[cipher_index].len() <= byte_index {
continue;
}
let chars: Vec<u8> = possible_chars[cipher_index].iter().copied().collect();
match chars.len() {
0 => {
// println!("No chars for {cipher_index} {byte_index}");
deciphered[cipher_index].borrow_mut().push(b'?');
}
1 => {
deciphered[cipher_index]
.borrow_mut()
.push(u8_lower(chars[0]));
}
2 => {
if u8_lower(chars[0]) == u8_lower(chars[1]) {
deciphered[cipher_index] deciphered[cipher_index]
.borrow_mut() .borrow_mut()
.push(u8_lower(chars[0])); .push(u8_lower(chars[0]));
} } else {
2 => {
if u8_lower(chars[0]) == u8_lower(chars[1]) {
deciphered[cipher_index]
.borrow_mut()
.push(u8_lower(chars[0]));
} else {
// println!("Two {chars:?} {cipher_index} {byte_index}");
deciphered[cipher_index].borrow_mut().push(b'^');
}
}
_ => {
// println!("Two {chars:?} {cipher_index} {byte_index}"); // println!("Two {chars:?} {cipher_index} {byte_index}");
deciphered[cipher_index].borrow_mut().push(b'^'); deciphered[cipher_index].borrow_mut().push(b'^');
} }
} }
_ => {
// println!("Two {chars:?} {cipher_index} {byte_index}");
deciphered[cipher_index].borrow_mut().push(b'^');
}
} }
} }
deciphered
} }
deciphered
}
pub fn challenge19() {
fn manual(decrypts: &[RefCell<Vec<u8>>]) { fn manual(decrypts: &[RefCell<Vec<u8>>]) {
// Add manually guessed letters // Add manually guessed letters
decrypts[0].borrow_mut()[30] = b'y'; decrypts[0].borrow_mut()[30] = b'y';
@@ -244,15 +252,15 @@ pub fn challenge19() {
let key = Bytes::from_utf8("YELLOW SUBMARINE"); let key = Bytes::from_utf8("YELLOW SUBMARINE");
let encrypt = |plaintext: &Bytes| -> Bytes { ctr::encrypt(&key, 0, plaintext) }; let encrypt = |plaintext: &Bytes| -> Bytes { ctr::encrypt(&key, 0, plaintext) };
let ciphers: Vec<Bytes> = plaintexts.iter().map(encrypt).collect(); let ciphers: Vec<Bytes> = plaintexts.iter().map(encrypt).collect();
let decrypts = attack(&ciphers); let decrypts = challenge19_attack(&ciphers);
manual(&decrypts); manual(&decrypts);
let first_line = Bytes(decrypts[0].borrow().to_vec()).to_utf8(); let first_line = Bytes(decrypts[0].borrow().to_vec()).to_utf8();
println!("[okay] Challenge 19: {first_line}"); println!("[okay] Challenge 19: {first_line}");
} }
pub fn challenge20() { pub fn challenge20() {
fn attack(ciphers: Vec<Bytes>) -> Vec<Bytes> { fn attack(ciphers: &[Bytes]) -> Vec<Bytes> {
let min_cipher_len = ciphers.iter().map(|c| c.len()).min().unwrap_or(0); let min_cipher_len = ciphers.iter().map(Bytes::len).min().unwrap_or(0);
let mut key: Vec<u8> = vec![]; let mut key: Vec<u8> = vec![];
for byte_index in 0..min_cipher_len { for byte_index in 0..min_cipher_len {
let bytes = Bytes(ciphers.iter().map(|c| c.0[byte_index]).collect()); let bytes = Bytes(ciphers.iter().map(|c| c.0[byte_index]).collect());
@@ -270,7 +278,7 @@ pub fn challenge20() {
let key = Bytes::from_utf8("YELLOW SUBMARINE"); let key = Bytes::from_utf8("YELLOW SUBMARINE");
let encrypt = |plaintext: &Bytes| -> Bytes { ctr::encrypt(&key, 0, plaintext) }; let encrypt = |plaintext: &Bytes| -> Bytes { ctr::encrypt(&key, 0, plaintext) };
let ciphers: Vec<Bytes> = plaintexts.iter().map(encrypt).collect(); let ciphers: Vec<Bytes> = plaintexts.iter().map(encrypt).collect();
let plaintexts = attack(ciphers); let plaintexts = attack(&ciphers);
println!("[okay] Challenge 20: {}", plaintexts[0].to_utf8()); println!("[okay] Challenge 20: {}", plaintexts[0].to_utf8());
} }
@@ -278,8 +286,16 @@ pub fn challenge20() {
pub fn challenge21() { pub fn challenge21() {
// Implement the MT19937 Mersenne Twister RNG // Implement the MT19937 Mersenne Twister RNG
let expected: Vec<u32> = vec![ let expected: Vec<u32> = vec![
0xD091BB5C, 0x22AE9EF6, 0xE7E1FAEE, 0xD5C31F79, 0x2082352C, 0xF807B7DF, 0xE9D30005, 0xD091_BB5C,
0x3895AFE1, 0xA1E24BBA, 0x4EE4092B, 0x22AE_9EF6,
0xE7E1_FAEE,
0xD5C3_1F79,
0x2082_352C,
0xF807_B7DF,
0xE9D3_0005,
0x3895_AFE1,
0xA1E2_4BBA,
0x4EE4_092B,
]; ];
let mut mt = mt19937::MT19937::new(); let mut mt = mt19937::MT19937::new();
mt.seed(5489); mt.seed(5489);
@@ -326,8 +342,8 @@ pub fn challenge23() {
const S: u32 = 7; const S: u32 = 7;
const T: u32 = 15; const T: u32 = 15;
const U: u32 = 11; const U: u32 = 11;
const B: u32 = 0x9D2C5680; const B: u32 = 0x9D2C_5680;
const C: u32 = 0xEFC60000; const C: u32 = 0xEFC6_0000;
const L: u32 = 18; const L: u32 = 18;
let mut y = x; let mut y = x;
@@ -339,8 +355,8 @@ pub fn challenge23() {
} }
fn untemper(x: u32) -> u32 { fn untemper(x: u32) -> u32 {
const B: u32 = 0x9D2C5680; const B: u32 = 0x9D2C_5680;
const C: u32 = 0xEFC60000; const C: u32 = 0xEFC6_0000;
let mut y = x; let mut y = x;
// reverse y = y ^ (y >> L); L = 18; // reverse y = y ^ (y >> L); L = 18;

View File

@@ -203,21 +203,21 @@ pub fn challenge29() {
for key_len in 1..128 { for key_len in 1..128 {
// get padding for key || orig-message // get padding for key || orig-message
let key_guessed = vec![b'z'; key_len]; // key-guessed let key_guessed = vec![b'z'; key_len]; // key-guessed
let mut bytes = key_guessed.to_vec(); let mut bytes = key_guessed.clone();
bytes.append(&mut message.0.to_vec()); // original-message bytes.append(&mut message.0.clone()); // original-message
let s1 = sha1::Sha1::default(); let s1 = sha1::Sha1::default();
let glue_padding = s1.get_padding(&bytes); // glue-padding let glue_padding = s1.get_padding(&bytes); // glue-padding
// forget MAC via fixture: make sure to fix sha1.h *and* sha1.byte_length // forget MAC via fixture: make sure to fix sha1.h *and* sha1.byte_length
let byte_length = (key_guessed.len() + message.len() + glue_padding.len()) as u64; let byte_length = (key_guessed.len() + message.len() + glue_padding.len()) as u64;
let new_message = b"admin=true".to_vec(); // new-message let new_message = b"admin=true".to_vec(); // new-message
mac_forged = hash_fixated(&Bytes(new_message.to_vec()), &mac, byte_length); mac_forged = hash_fixated(&Bytes(new_message.clone()), &mac, byte_length);
// forge message: original-message || glue-padding || new-message // forge message: original-message || glue-padding || new-message
forged_message = message.0.to_vec(); forged_message = message.0.clone();
forged_message.append(&mut glue_padding.clone()); forged_message.append(&mut glue_padding.clone());
forged_message.append(&mut new_message.clone()); forged_message.append(&mut new_message.clone());
let r = sha1::verify(&Bytes(forged_message.to_vec()), &key, &mac_forged); let r = sha1::verify(&Bytes(forged_message.clone()), &key, &mac_forged);
if r { if r {
break; break;
} }
@@ -274,20 +274,20 @@ pub fn challenge30() {
for key_len in 1..128 { for key_len in 1..128 {
// get padding for key || orig-message // get padding for key || orig-message
let key_guessed = vec![b'z'; key_len]; // key-guessed let key_guessed = vec![b'z'; key_len]; // key-guessed
let mut bytes = key_guessed.to_vec(); let mut bytes = key_guessed.clone();
bytes.append(&mut message.0.to_vec()); // original-message bytes.append(&mut message.0.clone()); // original-message
let md4 = md4::Md4Core::default(); let md4 = md4::Md4Core::default();
let glue_padding = md4.get_padding(&bytes); // glue-padding let glue_padding = md4.get_padding(&bytes); // glue-padding
// forget MAC via fixture: make sure to fix md4.state *and* md4.byte_length // forget MAC via fixture: make sure to fix md4.state *and* md4.byte_length
let byte_length = (key_guessed.len() + message.len() + glue_padding.len()) as u64; let byte_length = (key_guessed.len() + message.len() + glue_padding.len()) as u64;
let new_message = b"admin=true".to_vec(); // new-message let new_message = b"admin=true".to_vec(); // new-message
mac_forged = hash_fixated(&Bytes(new_message.to_vec()), &mac, byte_length); mac_forged = hash_fixated(&Bytes(new_message.clone()), &mac, byte_length);
// forge message: original-message || glue-padding || new-message // forge message: original-message || glue-padding || new-message
forged_message = message.0.to_vec(); forged_message = message.0.clone();
forged_message.append(&mut glue_padding.to_vec()); forged_message.append(&mut glue_padding.clone());
forged_message.append(&mut new_message.to_vec()); forged_message.append(&mut new_message.clone());
let r = md4::verify(&Bytes(forged_message.clone()), &key, &mac_forged); let r = md4::verify(&Bytes(forged_message.clone()), &key, &mac_forged);
if r { if r {
break; break;

View File

@@ -101,7 +101,7 @@ fn r4(block: &mut Block, v: u32, w: &mut u32, x: u32, y: u32, z: &mut u32, i: us
impl Sha1 { impl Sha1 {
#[inline] #[inline]
pub fn reset(&mut self) { pub fn reset(&mut self) {
*self = Default::default(); *self = Sha1::default();
} }
pub fn fix(&mut self, fixate: [u32; STATE_LEN], byte_len: u64) { pub fn fix(&mut self, fixate: [u32; STATE_LEN], byte_len: u64) {
@@ -227,8 +227,7 @@ impl Sha1 {
// append the original message length as a 64-bit big-endian integer // append the original message length as a 64-bit big-endian integer
let bit_len: u64 = (self.byte_len + bytes.len() as u64) as u64 * 8; let bit_len: u64 = (self.byte_len + bytes.len() as u64) as u64 * 8;
padding.append(&mut ((bit_len >> 32) as u32).to_be_bytes().to_vec()); padding.append(&mut bit_len.to_be_bytes().to_vec());
padding.append(&mut (bit_len as u32).to_be_bytes().to_vec());
padding padding
} }

View File

@@ -9,7 +9,10 @@ pub fn unix_timestamp() -> u32 {
let since_the_epoch = start let since_the_epoch = start
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
.expect("Time went backwards"); .expect("Time went backwards");
since_the_epoch.as_secs() as u32 since_the_epoch
.as_secs()
.try_into()
.expect("Linux time no longer fits into 32 bits.")
} }
pub fn read_base64(path: &str) -> Bytes { pub fn read_base64(path: &str) -> Bytes {