feat: finish 20
This commit is contained in:
60
problems/p20/src/main.rs
Normal file
60
problems/p20/src/main.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
||||
fn read_and_decode() -> Vec<Vec<u8>> {
|
||||
let file = File::open("problems/p20/20.txt").expect("Failed to open file");
|
||||
let reader = BufReader::new(file);
|
||||
reader
|
||||
.lines()
|
||||
.map(|line| line.expect("Failed to read line"))
|
||||
.filter(|line| !line.trim().is_empty())
|
||||
.map(|line| {
|
||||
STANDARD
|
||||
.decode(line.trim())
|
||||
.expect("Failed to decode base64")
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn is_printable(byte: u8) -> bool {
|
||||
byte.is_ascii_graphic() || byte == b' '
|
||||
}
|
||||
|
||||
fn automated_attack(ciphers: &[Vec<u8>]) -> Vec<u8> {
|
||||
let min_len = ciphers.iter().map(|c| c.len()).min().unwrap_or(0);
|
||||
let mut key_stream = vec![0u8; min_len];
|
||||
|
||||
for pos in 0..min_len {
|
||||
for key_byte in 0u8..=255u8 {
|
||||
let all_printable = ciphers
|
||||
.iter()
|
||||
.all(|cipher| pos < cipher.len() && is_printable(cipher[pos] ^ key_byte));
|
||||
|
||||
if all_printable {
|
||||
key_stream[pos] = key_byte;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 输出解密结果
|
||||
println!("Decrypted plaintexts:");
|
||||
for (i, cipher) in ciphers.iter().enumerate() {
|
||||
let plaintext: Vec<u8> = cipher
|
||||
.iter()
|
||||
.zip(key_stream.iter())
|
||||
.map(|(&c, &k)| c ^ k)
|
||||
.collect();
|
||||
let plaintext_str = String::from_utf8_lossy(&plaintext[..min_len.min(cipher.len())]);
|
||||
println!("{}: {}", i, plaintext_str);
|
||||
}
|
||||
|
||||
key_stream
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let ciphers = read_and_decode();
|
||||
let _key_stream = automated_attack(&ciphers);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user