Refactor code based on clippy suggestions

This commit is contained in:
2022-08-25 17:45:16 -04:00
parent 9a9b5335f1
commit b97b2fe6d0
16 changed files with 145 additions and 204 deletions

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"rust-analyzer.checkOnSave.command": "clippy",
}

View File

@@ -11,13 +11,16 @@ impl Bytes {
} }
pub fn from_utf8(s: &str) -> Bytes { pub fn from_utf8(s: &str) -> Bytes {
Bytes(s.as_bytes().iter().map(|c| c.clone()).collect()) Bytes(s.as_bytes().to_vec())
} }
#[allow(dead_code)]
pub fn to_utf8(&self) -> String { pub fn to_utf8(&self) -> String {
let Bytes(v) = self; let Bytes(v) = self;
String::from(std::str::from_utf8(&v).unwrap()) String::from(std::str::from_utf8(v).unwrap())
}
pub fn to_sub_utf8(&self, length: usize) -> String {
Bytes(self.0[..length].to_vec()).to_utf8()
} }
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
@@ -69,7 +72,7 @@ impl Bytes {
pub fn is_ascii(&self) -> bool { pub fn is_ascii(&self) -> bool {
let Bytes(v) = self; let Bytes(v) = self;
for &c in v.iter() { for &c in v.iter() {
if c < 32 || c > 127 { if !(32..=127).contains(&c) {
return false; return false;
} }
} }
@@ -103,7 +106,7 @@ impl Bytes {
h.last().unwrap().1 h.last().unwrap().1
} }
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) as u8;
for _ in 0..padding_value { for _ in 0..padding_value {
@@ -118,7 +121,7 @@ impl Bytes {
let last_block_index = self.len() / block_size - 1; let last_block_index = self.len() / block_size - 1;
let last_block = self.get_block(last_block_index, block_size).0; let last_block = self.get_block(last_block_index, block_size).0;
let pad_byte = last_block[block_size - 1]; let pad_byte = last_block[block_size - 1];
if pad_byte < 1 || pad_byte > 16 { if !(1..=16).contains(&pad_byte) {
return false; return false;
} }
for i in 0..(pad_byte as usize) { for i in 0..(pad_byte as usize) {
@@ -127,10 +130,10 @@ impl Bytes {
return false; return false;
} }
} }
return true; true
} }
pub fn remove_pkcs7(&mut self, block_size: usize) -> () { pub fn remove_pkcs7(&mut self, block_size: usize) {
if !self.has_valid_pkcs7(block_size) { if !self.has_valid_pkcs7(block_size) {
return; return;
} }
@@ -141,7 +144,7 @@ impl Bytes {
} }
} }
pub fn flip_bit(&mut self, byte_index: usize, bit_index: usize) -> () { pub fn flip_bit(&mut self, byte_index: usize, bit_index: usize) {
let Bytes(v) = self; let Bytes(v) = self;
let flip_mask: u8 = 0b1 << bit_index; let flip_mask: u8 = 0b1 << bit_index;
v[byte_index] ^= flip_mask; v[byte_index] ^= flip_mask;

View File

@@ -6,7 +6,31 @@ pub struct BytesBase64(pub Vec<u8>);
impl std::fmt::Display for BytesBase64 { impl std::fmt::Display for BytesBase64 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Base64({})", self.to_string()) let BytesBase64(digits) = self;
let mut r: Vec<u8> = digits
.iter()
.map(|d| match d {
0..=25 => *d + b'A',
26..=51 => *d - 26 + b'a',
52..=61 => *d - 52 + b'0',
62 => b'+',
63 => b'/',
_ => panic!("Unexpected base64 digit '{}'", d),
})
.collect();
// Handle padding
let pad = b'=';
match r.len() % 4 {
0 => (),
2 => {
r.push(pad);
r.push(pad);
}
3 => r.push(pad),
_ => panic!("Unexpected lenght for padding '{}'", r.len()),
}
let s = str::from_utf8(r.as_slice()).unwrap().to_string();
write!(f, "{}", s)
} }
} }
@@ -22,7 +46,7 @@ impl BytesBase64 {
(v[0] & 0b11111100) >> 2, (v[0] & 0b11111100) >> 2,
(v[0] & 0b00000011) << 4 | (v[1] & 0b11110000) >> 4, (v[0] & 0b00000011) << 4 | (v[1] & 0b11110000) >> 4,
(v[1] & 0b00001111) << 2 | (v[2] & 0b11000000) >> 6, (v[1] & 0b00001111) << 2 | (v[2] & 0b11000000) >> 6,
(v[2] & 0b00111111) << 0, (v[2] & 0b00111111),
]; ];
// removed padded bytes // removed padded bytes
for _ in c.len()..3 { for _ in c.len()..3 {
@@ -30,7 +54,7 @@ impl BytesBase64 {
} }
result result
} }
BytesBase64(bytes.chunks(3).map(|c| to_base64(c)).flatten().collect()) BytesBase64(bytes.chunks(3).flat_map(to_base64).collect())
} }
pub fn to_bytes(&self) -> Bytes { pub fn to_bytes(&self) -> Bytes {
@@ -44,7 +68,7 @@ impl BytesBase64 {
let mut result: Vec<u8> = vec![ let mut result: Vec<u8> = vec![
((v[0] & 0b00111111) << 2) | ((v[1] & 0b00110000) >> 4), ((v[0] & 0b00111111) << 2) | ((v[1] & 0b00110000) >> 4),
((v[1] & 0b00001111) << 4) | ((v[2] & 0b00111100) >> 2), ((v[1] & 0b00001111) << 4) | ((v[2] & 0b00111100) >> 2),
((v[2] & 0b00000011) << 6) | ((v[3] & 0b00111111) >> 0), ((v[2] & 0b00000011) << 6) | (v[3] & 0b00111111),
]; ];
// removed padded bytes // removed padded bytes
for _ in c.len()..4 { for _ in c.len()..4 {
@@ -52,16 +76,16 @@ impl BytesBase64 {
} }
result result
} }
Bytes(v.chunks(4).map(|c| to_bytes(c)).flatten().collect()) Bytes(v.chunks(4).flat_map(to_bytes).collect())
} }
pub fn from_base64(s: &str) -> Result<BytesBase64, String> { pub fn from_base64(s: &str) -> Result<BytesBase64, String> {
let mut r: Vec<u8> = Vec::with_capacity(s.len()); let mut r: Vec<u8> = Vec::with_capacity(s.len());
for c in s.chars() { for c in s.chars() {
match c { match c {
'A'..='Z' => r.push((c as u8) - ('A' as u8)), 'A'..='Z' => r.push((c as u8) - b'A'),
'a'..='z' => r.push((c as u8) - ('a' as u8) + 26), 'a'..='z' => r.push((c as u8) - b'a' + 26),
'0'..='9' => r.push((c as u8) - ('0' as u8) + 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' => (),
@@ -74,31 +98,4 @@ impl BytesBase64 {
} }
Ok(BytesBase64(r)) Ok(BytesBase64(r))
} }
pub fn to_string(&self) -> String {
let BytesBase64(digits) = self;
let mut r: Vec<u8> = digits
.iter()
.map(|d| match d {
0..=25 => *d + ('A' as u8),
26..=51 => *d - 26 + ('a' as u8),
52..=61 => *d - 52 + ('0' as u8),
62 => '+' as u8,
63 => '/' as u8,
_ => panic!("Unexpected base64 digit '{}'", d),
})
.collect();
// Handle padding
let pad = '=' as u8;
match r.len() % 4 {
0 => (),
2 => {
r.push(pad);
r.push(pad);
}
3 => r.push(pad),
_ => panic!("Unexpected lenght for padding '{}'", r.len()),
}
str::from_utf8(r.as_slice()).unwrap().to_string()
}
} }

View File

@@ -44,7 +44,7 @@ pub fn decrypt(Bytes(key): &Bytes, Bytes(iv): &Bytes, Bytes(data): &Bytes) -> By
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.to_vec(); // 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.to_vec());
prev_cipher = cipher.to_vec(); prev_cipher = cipher.to_vec();

View File

@@ -6,17 +6,15 @@ pub fn encrypt(key: &Bytes, nonce: u64, data: &Bytes) -> Bytes {
} }
pub fn decrypt(Bytes(key): &Bytes, nonce: u64, Bytes(data): &Bytes) -> Bytes { pub fn decrypt(Bytes(key): &Bytes, nonce: u64, Bytes(data): &Bytes) -> Bytes {
let mut counter: u64 = 0;
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![];
for cipher in data.chunks(block_size) { for (counter, cipher) in (0_u64..).zip(data.chunks(block_size)) {
let mut keyinput = nonce.to_le_bytes().to_vec(); let mut keyinput = nonce.to_le_bytes().to_vec();
keyinput.append(&mut counter.to_le_bytes().to_vec()); keyinput.append(&mut counter.to_le_bytes().to_vec());
let keystream = crate::cbc::enrypt_aes_128_ecb_block(key, &keyinput); let keystream = crate::cbc::enrypt_aes_128_ecb_block(key, &keyinput);
let mut data = crate::utils::xor(&keystream, &cipher.to_vec()); let mut data = crate::utils::xor(&keystream, cipher);
result.append(&mut data); result.append(&mut data);
counter += 1;
} }
Bytes(result) Bytes(result)
} }

View File

@@ -4,19 +4,17 @@ use openssl::symm;
pub fn encrypt(Bytes(key): &Bytes, Bytes(data): &Bytes) -> Bytes { pub fn encrypt(Bytes(key): &Bytes, Bytes(data): &Bytes) -> Bytes {
let cipher = symm::Cipher::aes_128_ecb(); let cipher = symm::Cipher::aes_128_ecb();
let data = symm::encrypt(cipher, key, None, data); let data = symm::encrypt(cipher, key, None, data);
let result = match data { match data {
Ok(data) => Bytes(data), Ok(data) => Bytes(data),
Err(err) => panic!("{}", err.to_string()), Err(err) => panic!("{}", err.to_string()),
}; }
result
} }
pub fn decrypt(Bytes(key): &Bytes, Bytes(data): &Bytes) -> Bytes { pub fn decrypt(Bytes(key): &Bytes, Bytes(data): &Bytes) -> Bytes {
let cipher = symm::Cipher::aes_128_ecb(); let cipher = symm::Cipher::aes_128_ecb();
let data = symm::decrypt(cipher, key, None, data); let data = symm::decrypt(cipher, key, None, data);
let result = match data { match data {
Ok(data) => Bytes(data), Ok(data) => Bytes(data),
Err(err) => panic!("{}", err.to_string()), Err(err) => panic!("{}", err.to_string()),
}; }
result
} }

View File

@@ -15,7 +15,7 @@ mod sha1;
mod utils; mod utils;
fn main() { fn main() {
const RUN_ALL: bool = false; const RUN_ALL: bool = true;
if RUN_ALL { if RUN_ALL {
set1::challenge1(); set1::challenge1();
set1::challenge2(); set1::challenge2();

View File

@@ -26,8 +26,7 @@ impl Md4Core {
.wrapping_add(buffer.len() as u64) .wrapping_add(buffer.len() as u64)
.wrapping_mul(8); .wrapping_mul(8);
let mut padding = vec![]; let mut padding = vec![0x80];
padding.push(0x80);
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);
} }
@@ -42,13 +41,7 @@ impl Md4Core {
compress(&mut self.state, &bytes.try_into().unwrap()); compress(&mut self.state, &bytes.try_into().unwrap());
} }
Bytes( Bytes(self.state.iter().flat_map(|i| i.to_le_bytes()).collect())
self.state
.iter()
.map(|i| i.to_le_bytes())
.flatten()
.collect(),
)
} }
pub fn hash(&mut self, bytes: &Bytes) -> Bytes { pub fn hash(&mut self, bytes: &Bytes) -> Bytes {
@@ -64,9 +57,7 @@ impl Md4Core {
} }
pub fn fix(&mut self, fixate: [u32; 4], byte_len: u64) { pub fn fix(&mut self, fixate: [u32; 4], byte_len: u64) {
for i in 0..4 { self.state[..4].copy_from_slice(&fixate[..4]);
self.state[i] = fixate[i];
}
self.byte_len = byte_len; self.byte_len = byte_len;
} }
} }
@@ -156,5 +147,5 @@ pub fn authenticate(message: &Bytes, key: &Bytes) -> Bytes {
} }
pub fn verify(message: &Bytes, key: &Bytes, mac: &Bytes) -> bool { pub fn verify(message: &Bytes, key: &Bytes, mac: &Bytes) -> bool {
return authenticate(&message, &key) == *mac; authenticate(message, key) == *mac
} }

