1
0

feat: finish 20

This commit is contained in:
2025-08-31 16:26:46 +08:00
parent d5b419d196
commit 41ea79cb8d
4 changed files with 128 additions and 0 deletions

60
problems/p20/src/main.rs Normal file
View 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);
}