diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..028a8c0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "rust-analyzer.checkOnSave.command": "clippy", +} \ No newline at end of file diff --git a/src/bytes.rs b/src/bytes.rs index 7d6d061..3dd6f22 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -11,13 +11,16 @@ impl 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 { 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 { @@ -69,7 +72,7 @@ impl Bytes { pub fn is_ascii(&self) -> bool { let Bytes(v) = self; for &c in v.iter() { - if c < 32 || c > 127 { + if !(32..=127).contains(&c) { return false; } } @@ -103,7 +106,7 @@ impl Bytes { 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 padding_value = (block_size - v.len() % block_size) as u8; for _ in 0..padding_value { @@ -118,7 +121,7 @@ impl Bytes { let last_block_index = self.len() / block_size - 1; let last_block = self.get_block(last_block_index, block_size).0; let pad_byte = last_block[block_size - 1]; - if pad_byte < 1 || pad_byte > 16 { + if !(1..=16).contains(&pad_byte) { return false; } for i in 0..(pad_byte as usize) { @@ -127,10 +130,10 @@ impl Bytes { 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) { 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 flip_mask: u8 = 0b1 << bit_index; v[byte_index] ^= flip_mask; diff --git a/src/bytes_base64.rs b/src/bytes_base64.rs index 661d645..6170275 100644 --- a/src/bytes_base64.rs +++ b/src/bytes_base64.rs @@ -6,7 +6,31 @@ pub struct BytesBase64(pub Vec); impl std::fmt::Display for BytesBase64 { 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 = 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] & 0b00000011) << 4 | (v[1] & 0b11110000) >> 4, (v[1] & 0b00001111) << 2 | (v[2] & 0b11000000) >> 6, - (v[2] & 0b00111111) << 0, + (v[2] & 0b00111111), ]; // removed padded bytes for _ in c.len()..3 { @@ -30,7 +54,7 @@ impl BytesBase64 { } 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 { @@ -44,7 +68,7 @@ impl BytesBase64 { let mut result: Vec = vec![ ((v[0] & 0b00111111) << 2) | ((v[1] & 0b00110000) >> 4), ((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 for _ in c.len()..4 { @@ -52,16 +76,16 @@ impl BytesBase64 { } 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 { let mut r: Vec = Vec::with_capacity(s.len()); for c in s.chars() { match c { - 'A'..='Z' => r.push((c as u8) - ('A' as u8)), - 'a'..='z' => r.push((c as u8) - ('a' as u8) + 26), - '0'..='9' => r.push((c as u8) - ('0' as u8) + 52), + 'A'..='Z' => r.push((c as u8) - b'A'), + 'a'..='z' => r.push((c as u8) - b'a' + 26), + '0'..='9' => r.push((c as u8) - b'0' + 52), '+' => r.push(62), '/' => r.push(63), '\n' => (), @@ -74,31 +98,4 @@ impl BytesBase64 { } Ok(BytesBase64(r)) } - - pub fn to_string(&self) -> String { - let BytesBase64(digits) = self; - let mut r: Vec = 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() - } } diff --git a/src/cbc.rs b/src/cbc.rs index 187add0..9311e54 100644 --- a/src/cbc.rs +++ b/src/cbc.rs @@ -44,7 +44,7 @@ pub fn decrypt(Bytes(key): &Bytes, Bytes(iv): &Bytes, Bytes(data): &Bytes) -> By let mut result: Vec = vec![]; let mut prev_cipher: Vec = iv.to_vec(); // first xor input is IV 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); result.extend(data.to_vec()); prev_cipher = cipher.to_vec(); diff --git a/src/ctr.rs b/src/ctr.rs index 2cba07d..39e0c99 100644 --- a/src/ctr.rs +++ b/src/ctr.rs @@ -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 { - let mut counter: u64 = 0; let cipher_type = symm::Cipher::aes_128_ecb(); let block_size = cipher_type.block_size(); let mut result: Vec = 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(); keyinput.append(&mut counter.to_le_bytes().to_vec()); 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); - counter += 1; } Bytes(result) } diff --git a/src/ecb.rs b/src/ecb.rs index fdcbc79..2a9120a 100644 --- a/src/ecb.rs +++ b/src/ecb.rs @@ -4,19 +4,17 @@ use openssl::symm; pub fn encrypt(Bytes(key): &Bytes, Bytes(data): &Bytes) -> Bytes { let cipher = symm::Cipher::aes_128_ecb(); let data = symm::encrypt(cipher, key, None, data); - let result = match data { + match data { Ok(data) => Bytes(data), Err(err) => panic!("{}", err.to_string()), - }; - result + } } pub fn decrypt(Bytes(key): &Bytes, Bytes(data): &Bytes) -> Bytes { let cipher = symm::Cipher::aes_128_ecb(); let data = symm::decrypt(cipher, key, None, data); - let result = match data { + match data { Ok(data) => Bytes(data), Err(err) => panic!("{}", err.to_string()), - }; - result + } } diff --git a/src/main.rs b/src/main.rs index 9be7b79..870fd41 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,7 @@ mod sha1; mod utils; fn main() { - const RUN_ALL: bool = false; + const RUN_ALL: bool = true; if RUN_ALL { set1::challenge1(); set1::challenge2(); diff --git a/src/md4.rs b/src/md4.rs index 14c0d6b..b50e0d7 100644 --- a/src/md4.rs +++ b/src/md4.rs @@ -26,8 +26,7 @@ impl Md4Core { .wrapping_add(buffer.len() as u64) .wrapping_mul(8); - let mut padding = vec![]; - padding.push(0x80); + let mut padding = vec![0x80]; while (buffer.len() + padding.len()) % BLOCK_BYTES != (BLOCK_BYTES - 8) { padding.push(0x0); } @@ -42,13 +41,7 @@ impl Md4Core { compress(&mut self.state, &bytes.try_into().unwrap()); } - Bytes( - self.state - .iter() - .map(|i| i.to_le_bytes()) - .flatten() - .collect(), - ) + Bytes(self.state.iter().flat_map(|i| i.to_le_bytes()).collect()) } 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) { - for i in 0..4 { - self.state[i] = fixate[i]; - } + self.state[..4].copy_from_slice(&fixate[..4]); 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 { - return authenticate(&message, &key) == *mac; + authenticate(message, key) == *mac } diff --git a/src/mt19937.rs b/src/mt19937.rs index 8c55827..5ad5860 100644 --- a/src/mt19937.rs +++ b/src/mt19937.rs @@ -40,10 +40,10 @@ impl MT19937 { } pub fn extract_number(&mut self) -> u32 { - if self.index == N { - self.twist(); - } else if self.index > N { - panic!("Generator was never seeded"); + match self.index.cmp(&N) { + std::cmp::Ordering::Equal => self.twist(), + std::cmp::Ordering::Greater => panic!("Generator was never seeded"), + std::cmp::Ordering::Less => (), } const S: u32 = 7; diff --git a/src/parser.rs b/src/parser.rs index 60f1327..9c02eae 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -16,7 +16,7 @@ pub fn parse_key_value(text: &str) -> HashMap { let mut result = HashMap::new(); tokens = scan(text, 0, tokens); for token_chunk in tokens.chunks(4) { - match &token_chunk[..] { + match token_chunk { [Token::Identifier(key), Token::Equal, Token::Identifier(value), Token::Ampersand] => { 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 { let start_ix = ix; - let mut chars = code[ix..].chars(); - while let Some(c) = chars.next() { + let chars = code[ix..].chars(); + for c in chars { if c.is_ascii_alphanumeric() || SPECIAL_CHARS.contains(&c) { ix += 1; } else { diff --git a/src/set1.rs b/src/set1.rs index 95f2502..bb0d5f6 100644 --- a/src/set1.rs +++ b/src/set1.rs @@ -72,13 +72,13 @@ pub fn challenge6() { for e in Iterator::zip(bytes.as_bytes().iter(), 0..bytes.len()) { 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('\n' as u8); + output_vec.push(b'\n'); let mut f = std::fs::File::create(path).unwrap(); - f.write(&output_vec).unwrap(); + f.write_all(&output_vec).unwrap(); } fn _test_roundtrip() { @@ -104,7 +104,7 @@ pub fn challenge6() { fn guess_key_size(bytes: &Bytes) -> usize { let (mut lowest_rating, mut lowest_size) = (f32::MAX, 0); for key_size in 2..40 { - let rating = rate(&bytes, key_size); + let rating = rate(bytes, key_size); if rating < lowest_rating { lowest_rating = rating; lowest_size = key_size; @@ -129,12 +129,7 @@ pub fn challenge6() { let bytes = utils::read_base64("data/6.txt"); let key_size = guess_key_size(&bytes); let bytes_tranposed = transpose(&bytes, key_size); - let key = Bytes( - bytes_tranposed - .iter() - .map(|b| Bytes::guess_key(&b)) - .collect(), - ); + let key = Bytes(bytes_tranposed.iter().map(Bytes::guess_key).collect()); let msg = Bytes::xor_cycle(&bytes, &key).to_utf8(); let partial_msg = msg[..20].to_string(); @@ -169,7 +164,7 @@ pub fn challenge8() { let expected_index: usize = 132; let bytes_vector = utils::read_hex_lines("data/8.txt"); - let ratings: Vec = bytes_vector.iter().map(|b| rate(&b, 16)).collect(); + let ratings: Vec = bytes_vector.iter().map(|b| rate(b, 16)).collect(); let min_rating = ratings.iter().min().unwrap(); let index = ratings.iter().position(|e| e == min_rating).unwrap(); let average = ratings.iter().sum::() as f32 / ratings.len() as f32; diff --git a/src/set2.rs b/src/set2.rs index 0be5e10..627ea2e 100644 --- a/src/set2.rs +++ b/src/set2.rs @@ -32,8 +32,8 @@ pub fn challenge10() { pub fn challenge11() { #[derive(Debug, PartialEq)] enum EncryptionType { - CBC, - ECB, + Cbc, + Ecb, } fn pad_data(mut v: Vec) -> Bytes { @@ -56,10 +56,10 @@ pub fn challenge11() { // which to use. let zero_or_one: u32 = rand::thread_rng().gen_range(0..2); let (data, encryption_type) = if zero_or_one == 1 { - (ecb::encrypt(&key, &padded_data), EncryptionType::ECB) + (ecb::encrypt(&key, &padded_data), EncryptionType::Ecb) } else { let iv = Bytes::random(16); - (cbc::encrypt(&key, &iv, &padded_data), EncryptionType::CBC) + (cbc::encrypt(&key, &iv, &padded_data), EncryptionType::Cbc) }; (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 // or CBC, tells you which one is happening. if data.has_duplicated_cycle(16) { - EncryptionType::ECB + EncryptionType::Ecb } else { - EncryptionType::CBC + EncryptionType::Cbc } } @@ -106,44 +106,38 @@ pub fn challenge12() { let mut data = data.to_vec(); let mut string = utils::read_base64("data/12.txt"); data.append(&mut string.0); - let cipher = ecb::encrypt(&key, &Bytes(data)); - cipher + ecb::encrypt(key, &Bytes(data)) } fn get_block_size(key: &Bytes) -> usize { // Detect cipher block size 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; while initial_cipher_len == new_cipher_len { 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(); } - let key_length = new_cipher_len - initial_cipher_len; - key_length + new_cipher_len - initial_cipher_len } fn is_encryption_ecb(key: &Bytes) -> bool { let data = Bytes::from_utf8("aaaabbbbccccddddaaaabbbbccccdddd"); - let cipher = encryption_oracle(&key, &data); - if cipher.has_duplicated_cycle(16) { - true - } else { - false - } + let cipher = encryption_oracle(key, &data); + cipher.has_duplicated_cycle(16) } fn decode(key: &Bytes) -> Bytes { - let block_size = get_block_size(&key); - let block_count = encryption_oracle(&key, &Bytes(vec![])).0.len() / block_size; + let block_size = get_block_size(key); + let block_count = encryption_oracle(key, &Bytes(vec![])).0.len() / block_size; let mut cleartext = vec![]; for block_index in 0..block_count { let mut cleartext_block = vec![]; for padding_length in (0..block_size).rev() { 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 mut known_text = if block_index == 0 { @@ -159,7 +153,7 @@ pub fn challenge12() { let mut guess_text = known_text.to_vec(); guess_text.push(i); 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 { cleartext_block.push(i); break; @@ -173,7 +167,7 @@ pub fn challenge12() { let key = Bytes::random(16); // consistent but unknown key 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 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 // oracle, but this approach is fine too. assert_eq!(roundtrip_text.0[..138], cleartext.0); - println!( - "[okay] Challenge 12: {}", - roundtrip_text.to_utf8()[..17].to_string() - ); + println!("[okay] Challenge 12: {}", roundtrip_text.to_sub_utf8(17)); } pub fn challenge13() { @@ -199,11 +190,11 @@ pub fn challenge13() { r.push_str("email="); r.push_str(input); 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 { - let c = ecb::decrypt(&key, &data); + let c = ecb::decrypt(key, data); 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 // 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()); // ---------------- // 0..34..78..bc..f0..34..78..bc..f0..34..78..bc..f // 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()); // ---------------- // 0..34..78..bc..f0..34..78..bc..f0..34..78..bc..f // 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()); Bytes(r) @@ -259,8 +250,7 @@ pub fn challenge14() { plaintext.append(&mut attacker_controlled.to_vec()); let mut target_bytes = utils::read_base64("data/12.txt").0; plaintext.append(&mut target_bytes); - let cipher = ecb::encrypt(&random_key, &Bytes(plaintext)); - cipher + ecb::encrypt(random_key, &Bytes(plaintext)) } fn get_block_size(prefix: &Bytes, key: &Bytes) -> usize { @@ -270,11 +260,8 @@ pub fn challenge14() { for i in 1..10 { let block_size = i * 16; - match get_duplicated_block_indices(&cipher, block_size) { - Some((_, _)) => { - return block_size; - } - _ => (), + if let Some((_, _)) = get_duplicated_block_indices(&cipher, block_size) { + return block_size; } } 0 @@ -293,18 +280,15 @@ pub fn challenge14() { } 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; for i in 0..block_size { let mut padding = vec![b'a'; i]; padding.append(&mut duplicated_text.to_vec()); let cipher = encryption_oracle(prefix, key, &Bytes(padding)); - match get_duplicated_block_indices(&cipher, block_size) { - Some((first_block, _)) => { - return block_size * first_block - i; - } - _ => (), + if let Some((first_block, _)) = get_duplicated_block_indices(&cipher, block_size) { + return block_size * first_block - i; } } 0 @@ -315,7 +299,7 @@ pub fn challenge14() { let prefix_size = get_prefix_size(prefix, key); let prefix_padding_size = block_size - (prefix_size % block_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 mut cleartext = vec![]; @@ -371,36 +355,17 @@ pub fn challenge14() { let roundtrip_text = decode(&prefix, &key); let cleartext = utils::read_base64("data/12.txt"); assert_eq!(roundtrip_text, cleartext); - println!( - "[okay] Challenge 14: {}", - roundtrip_text.to_utf8()[..17].to_string() - ); + println!("[okay] Challenge 14: {}", roundtrip_text.to_sub_utf8(17)); } pub fn challenge15() { - assert_eq!( - Bytes::from_utf8("ICE ICE BABY\u{4}\u{4}\u{4}").has_valid_pkcs7(16), - false - ); - 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 - ); + assert!(!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)); + 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)); let mut bytes = Bytes::from_utf8("ICE ICE BABY\u{3}\u{3}\u{4}\u{4}"); bytes.pad_pkcs7(16); - assert_eq!(bytes.has_valid_pkcs7(16), true); + assert!(bytes.has_valid_pkcs7(16)); println!("[okay] Challenge 15: PKCS7 works"); } @@ -417,7 +382,7 @@ pub fn challenge16() { 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) + cbc::encrypt(key, iv, &cleartext) } let iv = Bytes::random(16); diff --git a/src/set3.rs b/src/set3.rs index 83ee8d4..49fced3 100644 --- a/src/set3.rs +++ b/src/set3.rs @@ -46,8 +46,9 @@ pub fn challenge17() { for pad_byte in 1..=block_size { // 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; - for i in 0..(pad_byte - 1) { - attack_vector.0[block_size - 1 - i] = (pad_byte as u8) ^ intermittent_result[i]; + 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_byte; } // guess attack vector so that padding is valid @@ -129,7 +130,7 @@ pub fn challenge19() { } fn u8_lower(s: u8) -> u8 { - if s >= b'A' && s <= b'Z' { + if (b'A'..=b'Z').contains(&s) { return s + 32; } s @@ -220,7 +221,7 @@ pub fn challenge19() { deciphered } - fn manual(decrypts: &Vec>>) { + fn manual(decrypts: &[RefCell>]) { // Add manually guessed letters decrypts[0].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 key = Bytes::from_utf8("YELLOW SUBMARINE"); let encrypt = |plaintext: &Bytes| -> Bytes { ctr::encrypt(&key, 0, plaintext) }; - let ciphers: Vec = plaintexts.iter().map(|ct| encrypt(&ct)).collect(); + let ciphers: Vec = plaintexts.iter().map(encrypt).collect(); let decrypts = attack(ciphers); manual(&decrypts); - for row in decrypts { - println!( - "[okay] Challenge 19: {}", - Bytes(row.borrow().to_vec()).to_utf8() - ); - break; - } + let first_line = Bytes(decrypts[0].borrow().to_vec()).to_utf8(); + println!("[okay] Challenge 19: {first_line}"); } pub fn challenge20() { @@ -273,7 +269,7 @@ pub fn challenge20() { let plaintexts = utils::read_base64_lines("data/20.txt"); let key = Bytes::from_utf8("YELLOW SUBMARINE"); let encrypt = |plaintext: &Bytes| -> Bytes { ctr::encrypt(&key, 0, plaintext) }; - let ciphers: Vec = plaintexts.iter().map(|ct| encrypt(&ct)).collect(); + let ciphers: Vec = plaintexts.iter().map(encrypt).collect(); let plaintexts = attack(ciphers); println!("[okay] Challenge 20: {}", plaintexts[0].to_utf8()); @@ -287,8 +283,8 @@ pub fn challenge21() { ]; let mut mt = mt19937::MT19937::new(); mt.seed(5489); - for i in 0..expected.len() { - assert_eq!(mt.extract_number(), expected[i]); + for e in expected { + assert_eq!(mt.extract_number(), e); } println!("[okay] Challenge 21: implemented MT19937"); } @@ -442,7 +438,7 @@ pub fn challenge24() { // brute force bb! for key in 0..u16::MAX { 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 for i in (cipher_len - 14)..cipher_len { if roundtrip.0[i] != b'A' { @@ -500,9 +496,9 @@ pub fn challenge24() { false } - assert_eq!(is_time_token(&token), true); + assert!(is_time_token(&token)); 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"); } diff --git a/src/set4.rs b/src/set4.rs index f41fff6..d14494a 100644 --- a/src/set4.rs +++ b/src/set4.rs @@ -17,9 +17,7 @@ pub fn challenge25() { panic!("challenge25 - edit - out of bounds"); } - for i in 0..newtext.len() { - plaintext.0[offset + i] = newtext[i]; - } + plaintext.0[offset..(newtext.len() + offset)].copy_from_slice(&newtext[..]); 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=fobar;admin=true;comment2=%20like%20a%20pound%20of%20bacon 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 input = "fobar;admin=true"; 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()); 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 let mut ct = Bytes::from_utf8("comment1=cooking%20MCs;userdata=secretsaucenouse"); ct.pad_pkcs7(16); - cbc::encrypt(&key, &key, &ct) + cbc::encrypt(key, key, &ct) } fn decrypt(key: &Bytes, cipher: &Bytes) -> Result { @@ -93,7 +91,7 @@ pub fn challenge27() { // values). Noncompliant messages should raise an exception or return an // error that includes the decrypted plaintext (this happens all the // 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); match std::str::from_utf8(&cleartext.0) { Ok(_) => Ok(cleartext), @@ -191,7 +189,7 @@ pub fn challenge29() { // With the registers "fixated", hash the additional data you want to // forge. s.fix(fixate.try_into().unwrap(), byte_len); - s.hash(&bytes) + s.hash(bytes) } // use random @@ -263,7 +261,7 @@ pub fn challenge30() { .map(|c| u32::from_le_bytes(c.try_into().unwrap())) .collect(); m.fix(fixate.try_into().unwrap(), byte_len); - m.hash(&bytes) + m.hash(bytes) } let key = Bytes::random_range(2, 64); diff --git a/src/sha1.rs b/src/sha1.rs index 3270278..27317ce 100644 --- a/src/sha1.rs +++ b/src/sha1.rs @@ -39,14 +39,14 @@ fn bytes_to_block(bytes: &[u8]) -> Block { } 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 { - return rol( + rol( block[(i + 13) & 15] ^ block[(i + 8) & 15] ^ block[(i + 2) & 15] ^ block[i], 1, - ); + ) } 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) { - for i in 0..5 { - self.h[i] = fixate[i]; - } + self.h[..5].copy_from_slice(&fixate[..5]); self.byte_len = byte_len; } @@ -219,10 +217,8 @@ impl Sha1 { } pub fn get_padding(&self, bytes: &Vec) -> Vec { - let mut padding = vec![]; - // 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) while (bytes.len() + padding.len()) % BLOCK_BYTES != (BLOCK_BYTES - 8) { @@ -240,7 +236,7 @@ impl Sha1 { let mut final_bytes = vec![]; for bytes in bytes.0.chunks(BLOCK_BYTES) { if bytes.len() == BLOCK_BYTES { - self.update(&bytes); + self.update(bytes); } else { final_bytes = bytes.to_vec(); } @@ -248,9 +244,9 @@ impl Sha1 { let mut padding = self.get_padding(&final_bytes); final_bytes.append(&mut padding); 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()); // how to concatenate better: https://stackoverflow.com/a/56490417 let mut sha1 = Sha1::default(); - let r = sha1.hash(&Bytes(c)); - r + sha1.hash(&Bytes(c)) } pub fn verify(message: &Bytes, key: &Bytes, mac: &Bytes) -> bool { - return authenticate(&message, &key) == *mac; + authenticate(message, key) == *mac } diff --git a/src/utils.rs b/src/utils.rs index 51aaf96..4bab3c6 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -25,9 +25,11 @@ pub fn read_base64_lines(path: &str) -> Vec { let br = BufReader::new(file); br.lines() .map(|line| { - BytesBase64::from_base64(&line.expect(&format!("Failed to read line in {}", path))) - .expect(&format!("Invalid base64 in {}", path)) - .to_bytes() + BytesBase64::from_base64( + &line.unwrap_or_else(|_| panic!("Failed to read line in {}", path)), + ) + .unwrap_or_else(|_| panic!("Invalid base64 in {}", path)) + .to_bytes() }) .collect() }