151 lines
4.2 KiB
Rust
151 lines
4.2 KiB
Rust
use log::{debug, trace};
|
|
use std::ascii::escape_default;
|
|
use std::iter::repeat;
|
|
|
|
pub fn escape(text: &[u8]) -> String {
|
|
String::from_utf8(
|
|
text.iter()
|
|
.map(|ch| escape_default(ch.clone()))
|
|
.flatten()
|
|
.collect(),
|
|
)
|
|
.unwrap()
|
|
}
|
|
|
|
pub fn score(plaintext: &[u8]) -> u32 {
|
|
let ranking = [
|
|
24, 7, 15, 17, 26, 11, 10, 19, 22, 4, 5, 16, 13, 21, 23, 8, 2, 18, 20, 25, 14, 6, 12, 3, 9,
|
|
1,
|
|
];
|
|
let mut score = 0;
|
|
|
|
for ch in plaintext.iter() {
|
|
if ch >= &0x20 && ch <= &0x7e {
|
|
score += 1;
|
|
if ch == &(' ' as u8) {
|
|
score += 27;
|
|
} else if ch >= &('a' as u8) && ch <= &('z' as u8) {
|
|
score += ranking[(ch - 'a' as u8) as usize];
|
|
} else if ch >= &('A' as u8) && ch <= &('Z' as u8) {
|
|
score += ranking[(ch - 'A' as u8) as usize];
|
|
}
|
|
}
|
|
}
|
|
score
|
|
}
|
|
|
|
pub fn hamming(v1: &[u8], v2: &[u8]) -> u32 {
|
|
v1.iter()
|
|
.zip(v2.iter())
|
|
.map(|(a, b)| a ^ b)
|
|
.map(|x| ((x & 0xaa) >> 1) + (x & 0x55))
|
|
.map(|x| ((x & 0xcc) >> 2) + (x & 0x33))
|
|
.map(|x| ((x & 0xf0) >> 4) + (x & 0x0f))
|
|
.map(|x| x as u32)
|
|
.sum()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn challenge6_hamming() {
|
|
let v1 = "this is a test";
|
|
let v2 = "wokka wokka!!!";
|
|
assert_eq!(hamming(v1.as_bytes(), v2.as_bytes()), 37);
|
|
}
|
|
}
|
|
|
|
pub fn generic_xor_cipher(text: &[u8], keyiter: &mut dyn Iterator<Item = &u8>) -> Vec<u8> {
|
|
text.iter().zip(keyiter).map(|(c, k)| c ^ k).collect()
|
|
}
|
|
|
|
pub fn fixed_xor_cipher(text: &[u8], key: &[u8]) -> Vec<u8> {
|
|
generic_xor_cipher(text, &mut key.iter())
|
|
}
|
|
|
|
pub fn xor_cipher(text: &[u8], key: u8) -> Vec<u8> {
|
|
generic_xor_cipher(text, &mut repeat(&key))
|
|
}
|
|
|
|
pub fn break_xor_cipher(ciphertext: &[u8]) -> (Vec<u8>, u32) {
|
|
let mut maxscore = 0;
|
|
let mut decrypted: Vec<u8> = vec![];
|
|
|
|
for key in 0..0xff {
|
|
let plaintext = xor_cipher(ciphertext, key);
|
|
let s = score(plaintext.as_slice());
|
|
trace!("score:{} \"{}\"", s, escape(plaintext.as_slice()));
|
|
if s > maxscore {
|
|
maxscore = s;
|
|
decrypted = plaintext;
|
|
}
|
|
}
|
|
debug!("score:{} \"{}\"", maxscore, escape(decrypted.as_slice()));
|
|
(decrypted, maxscore)
|
|
}
|
|
|
|
pub fn repeating_xor_cipher(text: &[u8], key: &[u8]) -> Vec<u8> {
|
|
generic_xor_cipher(text, &mut key.iter().cycle())
|
|
}
|
|
|
|
pub fn break_repeating_xor_cipher(ciphertext: &[u8]) -> Vec<u8> {
|
|
let mut mindistance = u32::MAX;
|
|
let mut keylength = 0;
|
|
for length in 1..40 {
|
|
let mut distance = 0;
|
|
let maxoffset = ciphertext.len() / length - 1;
|
|
for offset in 0..maxoffset {
|
|
distance += hamming(
|
|
&ciphertext[offset * length..(offset + 1) * length],
|
|
&ciphertext[(offset + 1) * length..(offset + 2) * length],
|
|
);
|
|
}
|
|
// scale and normalize
|
|
distance *= 1024;
|
|
distance /= (maxoffset * length) as u32;
|
|
trace!("distance:{} keylength:{}", distance, length);
|
|
if distance < mindistance {
|
|
mindistance = distance;
|
|
keylength = length;
|
|
}
|
|
}
|
|
debug!("estimated keylength: {}", keylength);
|
|
|
|
let mut decryptedcolumns: Vec<Vec<u8>> = vec![];
|
|
for offset in 0..keylength {
|
|
let ciphercolumn: Vec<u8> = ciphertext[offset..]
|
|
.iter()
|
|
.step_by(keylength)
|
|
.cloned()
|
|
.collect();
|
|
let (decrypted, _) = break_xor_cipher(ciphercolumn.as_slice());
|
|
decryptedcolumns.push(decrypted);
|
|
}
|
|
|
|
let mut decrypted: Vec<u8> = vec![];
|
|
for i in 0..decryptedcolumns[0].len() {
|
|
for column in decryptedcolumns.iter() {
|
|
if column.len() <= i {
|
|
break;
|
|
}
|
|
decrypted.push(column[i]);
|
|
}
|
|
}
|
|
decrypted
|
|
}
|
|
|
|
pub fn detect_ecb(ciphertext: &[u8]) -> bool {
|
|
for block1_nr in 1..ciphertext.len() / 16 {
|
|
let block1 = &ciphertext[block1_nr * 16..(block1_nr + 1) * 16];
|
|
for block2_nr in 0..block1_nr {
|
|
let block2 = &ciphertext[block2_nr * 16..(block2_nr + 1) * 16];
|
|
if block1 == block2 {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
false
|
|
}
|