Finish challenge 20 statistical CTR.
This commit is contained in:
10
src/bytes.rs
10
src/bytes.rs
@@ -93,6 +93,16 @@ impl Bytes {
|
||||
r
|
||||
}
|
||||
|
||||
pub fn guess_key(&self) -> u8 {
|
||||
// Assuming all bytes have been xor-encrypted by the same u8 key, find that key
|
||||
// by trying all u8 values and compute an ascii score for the resulting "plaintext".
|
||||
// The u8 key for the "plaintext" with the highest score is returned as the guessed
|
||||
// key.
|
||||
let mut h: Vec<(Bytes, u8)> = (0..=255).map(|i| (Bytes::xor_byte(self, i), i)).collect();
|
||||
h.sort_by(|a, b| a.0.ascii_score().partial_cmp(&b.0.ascii_score()).unwrap());
|
||||
h.last().unwrap().1
|
||||
}
|
||||
|
||||
pub fn pad_pkcs7(&mut self, block_size: usize) -> () {
|
||||
let Bytes(v) = self;
|
||||
let padding_value = (block_size - v.len() % block_size) as u8;
|
||||
|
||||
@@ -31,7 +31,8 @@ fn main() {
|
||||
set3::challenge18();
|
||||
set3::challenge19();
|
||||
set3::challenge20();
|
||||
set3::challenge21();
|
||||
} else {
|
||||
set3::challenge20();
|
||||
set3::challenge21();
|
||||
}
|
||||
}
|
||||
|
||||
13
src/set1.rs
13
src/set1.rs
@@ -138,17 +138,16 @@ pub fn challenge6() {
|
||||
result
|
||||
}
|
||||
|
||||
fn guess_key(bytes: &Bytes) -> u8 {
|
||||
let mut h: Vec<(Bytes, u8)> = (0..=255).map(|i| (Bytes::xor_byte(bytes, i), i)).collect();
|
||||
h.sort_by(|a, b| a.0.ascii_score().partial_cmp(&b.0.ascii_score()).unwrap());
|
||||
h.last().unwrap().1
|
||||
}
|
||||
|
||||
_test_roundtrip();
|
||||
let bytes = read("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| guess_key(&b)).collect());
|
||||
let key = Bytes(
|
||||
bytes_tranposed
|
||||
.iter()
|
||||
.map(|b| Bytes::guess_key(&b))
|
||||
.collect(),
|
||||
);
|
||||
let msg = Bytes::xor_cycle(&bytes, &key).to_utf8();
|
||||
|
||||
let partial_msg = msg[..20].to_string();
|
||||
|
||||
35
src/set3.rs
35
src/set3.rs
@@ -268,5 +268,38 @@ pub fn challenge19() {
|
||||
}
|
||||
|
||||
pub fn challenge20() {
|
||||
println!("[xxxx] Challenge 20: TBD");
|
||||
fn read(path: &str) -> Vec<Bytes> {
|
||||
let file = std::fs::File::open(path).unwrap();
|
||||
let br = BufReader::new(file);
|
||||
br.lines()
|
||||
.map(|line| BytesBase64::from_base64(&line.unwrap()).to_bytes())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn attack(ciphers: Vec<Bytes>) -> Vec<Bytes> {
|
||||
let min_cipher_len = ciphers.iter().map(|c| c.len()).min().unwrap_or(0);
|
||||
let mut key: Vec<u8> = vec![];
|
||||
for byte_index in 0..min_cipher_len {
|
||||
let bytes = Bytes(ciphers.iter().map(|c| c.0[byte_index]).collect());
|
||||
let key_char = Bytes::guess_key(&bytes);
|
||||
key.push(key_char);
|
||||
}
|
||||
let key = Bytes(key);
|
||||
ciphers
|
||||
.iter()
|
||||
.map(|cipher| Bytes::xor(&key, cipher))
|
||||
.collect()
|
||||
}
|
||||
|
||||
let plaintexts = read("data/20.txt");
|
||||
let key = Bytes::from_utf8("YELLOW SUBMARINE");
|
||||
let encrypt = |plaintext: &Bytes| -> Bytes { ctr::encrypt(&key, 0, plaintext) };
|
||||
let ciphers: Vec<Bytes> = plaintexts.iter().map(|ct| encrypt(&ct)).collect();
|
||||
let plaintexts = attack(ciphers);
|
||||
|
||||
println!("[okay] Challenge 20: {}", plaintexts[0].to_utf8());
|
||||
}
|
||||
|
||||
pub fn challenge21() {
|
||||
println!("[xxxx] Challenge 21: TBD");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user