use num_bigint::RandBigInt; use rand::Rng; use rayon::prelude::*; use std::borrow::ToOwned; use std::collections::{HashMap, HashSet}; use crate::aes::encrypt_aes_128_cbc; use crate::data::ENGLISH_FREQUENCIES; use crate::primitives::{fixed_xor, hamming, repeating_key_xor, unpad_pkcs7}; use crate::random::MersenneTwister; #[derive(PartialEq, Eq, Debug)] pub enum BlockCipherMode { ECB, CBC, } pub fn find_single_byte_xor_encrypted_string(inputs: &[Vec]) -> Vec { let mut min_diff = 100.0; let mut best_decrypted = vec![]; for input in inputs { let (key, diff) = crack_single_byte_xor_with_confidence(input); if diff < min_diff { min_diff = diff; best_decrypted = repeating_key_xor(input, &[key]); } } return best_decrypted; } pub fn crack_single_byte_xor(input: &[u8]) -> Vec { let (key, _) = crack_single_byte_xor_with_confidence(input); return repeating_key_xor(input, &[key]); } pub fn crack_repeating_key_xor(input: &[u8]) -> Vec { let mut keysizes = vec![]; for keysize in 2..40 { let distance1 = hamming( &input[(keysize * 0)..(keysize * 1)], &input[(keysize * 1)..(keysize * 2)], ) as f64; let distance2 = hamming( &input[(keysize * 1)..(keysize * 2)], &input[(keysize * 2)..(keysize * 3)], ) as f64; let distance3 = hamming( &input[(keysize * 2)..(keysize * 3)], &input[(keysize * 3)..(keysize * 4)], ) as f64; let distance = distance1 + distance2 + distance3 / 3.0; let normal_distance = distance / (keysize as f64); keysizes.push((keysize, normal_distance)); if keysizes.len() > 5 { let (idx, _) = keysizes.iter().enumerate().fold( (0, (0, 0.0)), |(accidx, (accsize, accdist)), (idx, &(size, dist))| { if dist > accdist { (idx, (size, dist)) } else { (accidx, (accsize, accdist)) } }, ); keysizes.swap_remove(idx); } } let mut min_diff = 100.0; let mut best_key = vec![]; for (keysize, _) in keysizes { let (key, diff) = crack_repeating_key_xor_with_keysize(input, keysize); if diff < min_diff { min_diff = diff; best_key = key; } } return best_key; } pub fn find_aes_128_ecb_encrypted_string(inputs: &[Vec]) -> Vec { let mut max_dups = 0; let mut found = vec![]; for input in inputs { let dups = count_duplicate_blocks(input, 16); if dups > max_dups { max_dups = dups; found = input.clone(); } } return found; } pub fn detect_ecb_cbc(f: &F, block_size: usize) -> BlockCipherMode where F: Fn(&[u8]) -> Vec, { if block_size >= std::u8::MAX as usize { panic!("invalid block size: {}", block_size); } let block_size_byte = block_size as u8; let plaintext: Vec = (0..block_size_byte) .cycle() .take(block_size * 2) .flat_map(|n| std::iter::repeat(n).take(block_size + 1)) .collect(); let ciphertext = f(&plaintext[..]); if count_duplicate_blocks(&ciphertext[..], block_size) >= block_size { return BlockCipherMode::ECB; } else { return BlockCipherMode::CBC; } } pub fn crack_padded_aes_128_ecb(f: &F) -> Vec where F: Fn(&[u8]) -> Vec, { let block_size = find_block_size(f); if detect_ecb_cbc(f, block_size) != BlockCipherMode::ECB { panic!("Can only crack ECB-encrypted data"); } let mut plaintext = vec![]; let get_block = |input: &[u8], i| { let encrypted = f(input); let block_number = i / block_size; let low = block_number * block_size; let high = (block_number + 1) * block_size; encrypted[low..high].to_vec() }; let mut i = 0; loop { let mut map = HashMap::new(); let prefix: Vec = std::iter::repeat(b'A') .take(block_size - ((i % block_size) + 1)) .collect(); for c in 0..256 { let mut prefix_with_next_char = prefix.clone(); for &c in plaintext.iter() { prefix_with_next_char.push(c); } prefix_with_next_char.push(c as u8); map.insert(get_block(&prefix_with_next_char[..], i), c as u8); } let next_char = map.get(&get_block(&prefix[..], i)); if next_char.is_some() { plaintext.push(*next_char.unwrap()); } else { break; } i += 1; } return unpad_pkcs7(&plaintext[..]) .expect("invalid padding") .to_vec(); } pub fn crack_padded_aes_128_ecb_with_prefix(f: &F) -> Vec where F: Fn(&[u8]) -> Vec, { let (block_size, prefix_len) = find_block_size_and_fixed_prefix_len(f); let wrapped_f = |input: &[u8]| { let alignment_padding = block_size - (prefix_len % block_size); let padded_input: Vec = std::iter::repeat(b'A') .take(alignment_padding) .chain(input.iter().map(|x| *x)) .collect(); return f(&padded_input[..]) .iter() .skip(prefix_len + alignment_padding) .map(|x| *x) .collect(); }; return crack_padded_aes_128_ecb(&wrapped_f); } pub fn crack_querystring_aes_128_ecb( encrypter: &F, ) -> (String, Vec>) where F: Fn(&str) -> Vec, { fn incr_map_element(map: &mut HashMap, usize>, key: Vec) { if let Some(val) = map.get_mut(&key) { *val += 1; return; } map.insert(key, 1); }; // find blocks that correspond to "uid=10&role=user" or "role=user&uid=10" let find_uid_role_blocks = || { let mut map = HashMap::new(); for c in 32..127 { let email_bytes: Vec = std::iter::repeat(c).take(9).collect(); let email = std::str::from_utf8(&email_bytes[..]).unwrap(); let ciphertext = encrypter(email); incr_map_element(&mut map, ciphertext[..16].to_vec()); incr_map_element(&mut map, ciphertext[16..32].to_vec()); } let mut most_common_blocks = vec![]; for (k, v) in map { most_common_blocks.push((k, v)); if most_common_blocks.len() > 2 { let (idx, _) = most_common_blocks.iter().enumerate().fold( (0, (vec![], 10000)), |(aidx, (ablock, acount)), (idx, &(ref block, count))| { if count < acount { (idx, (block.clone(), count)) } else { (aidx, (ablock.clone(), acount)) } }, ); most_common_blocks.swap_remove(idx); } } if most_common_blocks.len() == 2 { let (ref block1, _) = most_common_blocks[0]; let (ref block2, _) = most_common_blocks[1]; return (block1.clone(), block2.clone()); } else { panic!("couldn't find most common blocks"); } }; // encrypt: // email=..........admin...............&uid=10&role=user let calculate_admin_block = |block1: Vec, block2: Vec| { for _ in 0..1000 { let email = "blorg@bar.admin\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b..............."; let ciphertext = encrypter(email); if &ciphertext[48..64] == &block1[..] || &ciphertext[48..64] == &block2[..] { return ciphertext[16..32].to_vec(); } } panic!("couldn't find a ciphertext with the correct role/uid block"); }; // find all possible encryptions with email=............ and then replace // the last block with the padded admin block above let calculate_possible_admin_ciphertexts = |admin_block: Vec| { let email = "blorg@bar.com"; let mut possibles = vec![]; while possibles.len() < 6 { let ciphertext = encrypter(email); let modified_ciphertext = ciphertext .iter() .take(32) .chain(admin_block.iter()) .map(|x| *x) .collect(); if !possibles .iter() .any(|possible| possible == &modified_ciphertext) { possibles.push(modified_ciphertext); } } return (email.to_owned(), possibles); }; let (block1, block2) = find_uid_role_blocks(); let admin_block = calculate_admin_block(block1, block2); return calculate_possible_admin_ciphertexts(admin_block); } pub fn crack_cbc_bitflipping(f: &F) -> Vec where F: Fn(&str) -> Vec, { let mut ciphertext = f("AAAAAAAAAAAAAAAA:admin( iv: &[u8], ciphertext: &[u8], f: &F, ) -> Vec where F: Fn(&[u8], &[u8]) -> bool, { let mut prev = iv; let mut plaintext = vec![]; for block in ciphertext.chunks(16) { let mut plaintext_block = vec![]; 'BYTE: for byte in 0..16u8 { for c_int in 0..256 { let c = (255 - c_int) as u8; let offset = (16 - byte - 1) as usize; let mut iv: Vec = prev.iter().take(offset).map(|x| *x).collect(); iv.push(prev[offset] ^ c ^ (byte + 1)); for i in 0..(byte as usize) { iv.push( prev[offset + i + 1] ^ plaintext_block[i] ^ (byte + 1), ); } if f(&iv[..], block) { plaintext_block.insert(0, c); continue 'BYTE; } } panic!("no byte found! ({})", byte); } for c in plaintext_block { plaintext.push(c); } prev = block; } return unpad_pkcs7(&plaintext[..]).unwrap().to_vec(); } pub fn crack_fixed_nonce_ctr_statistically( input: Vec>, ) -> Vec> { let min_len = input.iter().map(|line| line.len()).min().unwrap(); let max_len = input.iter().map(|line| line.len()).max().unwrap(); let mut plaintext_lines = vec![]; for _ in input.iter() { plaintext_lines.push(vec![]); } let mut full_key = vec![]; for len in min_len..(max_len + 1) { let mut idxs = vec![]; let ciphertext: Vec = input .iter() .enumerate() .filter(|&(idx, line)| { if line.len() >= len { idxs.push(idx); true } else { false } }) .flat_map(|(_, line)| line.iter().take(len)) .map(|x| *x) .collect(); let (key, _) = crack_repeating_key_xor_with_keysize(&ciphertext[..], len); for i in full_key.len()..key.len() { full_key.push(key[i]) } for idx in idxs { let line = repeating_key_xor(&input[idx][..], &full_key[..]) .iter() .take(full_key.len()) .map(|x| *x) .collect(); plaintext_lines[idx] = line; } } return plaintext_lines; } pub fn recover_mersenne_twister_seed_from_time(output: u32) -> Option { let now = time::now().to_timespec().sec as u32; for i in -10000..10000i32 { let seed = (now as i32).wrapping_add(i) as u32; let mut mt = MersenneTwister::from_u32(seed); let test_output: u32 = mt.gen(); if test_output == output { return Some(seed); } } return None; } pub fn clone_mersenne_twister_from_output( outputs: &[u32], ) -> MersenneTwister { fn untemper(val: u32) -> u32 { fn unxorshift(f: F, mut y: u32, n: usize, mask: u32) -> u32 where F: Fn(u32, usize) -> u32, { let mut a = y; for _ in 0..(32 / n) { y = f(y, n) & mask; a = a ^ y; } return a; } let mut y = val; y = unxorshift(|a, n| a >> n, y, 18, 0xffffffff); y = unxorshift(|a, n| a << n, y, 15, 0xefc60000); y = unxorshift(|a, n| a << n, y, 7, 0x9d2c5680); y = unxorshift(|a, n| a >> n, y, 11, 0xffffffff); y } let mut state = [0; 624]; for (i, &output) in outputs.iter().enumerate() { state[i] = untemper(output); } return MersenneTwister::from_state(state, 0); } pub fn recover_16_bit_mt19937_key( ciphertext: &[u8], suffix: &[u8], ) -> Option { for _key in 0..65536u32 { let key = _key as u16; let plaintext = crate::random::mt19937_stream_cipher(ciphertext, key as u32); if &plaintext[(ciphertext.len() - suffix.len())..] == suffix { return Some(key); } } return None; } pub fn recover_mt19937_key_from_time(token: &[u8]) -> Option { let now = time::now().to_timespec().sec as u32; for i in -500..500i32 { let seed = (now as i32).wrapping_add(i) as u32; let mut mt = MersenneTwister::from_u32(seed); let test_token: Vec = mt .sample_iter(&rand::distributions::Standard) .take(16) .collect(); if &test_token[..] == token { return Some(seed); } } return None; } pub fn crack_aes_128_ctr_random_access( ciphertext: &[u8], edit: F, ) -> Vec where F: Fn(&[u8], usize, &[u8]) -> Vec, { let empty_plaintext: Vec = std::iter::repeat(b'\x00').take(ciphertext.len()).collect(); let keystream = edit(ciphertext, 0, &empty_plaintext[..]); return fixed_xor(&keystream[..], ciphertext); } pub fn crack_ctr_bitflipping(f: &F) -> Vec where F: Fn(&str) -> Vec, { let ciphertext = f("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"); let replacement = fixed_xor(&ciphertext[32..44], b";admin=true;"); return ciphertext[..32] .iter() .chain(replacement.iter()) .chain(ciphertext[44..].iter()) .map(|x| *x) .collect(); } pub fn crack_cbc_iv_key(encrypt: &F1, verify: &F2) -> Vec where F1: Fn(&str) -> Vec, F2: Fn(&[u8]) -> Result>, { loop { let plaintext_bytes: Vec = rand::thread_rng() .sample_iter(&rand::distributions::Standard) .filter(|&c| c >= 32 && c < 127) .take(16 * 5) .collect(); let plaintext = std::str::from_utf8(&plaintext_bytes).unwrap(); let ciphertext = encrypt(plaintext); let modified_ciphertext: Vec = ciphertext[..16] .iter() .map(|x| *x) .chain(::std::iter::repeat(0).take(16)) .chain(ciphertext[..16].iter().map(|x| *x)) .chain(ciphertext[48..].iter().map(|x| *x)) .collect(); if let Err(modified_plaintext) = verify(&modified_ciphertext[..]) { let key = fixed_xor( &modified_plaintext[..16], &modified_plaintext[32..48], ); let desired_plaintext = b"comment1=cooking%20MCs;userdata=;admin=true;comment2=%20like%20a%20pound%20of%20bacon"; return encrypt_aes_128_cbc( desired_plaintext, &key[..], &key[..], ); } } } pub fn crack_sha1_mac_length_extension( input: &[u8], mac: [u8; 20], extension: &[u8], ) -> Vec<(Vec, [u8; 20])> { let mut sha1_state: [u32; 5] = unsafe { std::mem::transmute(mac) }; for word in sha1_state.iter_mut() { *word = u32::from_be(*word); } (0..100) .map(|i| { let new_input: Vec = input .iter() .chain( crate::sha1::sha1_padding(i + input.len() as u64).iter(), ) .chain(extension.iter()) .map(|x| *x) .collect(); let new_hash = crate::sha1::sha1_with_state( extension, sha1_state, i + new_input.len() as u64, ); (new_input, new_hash) }) .collect() } pub fn crack_md4_mac_length_extension( input: &[u8], mac: [u8; 16], extension: &[u8], ) -> Vec<(Vec, [u8; 16])> { let mut md4_state: [u32; 4] = unsafe { std::mem::transmute(mac) }; for word in md4_state.iter_mut() { *word = u32::from_le(*word); } (0..100) .map(|i| { let new_input: Vec = input .iter() .chain(crate::md4::md4_padding(i + input.len() as u64).iter()) .chain(extension.iter()) .map(|x| *x) .collect(); let new_hash = crate::md4::md4_with_state( extension, md4_state, i + new_input.len() as u64, ); (new_input, new_hash) }) .collect() } pub fn crack_hmac_timing_basic( _data: &str, request: impl Fn(&str) -> bool, ) -> Vec { let mut res = [0; 20]; for idx in 0..20 { let mut max_val = 0; let mut max_dur = 0; for i in 0..256 { let now = std::time::Instant::now(); res[idx] = i as u8; if request(&hex::encode(res)) { return res.to_vec(); } let dur = now.elapsed().as_micros(); if dur > max_dur { max_dur = dur; max_val = i; } } res[idx] = max_val as u8; } unreachable!() } fn crack_hmac_timing_advanced_rec( file: &str, request: &F, key: [u8; 20], idx: usize, timing_cutoff: u128, ) -> Option<(Vec<(u8, u128)>, [u8; 20])> where F: Sync + Send + Fn(&str) -> bool, { let get_timing_for = |i: u8| { let mut key = key.clone(); key[idx] = i; let guess = hex::encode(key); let mut params = std::collections::HashMap::new(); params.insert("file", file.to_string()); params.insert("signature", guess.to_string()); let uri = format!( "{}{}", "http://localhost:9000/?", crate::http::create_query_string(¶ms) ); let start = std::time::Instant::now(); let success = request(&uri); (success, start.elapsed().as_micros()) }; let initial_timings: Vec<_> = (0..256) .into_par_iter() .map(|i| (i as u8, get_timing_for(i as u8))) .collect(); for (i, (success, _)) in initial_timings.iter() { if *success { let mut key = key.clone(); key[idx] = *i; return Some((vec![], key)); } } let (_, (_, min_dur)) = initial_timings .iter() .cloned() .min_by_key(|(_, (_, dur))| *dur) .unwrap(); let mut timings: Vec<_> = (0..256) .into_par_iter() .map(|i| { let (_, (_, mut dur)) = initial_timings[i as usize]; let mut count = 0; while dur > min_dur + 2500 && count < 100 { let res = get_timing_for(i as u8); dur = res.1; count += 1; } (i as u8, dur) }) .collect(); timings.par_sort_by_key(|(_, dur)| *dur); // eprintln!( // "got timings for byte {} ranging from {} to {} (expected: >{})", // idx, timings[0].1, timings[255].1, timing_cutoff // ); if timings[0].1 < timing_cutoff { return None; } // if idx > 0 { // eprintln!("byte {} confirmed to be {}", idx - 1, key[idx - 1]); // } for (i, _dur) in timings.iter().rev() { let mut new_key = key.clone(); new_key[idx] = *i; // eprintln!("guessing that byte {} is {} (dur {})", idx, i, _dur); let rec = crack_hmac_timing_advanced_rec( file, request, new_key, idx + 1, timings[0].1 + 2500, ); if rec.is_some() { return rec; } } unreachable!() } pub fn crack_hmac_timing_advanced(file: &str, request: F) -> Vec where F: Sync + Send + Fn(&str) -> bool, { let pool = rayon::ThreadPoolBuilder::new() .num_threads(3) .build() .unwrap(); let key = pool.install(|| { let (_, key) = crack_hmac_timing_advanced_rec(file, &request, [0; 20], 0, 0) .unwrap(); key }); key.to_vec() } pub trait DiffieHellmanMessageExchanger { fn a_channel( &self, ) -> ( &crossbeam::channel::Sender>, &crossbeam::channel::Receiver>, ); fn b_channel( &self, ) -> ( &crossbeam::channel::Sender>, &crossbeam::channel::Receiver>, ); } pub struct NullDiffieHellmanMessageExchanger { a_sender: crossbeam::channel::Sender>, a_recver: crossbeam::channel::Receiver>, b_sender: crossbeam::channel::Sender>, b_recver: crossbeam::channel::Receiver>, } impl NullDiffieHellmanMessageExchanger { pub fn new() -> NullDiffieHellmanMessageExchanger { let (a_sender, b_recver) = crossbeam::channel::unbounded(); let (b_sender, a_recver) = crossbeam::channel::unbounded(); NullDiffieHellmanMessageExchanger { a_sender, a_recver, b_sender, b_recver, } } } impl DiffieHellmanMessageExchanger for NullDiffieHellmanMessageExchanger { fn a_channel( &self, ) -> ( &crossbeam::channel::Sender>, &crossbeam::channel::Receiver>, ) { (&self.a_sender, &self.a_recver) } fn b_channel( &self, ) -> ( &crossbeam::channel::Sender>, &crossbeam::channel::Receiver>, ) { (&self.b_sender, &self.b_recver) } } pub struct ParameterInjectionDiffieHellmanMessageExchanger { a_sender: crossbeam::channel::Sender>, a_recver: crossbeam::channel::Receiver>, b_sender: crossbeam::channel::Sender>, b_recver: crossbeam::channel::Receiver>, thread: std::thread::JoinHandle>, } impl ParameterInjectionDiffieHellmanMessageExchanger { pub fn new( inject_pg: F, inject_pubkey: G, generate_s: H, ) -> ParameterInjectionDiffieHellmanMessageExchanger where F: 'static + Send + Fn( num_bigint::BigUint, num_bigint::BigUint, ) -> (num_bigint::BigUint, num_bigint::BigUint), G: 'static + Send + Fn( num_bigint::BigUint, num_bigint::BigUint, num_bigint::BigUint, ) -> num_bigint::BigUint, H: 'static + Send + Fn( num_bigint::BigUint, num_bigint::BigUint, ) -> Vec, { let (a_sender, ma_recver) = crossbeam::channel::unbounded(); let (ma_sender, b_recver) = crossbeam::channel::unbounded(); let (b_sender, mb_recver) = crossbeam::channel::unbounded(); let (mb_sender, a_recver) = crossbeam::channel::unbounded(); let thread = std::thread::spawn(move || { let p_bytes: Vec = ma_recver.recv().unwrap(); let p: num_bigint::BigUint = serde_json::from_slice(&p_bytes).unwrap(); let g_bytes: Vec = ma_recver.recv().unwrap(); let g: num_bigint::BigUint = serde_json::from_slice(&g_bytes).unwrap(); let (modified_p, modified_g) = inject_pg(p.clone(), g.clone()); ma_sender .send(serde_json::to_vec(&modified_p).unwrap()) .unwrap(); ma_sender .send(serde_json::to_vec(&modified_g).unwrap()) .unwrap(); let p_bytes: Vec = mb_recver.recv().unwrap(); let p: num_bigint::BigUint = serde_json::from_slice(&p_bytes).unwrap(); let g_bytes: Vec = mb_recver.recv().unwrap(); let g: num_bigint::BigUint = serde_json::from_slice(&g_bytes).unwrap(); mb_sender .send(serde_json::to_vec(&modified_p).unwrap()) .unwrap(); mb_sender .send(serde_json::to_vec(&modified_g).unwrap()) .unwrap(); let possible_s = generate_s(modified_p.clone(), modified_g.clone()); let a_bytes: Vec = ma_recver.recv().unwrap(); let a_pubkey: num_bigint::BigUint = serde_json::from_slice(&a_bytes).unwrap(); let modified_pubkey_a = inject_pubkey(p.clone(), g.clone(), a_pubkey); ma_sender .send(serde_json::to_vec(&modified_pubkey_a).unwrap()) .unwrap(); let b_bytes: Vec = mb_recver.recv().unwrap(); let b_pubkey: num_bigint::BigUint = serde_json::from_slice(&b_bytes).unwrap(); let modified_pubkey_b = inject_pubkey(p.clone(), g.clone(), b_pubkey); mb_sender .send(serde_json::to_vec(&modified_pubkey_b).unwrap()) .unwrap(); let a_ciphertext = ma_recver.recv().unwrap(); ma_sender.send(a_ciphertext.clone()).unwrap(); let a_iv = ma_recver.recv().unwrap(); ma_sender.send(a_iv.clone()).unwrap(); let b_ciphertext = mb_recver.recv().unwrap(); mb_sender.send(b_ciphertext.clone()).unwrap(); let b_iv = mb_recver.recv().unwrap(); mb_sender.send(b_iv.clone()).unwrap(); for s in possible_s { let mut aes_key = crate::sha1::sha1(&s.to_bytes_le()).to_vec(); aes_key.truncate(16); let a_plaintext = crate::aes::decrypt_aes_128_cbc( &a_ciphertext, &aes_key, &a_iv, ); if let Some(a_plaintext) = a_plaintext { return a_plaintext; } } unreachable!() }); ParameterInjectionDiffieHellmanMessageExchanger { a_sender, a_recver, b_sender, b_recver, thread, } } pub fn retrieve_plaintext(self) -> Vec { self.thread.join().unwrap() } } impl DiffieHellmanMessageExchanger for ParameterInjectionDiffieHellmanMessageExchanger { fn a_channel( &self, ) -> ( &crossbeam::channel::Sender>, &crossbeam::channel::Receiver>, ) { (&self.a_sender, &self.a_recver) } fn b_channel( &self, ) -> ( &crossbeam::channel::Sender>, &crossbeam::channel::Receiver>, ) { (&self.b_sender, &self.b_recver) } } pub trait SRPClient { fn server(&mut self) -> &mut crate::dh::SRPServer; fn key_exchange_impl( &mut self, user: &str, pass: &str, ) -> (Vec, Vec, num_bigint::BigUint); fn register(&mut self, user: &str, pass: &str) { let n = &self.server().n.clone(); let g = &self.server().g.clone(); let mut salt = [0; 16]; rand::thread_rng().fill(&mut salt); let input = [&salt[..], pass.as_bytes()].concat(); let xh = crate::sha1::sha1(&input); let x = num_bigint::BigUint::from_bytes_le(&xh[..]); let v = g.modpow(&x, n); self.server().register(user, &salt, &v); } fn key_exchange( &mut self, user: &str, pass: &str, ) -> Option { let (session, salt, s) = self.key_exchange_impl(user, pass); let k = crate::sha1::sha1(&s.to_bytes_le()); let hmac = crate::sha1::sha1_hmac(&k, &salt); if !self.server().verify(session, hmac.to_vec()) { return None; } Some(s) } } #[derive(Debug)] pub struct CorrectSRPClient<'a> { server: &'a mut crate::dh::SRPServer, } impl<'a> CorrectSRPClient<'a> { pub fn new(server: &'a mut crate::dh::SRPServer) -> CorrectSRPClient<'a> { CorrectSRPClient { server } } } impl<'a> SRPClient for CorrectSRPClient<'a> { fn server(&mut self) -> &mut crate::dh::SRPServer { self.server } fn key_exchange_impl( &mut self, user: &str, pass: &str, ) -> (Vec, Vec, num_bigint::BigUint) { let n = &self.server.n.clone(); let g = &self.server.g.clone(); let k = &self.server.k.clone(); let a_priv = rand::thread_rng().gen_biguint_below(n); let a_pub = g.modpow(&a_priv, n); let (session, salt, b_pub) = self.server.exchange_pubkeys(user, &a_pub); let uinput = [a_pub.to_bytes_le(), b_pub.to_bytes_le()].concat(); let uh = crate::sha1::sha1(&uinput); let u = num_bigint::BigUint::from_bytes_le(&uh[..]); let xinput = [salt.clone(), pass.as_bytes().to_vec()].concat(); let xh = crate::sha1::sha1(&xinput); let x = num_bigint::BigUint::from_bytes_le(&xh[..]); let s = (b_pub - k * g.modpow(&x, n)).modpow(&(a_priv + u * x), n); (session, salt, s) } } #[derive(Debug)] pub struct ZeroKeySRPClient<'a> { server: &'a mut crate::dh::SRPServer, } impl<'a> ZeroKeySRPClient<'a> { pub fn new(server: &'a mut crate::dh::SRPServer) -> ZeroKeySRPClient<'a> { ZeroKeySRPClient { server } } } impl<'a> SRPClient for ZeroKeySRPClient<'a> { fn server(&mut self) -> &mut crate::dh::SRPServer { self.server } fn key_exchange_impl( &mut self, user: &str, _: &str, ) -> (Vec, Vec, num_bigint::BigUint) { let a_pub = num_bigint::BigUint::from(0 as u8); let (session, salt, _b_pub) = self.server.exchange_pubkeys(user, &a_pub); let s = num_bigint::BigUint::from(0 as u8); (session, salt, s) } } fn crack_single_byte_xor_with_confidence(input: &[u8]) -> (u8, f64) { let mut min_diff = 100.0; let mut best_key = 0; for a in 0..256u16 { let decrypted = fixed_xor( input, &::std::iter::repeat(a as u8) .take(input.len()) .collect::>()[..], ); if !decrypted.is_ascii() { continue; } if decrypted .iter() .any(|&c| c != b'\n' && (c < 0x20 || c > 0x7E)) { continue; } let lowercase = decrypted.to_ascii_lowercase(); let mut frequencies = [0; 26]; let mut total_frequency = 0; let mut extra_frequencies = 0; for c in lowercase { total_frequency += 1; if c >= 0x61 && c <= 0x7A { frequencies[(c - 0x61) as usize] += 1; } else { extra_frequencies += 1; } } let mut total_diff = 0.0; for (&english, &crypt) in ENGLISH_FREQUENCIES.iter().zip(frequencies.iter()) { let relative_frequency = (crypt as f64) / (total_frequency as f64); total_diff += (english - relative_frequency).abs(); } total_diff += (extra_frequencies as f64) / (total_frequency as f64); if total_diff < min_diff { min_diff = total_diff; best_key = a as u8; } } return (best_key, min_diff); } fn crack_repeating_key_xor_with_keysize( input: &[u8], keysize: usize, ) -> (Vec, f64) { let strides: Vec> = (0..keysize) .map(|n| { // XXX sigh ): let mut elts = vec![]; for (i, &c) in input.iter().enumerate() { if i % keysize == n { elts.push(c); } } elts }) .collect(); let cracked: Vec<(u8, f64)> = strides .iter() .map(|input| crack_single_byte_xor_with_confidence(input)) .collect(); let diff = cracked .iter() .map(|&(_, diff)| diff) .fold(0.0, |acc, x| acc + x); let key = cracked.iter().map(|&(c, _)| c).collect(); return (key, diff / (keysize as f64)); } fn count_duplicate_blocks(input: &[u8], block_size: usize) -> usize { let mut set = HashSet::new(); let mut dups = 0; for block in input.chunks(block_size) { if !set.insert(block) { dups += 1; } } return dups; } fn find_block_size(f: &F) -> usize where F: Fn(&[u8]) -> Vec, { let (block_size, _) = find_block_size_and_fixed_prefix_len(f); return block_size; } fn find_block_size_and_fixed_prefix_len(f: &F) -> (usize, usize) where F: Fn(&[u8]) -> Vec, { let fixed_prefix_len = find_fixed_block_prefix_len(f); let byte = b'A'; let mut prev = f(&[b'f']); let mut len = 0; loop { let prefix: Vec = std::iter::repeat(byte).take(len).collect(); let next = f(&prefix[..]); let prefix_len = shared_prefix_len(prev.iter(), next.iter()); if prefix_len > fixed_prefix_len { let block_size = prefix_len - fixed_prefix_len; return (block_size, fixed_prefix_len + block_size - (len - 1)); } prev = next; len += 1; } } fn find_fixed_block_prefix_len(f: &F) -> usize where F: Fn(&[u8]) -> Vec, { let ciphertext1 = f(b""); let ciphertext2 = f(b"A"); return shared_prefix_len(ciphertext1.iter(), ciphertext2.iter()); } fn shared_prefix_len(i1: I, i2: I) -> usize where I: Iterator, ::Item: PartialEq, { return i1.zip(i2).take_while(|&(ref c1, ref c2)| c1 == c2).count(); }