View File

@@ -40,10 +40,10 @@ impl MT19937 {
} }
pub fn extract_number(&mut self) -> u32 { pub fn extract_number(&mut self) -> u32 {
if self.index == N { match self.index.cmp(&N) {
self.twist(); std::cmp::Ordering::Equal => self.twist(),
} else if self.index > N { std::cmp::Ordering::Greater => panic!("Generator was never seeded"),
panic!("Generator was never seeded"); std::cmp::Ordering::Less => (),
} }
const S: u32 = 7; const S: u32 = 7;

View File

@@ -16,7 +16,7 @@ pub fn parse_key_value(text: &str) -> HashMap<String, String> {
let mut result = HashMap::new(); let mut result = HashMap::new();
tokens = scan(text, 0, tokens); tokens = scan(text, 0, tokens);
for token_chunk in tokens.chunks(4) { for token_chunk in tokens.chunks(4) {
match &token_chunk[..] { match token_chunk {
[Token::Identifier(key), Token::Equal, Token::Identifier(value), Token::Ampersand] => { [Token::Identifier(key), Token::Equal, Token::Identifier(value), Token::Ampersand] => {
result.insert(key.to_string(), value.to_string()); result.insert(key.to_string(), value.to_string());
} }
@@ -54,8 +54,8 @@ fn scan(code: &str, mut ix: usize, mut tokens: Tokens) -> Tokens {
fn scan_identifier(code: &str, mut ix: usize, mut tokens: Tokens) -> Tokens { fn scan_identifier(code: &str, mut ix: usize, mut tokens: Tokens) -> Tokens {
let start_ix = ix; let start_ix = ix;
let mut chars = code[ix..].chars(); let chars = code[ix..].chars();
while let Some(c) = chars.next() { for c in chars {
if c.is_ascii_alphanumeric() || SPECIAL_CHARS.contains(&c) { if c.is_ascii_alphanumeric() || SPECIAL_CHARS.contains(&c) {
ix += 1; ix += 1;
} else { } else {

View File

@@ -72,13 +72,13 @@ 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('\n' as u8) output_vec.push(b'\n')
} }
output_vec.push(*e.0); output_vec.push(*e.0);
} }
output_vec.push('\n' as u8); output_vec.push(b'\n');
let mut f = std::fs::File::create(path).unwrap(); let mut f = std::fs::File::create(path).unwrap();
f.write(&output_vec).unwrap(); f.write_all(&output_vec).unwrap();
} }
fn _test_roundtrip() { fn _test_roundtrip() {
@@ -104,7 +104,7 @@ pub fn challenge6() {
fn guess_key_size(bytes: &Bytes) -> usize { fn guess_key_size(bytes: &Bytes) -> usize {
let (mut lowest_rating, mut lowest_size) = (f32::MAX, 0); let (mut lowest_rating, mut lowest_size) = (f32::MAX, 0);
for key_size in 2..40 { for key_size in 2..40 {
let rating = rate(&bytes, key_size); let rating = rate(bytes, key_size);
if rating < lowest_rating { if rating < lowest_rating {
lowest_rating = rating; lowest_rating = rating;
lowest_size = key_size; lowest_size = key_size;
@@ -129,12 +129,7 @@ pub fn challenge6() {
let bytes = utils::read_base64("data/6.txt"); let bytes = utils::read_base64("data/6.txt");
let key_size = guess_key_size(&bytes); let key_size = guess_key_size(&bytes);
let bytes_tranposed = transpose(&bytes, key_size); let bytes_tranposed = transpose(&bytes, key_size);
let key = Bytes( let key = Bytes(bytes_tranposed.iter().map(Bytes::guess_key).collect());
bytes_tranposed
.iter()
.map(|b| Bytes::guess_key(&b))
.collect(),
);
let msg = Bytes::xor_cycle(&bytes, &key).to_utf8(); let msg = Bytes::xor_cycle(&bytes, &key).to_utf8();
let partial_msg = msg[..20].to_string(); let partial_msg = msg[..20].to_string();
@@ -169,7 +164,7 @@ pub fn challenge8() {
let expected_index: usize = 132; let expected_index: usize = 132;
let bytes_vector = utils::read_hex_lines("data/8.txt"); let bytes_vector = utils::read_hex_lines("data/8.txt");
let ratings: Vec<u32> = bytes_vector.iter().map(|b| rate(&b, 16)).collect(); let ratings: Vec<u32> = bytes_vector.iter().map(|b| rate(b, 16)).collect();
let min_rating = ratings.iter().min().unwrap(); let min_rating = ratings.iter().min().unwrap();
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;

View File

@@ -32,8 +32,8 @@ pub fn challenge10() {
pub fn challenge11() { pub fn challenge11() {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
enum EncryptionType { enum EncryptionType {
CBC, Cbc,
ECB, Ecb,
} }
fn pad_data(mut v: Vec<u8>) -> Bytes { fn pad_data(mut v: Vec<u8>) -> Bytes {
@@ -56,10 +56,10 @@ pub fn challenge11() {
// which to use. // which to use.
let zero_or_one: u32 = rand::thread_rng().gen_range(0..2); let zero_or_one: u32 = rand::thread_rng().gen_range(0..2);
let (data, encryption_type) = if zero_or_one == 1 { let (data, encryption_type) = if zero_or_one == 1 {
(ecb::encrypt(&key, &padded_data), EncryptionType::ECB) (ecb::encrypt(&key, &padded_data), EncryptionType::Ecb)
} else { } else {
let iv = Bytes::random(16); let iv = Bytes::random(16);
(cbc::encrypt(&key, &iv, &padded_data), EncryptionType::CBC) (cbc::encrypt(&key, &iv, &padded_data), EncryptionType::Cbc)
}; };
(data, encryption_type) (data, encryption_type)
} }
@@ -69,9 +69,9 @@ pub fn challenge11() {
// with a piece of code that, pointed at a block box that might be encrypting ECB // with a piece of code that, pointed at a block box that might be encrypting ECB
// or CBC, tells you which one is happening. // or CBC, tells you which one is happening.
if data.has_duplicated_cycle(16) { if data.has_duplicated_cycle(16) {
EncryptionType::ECB EncryptionType::Ecb
} else { } else {
EncryptionType::CBC EncryptionType::Cbc
} }
} }
@@ -106,44 +106,38 @@ pub fn challenge12() {
let mut data = data.to_vec(); let mut data = data.to_vec();
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);
let cipher = ecb::encrypt(&key, &Bytes(data)); ecb::encrypt(key, &Bytes(data))
cipher
} }
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.to_vec())).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.to_vec()));
new_cipher_len = cipher.0.len(); new_cipher_len = cipher.0.len();
} }
let key_length = new_cipher_len - initial_cipher_len; new_cipher_len - initial_cipher_len
key_length
} }
fn is_encryption_ecb(key: &Bytes) -> bool { fn is_encryption_ecb(key: &Bytes) -> bool {
let data = Bytes::from_utf8("aaaabbbbccccddddaaaabbbbccccdddd"); let data = Bytes::from_utf8("aaaabbbbccccddddaaaabbbbccccdddd");
let cipher = encryption_oracle(&key, &data); let cipher = encryption_oracle(key, &data);
if cipher.has_duplicated_cycle(16) { cipher.has_duplicated_cycle(16)
true
} else {
false
}
} }
fn decode(key: &Bytes) -> Bytes { fn decode(key: &Bytes) -> Bytes {
let block_size = get_block_size(&key); let block_size = get_block_size(key);
let block_count = encryption_oracle(&key, &Bytes(vec![])).0.len() / block_size; let block_count = encryption_oracle(key, &Bytes(vec![])).0.len() / block_size;
let mut cleartext = vec![]; let mut cleartext = vec![];
for block_index in 0..block_count { for block_index in 0..block_count {
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.to_vec()));
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 {
@@ -159,7 +153,7 @@ pub fn challenge12() {
let mut guess_text = known_text.to_vec(); let mut guess_text = known_text.to_vec();
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);
if cipher_block.0 == expected_block.0 { if cipher_block.0 == expected_block.0 {
cleartext_block.push(i); cleartext_block.push(i);
break; break;
@@ -173,7 +167,7 @@ pub fn challenge12() {
let key = Bytes::random(16); // consistent but unknown key let key = Bytes::random(16); // consistent but unknown key
assert_eq!(get_block_size(&key), 16); // 1. discover block size assert_eq!(get_block_size(&key), 16); // 1. discover block size
assert_eq!(is_encryption_ecb(&key), true); // 2. confirm oracle uses ecb assert!(is_encryption_ecb(&key)); // 2. confirm oracle uses ecb
let roundtrip_text = decode(&key); // 3.-6. let roundtrip_text = decode(&key); // 3.-6.
let cleartext = utils::read_base64("data/12.txt"); let cleartext = utils::read_base64("data/12.txt");
@@ -182,10 +176,7 @@ pub fn challenge12() {
// byte, encrypt it, and then compare it to the result of the encryption // byte, encrypt it, and then compare it to the result of the encryption
// oracle, but this approach is fine too. // oracle, but this approach is fine too.
assert_eq!(roundtrip_text.0[..138], cleartext.0); assert_eq!(roundtrip_text.0[..138], cleartext.0);
println!( println!("[okay] Challenge 12: {}", roundtrip_text.to_sub_utf8(17));
"[okay] Challenge 12: {}",
roundtrip_text.to_utf8()[..17].to_string()
);
} }
pub fn challenge13() { pub fn challenge13() {
@@ -199,11 +190,11 @@ pub fn challenge13() {
r.push_str("email="); r.push_str("email=");
r.push_str(input); r.push_str(input);
r.push_str("&uid=1337&role=user"); r.push_str("&uid=1337&role=user");
ecb::encrypt(&key, &Bytes(r.as_bytes().to_vec())) ecb::encrypt(key, &Bytes(r.as_bytes().to_vec()))
} }
fn decrypt(key: &Bytes, data: &Bytes) -> HashMap<String, String> { fn decrypt(key: &Bytes, data: &Bytes) -> HashMap<String, String> {
let c = ecb::decrypt(&key, &data); let c = ecb::decrypt(key, data);
parser::parse_key_value(&c.to_utf8()) parser::parse_key_value(&c.to_utf8())
} }
@@ -218,19 +209,19 @@ pub fn challenge13() {
// ________________________________ // ________________________________
// 0..34..78..bc..f0..34..78..bc..f0..34..78..bc..f // 0..34..78..bc..f0..34..78..bc..f0..34..78..bc..f
// email=aaaaa@a.com&uid=1337&role=user // email=aaaaa@a.com&uid=1337&role=user
let p = profile_for("aaaaa@a.com", &key); let p = profile_for("aaaaa@a.com", key);
r.append(&mut p.0[0..32].to_vec()); r.append(&mut p.0[0..32].to_vec());
// ---------------- // ----------------
// 0..34..78..bc..f0..34..78..bc..f0..34..78..bc..f // 0..34..78..bc..f0..34..78..bc..f0..34..78..bc..f
// email=aaaaaaa@a.admin&uid=1337&role=user // email=aaaaaaa@a.admin&uid=1337&role=user
let p = profile_for("aaaaaaa@a.admin", &key); let p = profile_for("aaaaaaa@a.admin", key);
r.append(&mut p.0[16..32].to_vec()); r.append(&mut p.0[16..32].to_vec());
// ---------------- // ----------------
// 0..34..78..bc..f0..34..78..bc..f0..34..78..bc..f // 0..34..78..bc..f0..34..78..bc..f0..34..78..bc..f
// email=aaaaaaaa@a.admin&uid=1337&role=user // email=aaaaaaaa@a.admin&uid=1337&role=user
let p = profile_for("aaaaaaaa@a.admin", &key); let p = profile_for("aaaaaaaa@a.admin", key);
r.append(&mut p.0[32..48].to_vec()); r.append(&mut p.0[32..48].to_vec());
Bytes(r) Bytes(r)
@@ -259,8 +250,7 @@ pub fn challenge14() {
plaintext.append(&mut attacker_controlled.to_vec()); plaintext.append(&mut attacker_controlled.to_vec());
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);
let cipher = ecb::encrypt(&random_key, &Bytes(plaintext)); ecb::encrypt(random_key, &Bytes(plaintext))
cipher
} }
fn get_block_size(prefix: &Bytes, key: &Bytes) -> usize { fn get_block_size(prefix: &Bytes, key: &Bytes) -> usize {
@@ -270,11 +260,8 @@ pub fn challenge14() {
for i in 1..10 { for i in 1..10 {
let block_size = i * 16; let block_size = i * 16;
match get_duplicated_block_indices(&cipher, block_size) { if let Some((_, _)) = get_duplicated_block_indices(&cipher, block_size) {
Some((_, _)) => { return block_size;
return block_size;
}
_ => (),
} }
} }
0 0
@@ -293,18 +280,15 @@ pub fn challenge14() {
} }
fn get_prefix_size(prefix: &Bytes, key: &Bytes) -> usize { fn get_prefix_size(prefix: &Bytes, key: &Bytes) -> usize {
let block_size = get_block_size(&prefix, &key); let block_size = get_block_size(prefix, key);
let duplicated_text = Bytes::from_utf8("aaaabbbbccccddddaaaabbbbccccdddd").0; let duplicated_text = Bytes::from_utf8("aaaabbbbccccddddaaaabbbbccccdddd").0;
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.to_vec());
let cipher = encryption_oracle(prefix, key, &Bytes(padding)); let cipher = encryption_oracle(prefix, key, &Bytes(padding));
match get_duplicated_block_indices(&cipher, block_size) { if let Some((first_block, _)) = get_duplicated_block_indices(&cipher, block_size) {
Some((first_block, _)) => { return block_size * first_block - i;
return block_size * first_block - i;
}
_ => (),
} }
} }
0 0
@@ -315,7 +299,7 @@ pub fn challenge14() {
let prefix_size = get_prefix_size(prefix, key); let prefix_size = get_prefix_size(prefix, key);
let prefix_padding_size = block_size - (prefix_size % block_size); let prefix_padding_size = block_size - (prefix_size % block_size);
let prefix_padding = Bytes(vec![b'a'; prefix_padding_size]); let prefix_padding = Bytes(vec![b'a'; prefix_padding_size]);
let block_count = encryption_oracle(&prefix, &key, &prefix_padding).len() / block_size; let block_count = encryption_oracle(prefix, key, &prefix_padding).len() / block_size;
let first_block_index = (prefix_size + prefix_padding_size) / block_size; let first_block_index = (prefix_size + prefix_padding_size) / block_size;
let mut cleartext = vec![]; let mut cleartext = vec![];
@@ -371,36 +355,17 @@ pub fn challenge14() {
let roundtrip_text = decode(&prefix, &key); let roundtrip_text = decode(&prefix, &key);
let cleartext = utils::read_base64("data/12.txt"); let cleartext = utils::read_base64("data/12.txt");
assert_eq!(roundtrip_text, cleartext); assert_eq!(roundtrip_text, cleartext);
println!( println!("[okay] Challenge 14: {}", roundtrip_text.to_sub_utf8(17));
"[okay] Challenge 14: {}",
roundtrip_text.to_utf8()[..17].to_string()
);
} }
pub fn challenge15() { pub fn challenge15() {
assert_eq!( assert!(!Bytes::from_utf8("ICE ICE BABY\u{4}\u{4}\u{4}").has_valid_pkcs7(16));
Bytes::from_utf8("ICE ICE BABY\u{4}\u{4}\u{4}").has_valid_pkcs7(16), assert!(!Bytes::from_utf8("ICE ICE BABY\u{3}\u{3}\u{4}\u{4}").has_valid_pkcs7(16));
false assert!(!Bytes::from_utf8("ICE ICE BABY!!!\u{0}").has_valid_pkcs7(16));
); assert!(Bytes::from_utf8("ICE ICE BABY!!!\u{1}").has_valid_pkcs7(16));
assert_eq!(
Bytes::from_utf8("ICE ICE BABY\u{4}\u{4}\u{4}\u{4}").has_valid_pkcs7(16),
true
);
assert_eq!(
Bytes::from_utf8("ICE ICE BABY\u{3}\u{3}\u{4}\u{4}").has_valid_pkcs7(16),
false
);
assert_eq!(
Bytes::from_utf8("ICE ICE BABY!!!\u{0}").has_valid_pkcs7(16),
false
);
assert_eq!(
Bytes::from_utf8("ICE ICE BABY!!!\u{1}").has_valid_pkcs7(16),
true
);
let mut bytes = Bytes::from_utf8("ICE ICE BABY\u{3}\u{3}\u{4}\u{4}"); let mut bytes = Bytes::from_utf8("ICE ICE BABY\u{3}\u{3}\u{4}\u{4}");
bytes.pad_pkcs7(16); bytes.pad_pkcs7(16);
assert_eq!(bytes.has_valid_pkcs7(16), true); assert!(bytes.has_valid_pkcs7(16));
println!("[okay] Challenge 15: PKCS7 works"); println!("[okay] Challenge 15: PKCS7 works");
} }
@@ -417,7 +382,7 @@ pub fn challenge16() {
r.push_str(";comment2=%20like%20a%20pound%20of%20bacon"); r.push_str(";comment2=%20like%20a%20pound%20of%20bacon");
let mut cleartext = Bytes(r.as_bytes().to_vec()); let mut cleartext = Bytes(r.as_bytes().to_vec());
cleartext.pad_pkcs7(16); cleartext.pad_pkcs7(16);
cbc::encrypt(&key, &iv, &cleartext) cbc::encrypt(key, iv, &cleartext)
} }
let iv = Bytes::random(16); let iv = Bytes::random(16);

View File

@@ -46,8 +46,9 @@ pub fn challenge17() {
for pad_byte in 1..=block_size { for pad_byte in 1..=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; attack_vector.0[block_size - pad_byte] = pad_byte as u8;
for i in 0..(pad_byte - 1) { for (i, intermittent_byte) in intermittent_result.iter().enumerate().take(pad_byte - 1)
attack_vector.0[block_size - 1 - i] = (pad_byte as u8) ^ intermittent_result[i]; {
attack_vector.0[block_size - 1 - i] = (pad_byte as u8) ^ intermittent_byte;
} }
// guess attack vector so that padding is valid // guess attack vector so that padding is valid
@@ -129,7 +130,7 @@ pub fn challenge19() {
} }
fn u8_lower(s: u8) -> u8 { fn u8_lower(s: u8) -> u8 {
if s >= b'A' && s <= b'Z' { if (b'A'..=b'Z').contains(&s) {
return s + 32; return s + 32;
} }
s s
@@ -220,7 +221,7 @@ pub fn challenge19() {
deciphered deciphered
} }
fn manual(decrypts: &Vec<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';
decrypts[2].borrow_mut()[30] = b'y'; decrypts[2].borrow_mut()[30] = b'y';
@@ -242,16 +243,11 @@ pub fn challenge19() {
let plaintexts = utils::read_base64_lines("data/19.txt"); let plaintexts = utils::read_base64_lines("data/19.txt");
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(|ct| encrypt(&ct)).collect(); let ciphers: Vec<Bytes> = plaintexts.iter().map(encrypt).collect();
let decrypts = attack(ciphers); let decrypts = attack(ciphers);
manual(&decrypts); manual(&decrypts);
for row in decrypts { let first_line = Bytes(decrypts[0].borrow().to_vec()).to_utf8();
println!( println!("[okay] Challenge 19: {first_line}");
"[okay] Challenge 19: {}",
Bytes(row.borrow().to_vec()).to_utf8()
);
break;
}
} }
pub fn challenge20() { pub fn challenge20() {
@@ -273,7 +269,7 @@ pub fn challenge20() {
let plaintexts = utils::read_base64_lines("data/20.txt"); let plaintexts = utils::read_base64_lines("data/20.txt");
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(|ct| encrypt(&ct)).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());
@@ -287,8 +283,8 @@ pub fn challenge21() {
]; ];
let mut mt = mt19937::MT19937::new(); let mut mt = mt19937::MT19937::new();
mt.seed(5489); mt.seed(5489);
for i in 0..expected.len() { for e in expected {
assert_eq!(mt.extract_number(), expected[i]); assert_eq!(mt.extract_number(), e);
} }
println!("[okay] Challenge 21: implemented MT19937"); println!("[okay] Challenge 21: implemented MT19937");
} }
@@ -442,7 +438,7 @@ pub fn challenge24() {
// brute force bb! // brute force bb!
for key in 0..u16::MAX { for key in 0..u16::MAX {
let mut found_key = true; let mut found_key = true;
let roundtrip = mtcipher::decrypt(key, &cipher); let roundtrip = mtcipher::decrypt(key, cipher);
// check if the last 14 chars are 'A' - if yes, we found the key // check if the last 14 chars are 'A' - if yes, we found the key
for i in (cipher_len - 14)..cipher_len { for i in (cipher_len - 14)..cipher_len {
if roundtrip.0[i] != b'A' { if roundtrip.0[i] != b'A' {
@@ -500,9 +496,9 @@ pub fn challenge24() {
false false
} }
assert_eq!(is_time_token(&token), true); assert!(is_time_token(&token));
let non_token = Bytes(vec![b'z', 16]); let non_token = Bytes(vec![b'z', 16]);
assert_eq!(is_time_token(&non_token), false); assert!(!is_time_token(&non_token));
println!("[okay] Challenge 24: MT19937 stream cipher implemented and cracked"); println!("[okay] Challenge 24: MT19937 stream cipher implemented and cracked");
} }

View File

@@ -17,9 +17,7 @@ pub fn challenge25() {
panic!("challenge25 - edit - out of bounds"); panic!("challenge25 - edit - out of bounds");
} }
for i in 0..newtext.len() { plaintext.0[offset..(newtext.len() + offset)].copy_from_slice(&newtext[..]);
plaintext.0[offset + i] = newtext[i];
}
ctr::encrypt(key, 0, &plaintext) ctr::encrypt(key, 0, &plaintext)
} }
@@ -64,12 +62,12 @@ pub fn challenge26() {
// comment1=cooking%20MCs;userdata=aaaaaaaaaaaaaaaa;comment2=%20like%20a%20pound%20of%20bacon // comment1=cooking%20MCs;userdata=aaaaaaaaaaaaaaaa;comment2=%20like%20a%20pound%20of%20bacon
// comment1=cooking%20MCs;userdata=fobar;admin=true;comment2=%20like%20a%20pound%20of%20bacon // comment1=cooking%20MCs;userdata=fobar;admin=true;comment2=%20like%20a%20pound%20of%20bacon
let input = "aaaaaaaaaaaaaaaa"; let input = "aaaaaaaaaaaaaaaa";
let cipher = encrypt(&input, &key); let cipher = encrypt(input, &key);
let keystream = utils::xor(&cipher.0[32..48], &Bytes::from_utf8(input).0); let keystream = utils::xor(&cipher.0[32..48], &Bytes::from_utf8(input).0);
let input = "fobar;admin=true"; let input = "fobar;admin=true";
let mut flipped_cipher = cipher.0[0..32].to_vec(); let mut flipped_cipher = cipher.0[0..32].to_vec();
flipped_cipher.append(&mut utils::xor(&keystream, &input.as_bytes())); flipped_cipher.append(&mut utils::xor(&keystream, input.as_bytes()));
flipped_cipher.append(&mut cipher.0[48..cipher.len()].to_vec()); flipped_cipher.append(&mut cipher.0[48..cipher.len()].to_vec());
let cleartext = ctr::decrypt(&key, 0, &Bytes(flipped_cipher)); let cleartext = ctr::decrypt(&key, 0, &Bytes(flipped_cipher));
@@ -84,7 +82,7 @@ pub fn challenge27() {
// AES-CBC(P_1, P_2, P_3) -> C_1, C_2, C_3 // AES-CBC(P_1, P_2, P_3) -> C_1, C_2, C_3
let mut ct = Bytes::from_utf8("comment1=cooking%20MCs;userdata=secretsaucenouse"); let mut ct = Bytes::from_utf8("comment1=cooking%20MCs;userdata=secretsaucenouse");
ct.pad_pkcs7(16); ct.pad_pkcs7(16);
cbc::encrypt(&key, &key, &ct) cbc::encrypt(key, key, &ct)
} }
fn decrypt(key: &Bytes, cipher: &Bytes) -> Result<Bytes, Bytes> { fn decrypt(key: &Bytes, cipher: &Bytes) -> Result<Bytes, Bytes> {
@@ -93,7 +91,7 @@ pub fn challenge27() {
// values). Noncompliant messages should raise an exception or return an // values). Noncompliant messages should raise an exception or return an
// error that includes the decrypted plaintext (this happens all the // error that includes the decrypted plaintext (this happens all the
// time in real systems, for what it's worth). // time in real systems, for what it's worth).
let mut cleartext = cbc::decrypt(&key, &key, &cipher); let mut cleartext = cbc::decrypt(key, key, cipher);
cleartext.remove_pkcs7(16); cleartext.remove_pkcs7(16);
match std::str::from_utf8(&cleartext.0) { match std::str::from_utf8(&cleartext.0) {
Ok(_) => Ok(cleartext), Ok(_) => Ok(cleartext),
@@ -191,7 +189,7 @@ pub fn challenge29() {
// With the registers "fixated", hash the additional data you want to // With the registers "fixated", hash the additional data you want to
// forge. // forge.
s.fix(fixate.try_into().unwrap(), byte_len); s.fix(fixate.try_into().unwrap(), byte_len);
s.hash(&bytes) s.hash(bytes)
} }
// use random // use random
@@ -263,7 +261,7 @@ pub fn challenge30() {
.map(|c| u32::from_le_bytes(c.try_into().unwrap())) .map(|c| u32::from_le_bytes(c.try_into().unwrap()))
.collect(); .collect();
m.fix(fixate.try_into().unwrap(), byte_len); m.fix(fixate.try_into().unwrap(), byte_len);
m.hash(&bytes) m.hash(bytes)
} }
let key = Bytes::random_range(2, 64); let key = Bytes::random_range(2, 64);

View File

@@ -39,14 +39,14 @@ fn bytes_to_block(bytes: &[u8]) -> Block {
} }
fn rol(value: u32, bits: usize) -> u32 { fn rol(value: u32, bits: usize) -> u32 {
return (value << bits) | (value >> (32 - bits)); (value << bits) | (value >> (32 - bits))
} }
fn blk(block: &Block, i: usize) -> u32 { fn blk(block: &Block, i: usize) -> u32 {
return rol( rol(
block[(i + 13) & 15] ^ block[(i + 8) & 15] ^ block[(i + 2) & 15] ^ block[i], block[(i + 13) & 15] ^ block[(i + 8) & 15] ^ block[(i + 2) & 15] ^ block[i],
1, 1,
); )
} }
fn r0(block: &Block, v: u32, w: &mut u32, x: u32, y: u32, z: &mut u32, i: usize) { fn r0(block: &Block, v: u32, w: &mut u32, x: u32, y: u32, z: &mut u32, i: usize) {
@@ -105,9 +105,7 @@ impl Sha1 {
} }
pub fn fix(&mut self, fixate: [u32; STATE_LEN], byte_len: u64) { pub fn fix(&mut self, fixate: [u32; STATE_LEN], byte_len: u64) {
for i in 0..5 { self.h[..5].copy_from_slice(&fixate[..5]);
self.h[i] = fixate[i];
}
self.byte_len = byte_len; self.byte_len = byte_len;
} }
@@ -219,10 +217,8 @@ impl Sha1 {
} }
pub fn get_padding(&self, bytes: &Vec<u8>) -> Vec<u8> { pub fn get_padding(&self, bytes: &Vec<u8>) -> Vec<u8> {
let mut padding = vec![];
// append 0x80 to the message // append 0x80 to the message
padding.push(0x80); let mut padding = vec![0x80];
// append 0 ≤ k < 64 bytes so that message.len() ≡ 56 (mod 64) // append 0 ≤ k < 64 bytes so that message.len() ≡ 56 (mod 64)
while (bytes.len() + padding.len()) % BLOCK_BYTES != (BLOCK_BYTES - 8) { while (bytes.len() + padding.len()) % BLOCK_BYTES != (BLOCK_BYTES - 8) {
@@ -240,7 +236,7 @@ impl Sha1 {
let mut final_bytes = vec![]; let mut final_bytes = vec![];
for bytes in bytes.0.chunks(BLOCK_BYTES) { for bytes in bytes.0.chunks(BLOCK_BYTES) {
if bytes.len() == BLOCK_BYTES { if bytes.len() == BLOCK_BYTES {
self.update(&bytes); self.update(bytes);
} else { } else {
final_bytes = bytes.to_vec(); final_bytes = bytes.to_vec();
} }
@@ -248,9 +244,9 @@ impl Sha1 {
let mut padding = self.get_padding(&final_bytes); let mut padding = self.get_padding(&final_bytes);
final_bytes.append(&mut padding); final_bytes.append(&mut padding);
for bytes in final_bytes.chunks(BLOCK_BYTES) { for bytes in final_bytes.chunks(BLOCK_BYTES) {
self.update(&bytes); self.update(bytes);
} }
Bytes(self.h.iter().map(|i| i.to_be_bytes()).flatten().collect()) Bytes(self.h.iter().flat_map(|i| i.to_be_bytes()).collect())
} }
} }
@@ -263,10 +259,9 @@ pub fn authenticate(message: &Bytes, key: &Bytes) -> Bytes {
c.append(&mut message.0.to_vec()); c.append(&mut message.0.to_vec());
// how to concatenate better: https://stackoverflow.com/a/56490417 // how to concatenate better: https://stackoverflow.com/a/56490417
let mut sha1 = Sha1::default(); let mut sha1 = Sha1::default();
let r = sha1.hash(&Bytes(c)); sha1.hash(&Bytes(c))
r
} }
pub fn verify(message: &Bytes, key: &Bytes, mac: &Bytes) -> bool { pub fn verify(message: &Bytes, key: &Bytes, mac: &Bytes) -> bool {
return authenticate(&message, &key) == *mac; authenticate(message, key) == *mac
} }

View File

@@ -25,9 +25,11 @@ pub fn read_base64_lines(path: &str) -> Vec<Bytes> {
let br = BufReader::new(file); let br = BufReader::new(file);
br.lines() br.lines()
.map(|line| { .map(|line| {
BytesBase64::from_base64(&line.expect(&format!("Failed to read line in {}", path))) BytesBase64::from_base64(
.expect(&format!("Invalid base64 in {}", path)) &line.unwrap_or_else(|_| panic!("Failed to read line in {}", path)),
.to_bytes() )
.unwrap_or_else(|_| panic!("Invalid base64 in {}", path))
.to_bytes()
}) })
.collect() .collect()
} }