solve Challenge 6 of Set 1

This commit is contained in:
Thomas Lindner 2023-12-17 21:30:54 +01:00
parent 3f4de3d0d2
commit 311f274645
8 changed files with 280 additions and 4 deletions

13
Cargo.lock generated
View file

@ -17,9 +17,22 @@ version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "break_repeating_xor"
version = "0.1.0"
dependencies = [
"common",
"env_logger",
"log",
"rustc-serialize",
]
[[package]]
name = "common"
version = "0.1.0"
dependencies = [
"log",
]
[[package]]
name = "detect_xor"

View file

@ -7,5 +7,6 @@ members = [
"set1/challenge3_xor_cipher",
"set1/challenge4_detect_xor",
"set1/challenge5_repeating_xor",
"set1/challenge6_break_repeating_xor",
]
resolver = "2"

View file

@ -5,3 +5,4 @@ edition = "2021"
authors = ["Thomas Lindner <tom@dl6tom.de>"]
[dependencies]
log = "0.4"

View file

@ -1,6 +1,12 @@
use log::{debug, trace};
use std::ascii::escape_default;
use std::iter::repeat;
pub fn score(plaintext: &Vec<u8>) -> u32 {
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;
@ -8,7 +14,9 @@ pub fn score(plaintext: &Vec<u8>) -> u32 {
for ch in plaintext.iter() {
if ch >= &0x20 && ch <= &0x7e {
score += 1;
if ch >= &('a' as u8) && ch <= &('z' as u8) {
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];
@ -18,6 +26,26 @@ pub fn score(plaintext: &Vec<u8>) -> u32 {
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()
}
@ -35,16 +63,58 @@ pub fn break_xor_cipher(ciphertext: &[u8]) -> (Vec<u8>, u32) {
let mut decrypted: Vec<u8> = vec![];
for key in 0..0xff {
let plaintext: Vec<u8> = xor_cipher(&ciphertext, key);
let s = score(&plaintext);
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
}

View file

@ -0,0 +1,64 @@
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

View file

@ -0,0 +1,79 @@
I'm back and I'm ringin' the bell
A rockin' on the mike while the fly girls yell
In ecstasy in the back of me
Well that's my DJ Deshay cuttin' all them Z's
Hittin' hard and the girlies goin' crazy
Vanilla's on the mike, man I'm not lazy.
I'm lettin' my drug kick in
It controls my mouth and I begin
To just let it flow, let my concepts go
My posse's to the side yellin', Go Vanilla Go!
Smooth 'cause that's the way I will be
And if you don't give a damn, then
Why you starin' at me
So get off 'cause I control the stage
There's no dissin' allowed
I'm in my own phase
The girlies sa y they love me and that is ok
And I can dance better than any kid n' play
Stage 2 -- Yea the one ya' wanna listen to
It's off my head so let the beat play through
So I can funk it up and make it sound good
1-2-3 Yo -- Knock on some wood
For good luck, I like my rhymes atrocious
Supercalafragilisticexpialidocious
I'm an effect and that you can bet
I can take a fly girl and make her wet.
I'm like Samson -- Samson to Delilah
There's no denyin', You can try to hang
But you'll keep tryin' to get my style
Over and over, practice makes perfect
But not if you're a loafer.
You'll get nowhere, no place, no time, no girls
Soon -- Oh my God, homebody, you probably eat
Spaghetti with a spoon! Come on and say it!
VIP. Vanilla Ice yep, yep, I'm comin' hard like a rhino
Intoxicating so you stagger like a wino
So punks stop trying and girl stop cryin'
Vanilla Ice is sellin' and you people are buyin'
'Cause why the freaks are jockin' like Crazy Glue
Movin' and groovin' trying to sing along
All through the ghetto groovin' this here song
Now you're amazed by the VIP posse.
Steppin' so hard like a German Nazi
Startled by the bases hittin' ground
There's no trippin' on mine, I'm just gettin' down
Sparkamatic, I'm hangin' tight like a fanatic
You trapped me once and I thought that
You might have it
So step down and lend me your ear
'89 in my time! You, '90 is my year.
You're weakenin' fast, YO! and I can tell it
Your body's gettin' hot, so, so I can smell it
So don't be mad and don't be sad
'Cause the lyrics belong to ICE, You can call me Dad
You're pitchin' a fit, so step back and endure
Let the witch doctor, Ice, do the dance to cure
So come up close and don't be square
You wanna battle me -- Anytime, anywhere
You thought that I was weak, Boy, you're dead wrong
So come on, everybody and sing this song
Say -- Play that funky music Say, go white boy, go white boy go
play that funky music Go white boy, go white boy, go
Lay down and boogie and play that funky music till you die.
Play that funky music Come on, Come on, let me hear
Play that funky music white boy you say it, say it
Play that funky music A little louder now
Play that funky music, white boy Come on, Come on, Come on
Play that funky music

View file

@ -0,0 +1,11 @@
[package]
name = "break_repeating_xor"
version = "0.1.0"
edition = "2021"
authors = ["Thomas Lindner <tom@dl6tom.de>"]
[dependencies]
common = { path = "../../common" }
log = "0.4"
env_logger = "0.10"
rustc-serialize = "0.3"

View file

@ -0,0 +1,37 @@
use common::break_repeating_xor_cipher;
use log::error;
use rustc_serialize::base64::FromBase64;
use std::error::Error;
use std::io::{Read, read_to_string, stdin};
fn break_xor(input: &mut dyn Read) -> Result<String, Box<dyn Error>> {
let ciphertext = read_to_string(input)?.from_base64()?;
let decrypted = break_repeating_xor_cipher(ciphertext.as_slice());
Ok(String::from_utf8(decrypted)?)
}
fn main() {
env_logger::init();
match break_xor(&mut stdin().lock()) {
Ok(s) => println!("{}", s),
Err(e) => error!("{}", e)
}
}
#[cfg(test)]
mod tests {
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use super::*;
#[test]
fn challenge6_break_repeating_xor() {
let cargo_manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let testinput = File::open(cargo_manifest_dir.join("6.txt")).unwrap();
let decrypted = break_xor(&mut BufReader::new(testinput)).unwrap();
let plaintext = read_to_string(File::open(cargo_manifest_dir.join("6_plaintext.txt")).unwrap()).unwrap();
assert_eq!(decrypted, plaintext);
}
}