otp.rs
// This code can be compiled in 3 modes: | |
// rustc --cfg find | |
// rustc --cfg interactive | |
// rustc --cfg oracle | |
// | |
// The interactive mode is based on the | |
// crib dragging technique described by | |
// Dan Crowley at: | |
// https://www.trustwave.com/Resources/SpiderLabs-Blog/The-Way-of-the-Cryptologist/ | |
fn code(msg: &str, key: &str, encode: bool) -> String { | |
use std::ascii::AsciiExt; | |
assert!(msg.is_ascii()); | |
assert!(key.is_ascii()); | |
let len = std::cmp::min(msg.len(), key.len()); | |
let mut result = String::with_capacity(len); | |
// Convery lowercase -> uppercase and everything else to space | |
// Then encode 'A'..'Z' => '1'..'26' and ' ' => 0 | |
let normalize = |x| match x { | |
b'a'...b'z' => (x - 32) as i8 - b'A' as i8 + 1, | |
b'A'...b'Z' => x as i8 - b'A' as i8 + 1, | |
_ => 0 | |
}; | |
// % does not have the semantics we want for negative values | |
let modulus = |a, b| ((a % b) + b) % b; | |
for (k, m) in key.bytes().zip(msg.bytes()) { | |
let (k, m) = (normalize(k), normalize(m)); | |
// Either encode or decode | |
let c = if encode { m + k } else { m - k }; | |
// Gotta make sure we keep to A-Z | |
let c = modulus(c, 27); | |
// And back to ASCII | |
let c = if c == 0 { b' ' } else { c as u8 + b'A' - 1 }; | |
result.push(c as char); | |
} | |
result | |
} | |
#[cfg(interactive)] | |
#[allow(unused_must_use)] | |
fn main() { | |
use std::io::{self, Write}; | |
use std::iter; | |
let c1 = include_str!("enc1.txt").trim_right_matches('\n'); | |
let c2 = include_str!("enc2.txt").trim_right_matches('\n'); | |
let diff = code(c1, c2, false); | |
println!("c1 - c2 = m1 - m2:\n{}\n", diff); | |
if let Some(pos) = diff.find("LIKELY") { | |
println!("LIKELY Position: {}", pos); | |
println!(" c1 \t {}", &c1[pos-1..pos+7]); | |
println!(" c2 \t {}", &c2[pos-1..pos+7]); | |
println!("c1-c2 \t {}", &diff[pos-1..pos+7]); | |
} | |
let stdout = io::stdout(); | |
let mut out = stdout.lock(); | |
let mut m1: String = iter::repeat('_').take(diff.len()).collect(); | |
let mut m2: String = iter::repeat('_').take(diff.len()).collect(); | |
let mut key: String = iter::repeat('_').take(diff.len()).collect(); | |
let mut crib = String::new(); | |
let mut res = String::new(); | |
'repl: loop { | |
crib.clear(); | |
res.clear(); | |
write!(out, "Msg 1:\t{}\n\nMsg 2:\t{}\n\nKey:\t{}\n\n", m1, m2, key); | |
write!(out, "Crib: "); | |
out.flush(); | |
if let Err(e) = io::stdin().read_line(&mut crib) { | |
panic!("Error reading crib: {}", e); | |
} | |
crib = crib.trim_right_matches('\n').to_string(); | |
let run = diff.len() - crib.len() + 1; | |
for i in 0..run { | |
write!(out, "[{}]: >{}< >{}<\n", i, code(&diff[i..], &crib, true), code(&crib, &diff[i..], false)); | |
} | |
'resp: loop { | |
write!(out, "\nSelect [i], 'none' or 'quit'? "); | |
out.flush(); | |
if let Err(e) = io::stdin().read_line(&mut res) { | |
panic!("Error reading response: {}", e); | |
} | |
if res.trim() == "quit" || res.trim() == "q" { | |
break 'repl; | |
} else if res.trim() == "none" || res.trim() == "n" { | |
break 'resp; | |
} else { | |
match res.trim().parse::<usize>() { | |
Ok(i) if i < diff.len() => { | |
let mut which = String::new(); | |
write!(out, "Crib part of message [1] or [2]? "); | |
out.flush(); | |
if let Err(e) = io::stdin().read_line(&mut which) { | |
panic!("Error reading message selection: {}", e); | |
} | |
let loc1 = (&mut m1[i..i + crib.len()]).as_ptr() as *mut _; | |
let loc2 = (&mut m2[i..i + crib.len()]).as_ptr() as *mut _; | |
let k = if which.trim() == "1" { | |
let m2 = code(&crib, &diff[i..], false); | |
unsafe { | |
std::intrinsics::copy(crib.as_ptr(), loc1, m2.len()); | |
std::intrinsics::copy(m2.as_ptr(), loc2, m2.len()); | |
} | |
code(&c2[i..], &m2, false) | |
} else if which.trim() == "2" { | |
let m1 = code(&diff[i..], &crib, true); | |
unsafe { | |
std::intrinsics::copy(m1.as_ptr(), loc1, m1.len()); | |
std::intrinsics::copy(crib.as_ptr(), loc2, m1.len()); | |
} | |
code(&c1[i..], &m1, false) | |
} else { | |
break 'resp; | |
}; | |
unsafe { | |
let loc = (&mut key[i..i + crib.len()]).as_ptr() as *mut _; | |
std::intrinsics::copy(k.as_ptr(), loc, k.len()); | |
} | |
break 'resp; | |
} | |
_ => { | |
write!(out, "Invalid entry.\n"); | |
} | |
} | |
} | |
} | |
} | |
} | |
#[cfg(find)] | |
fn main() { | |
let words = include_str!("1000.dicin"); | |
let c1 = include_str!("enc1.txt").trim_right_matches('\n'); | |
let c2 = include_str!("enc2.txt").trim_right_matches('\n'); | |
let diff = code(c1, c2, false); | |
for w in words.lines() { | |
let mut word = "LIKELY ".to_string(); | |
word.push_str(w.trim_right_matches('\n')); | |
// m1 - (c1 - c2) | |
// = m1 - ((m1 + k) - (m2 + k)) | |
// = m1 - (m1 + k - m2 - k) | |
// = m1 - (m1 - m2) | |
// = m1 - m1 + m2 | |
// = m2 | |
let c = code(&word, &diff[2641..2641+word.len()], false); | |
println!("{} - {}", word, c.trim()); | |
} | |
} | |
#[cfg(oracle)] | |
fn main() { | |
use std::io::{self, Write}; | |
let stdout = io::stdout(); | |
let mut handle = stdout.lock(); | |
write!(handle, "Key: ").unwrap(); | |
handle.flush().unwrap(); | |
let mut key = String::new(); | |
if let Err(e) = io::stdin().read_line(&mut key) { | |
panic!("Error reading key: {}", e); | |
} | |
write!(handle, "Msg: ").unwrap(); | |
handle.flush().unwrap(); | |
let mut msg = String::new(); | |
if let Err(e) = io::stdin().read_line(&mut msg) { | |
panic!("Error reading msg: {}", e); | |
} | |
write!(handle, "[D]ecode or [E]ncode? ").unwrap(); | |
handle.flush().unwrap(); | |
let mut action = String::new(); | |
if let Err(e) = io::stdin().read_line(&mut action) { | |
panic!("Error reading action: {}", e); | |
} | |
if !action.starts_with("D") && | |
!action.starts_with("E") { | |
panic!("Plz."); | |
} | |
let c = code(msg.trim_right_matches('\n'), | |
key.trim_right_matches('\n'), | |
action.starts_with("E")); | |
println!("Result:\n{}", c); | |
} |