Implement challenge 34
parent
9e568ac97c
commit
ad3a292e0e
|
@ -83,7 +83,7 @@ impl Bytes {
|
||||||
|
|
||||||
pub fn pad_pkcs7(&mut self, block_size: usize) {
|
pub fn pad_pkcs7(&mut self, block_size: usize) {
|
||||||
let Bytes(v) = self;
|
let Bytes(v) = self;
|
||||||
let padding_value = (block_size - v.len() % block_size)
|
let padding_value = (block_size - (v.len() % block_size))
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("Padding value has to be an u8");
|
.expect("Padding value has to be an u8");
|
||||||
for _ in 0..padding_value {
|
for _ in 0..padding_value {
|
||||||
|
|
32
src/rsa.rs
32
src/rsa.rs
|
@ -1,12 +1,26 @@
|
||||||
pub fn expmod(base: u32, exp: u32, m: u32) -> u32 {
|
use num_bigint::BigUint;
|
||||||
if exp == 0 {
|
use num_bigint::RandBigInt;
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if exp % 2 == 0 {
|
#[derive(Clone)]
|
||||||
let s = expmod(base, exp / 2, m);
|
pub struct PublicKey(pub BigUint);
|
||||||
(s * s) % m
|
|
||||||
} else {
|
#[derive(Clone)]
|
||||||
(expmod(base, exp - 1, m) * base) % m
|
pub struct PrivateKey(pub BigUint);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Keypair {
|
||||||
|
pub private: PrivateKey,
|
||||||
|
pub public: PublicKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Keypair {
|
||||||
|
pub fn make(p: &BigUint, g: &BigUint) -> Self {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let private = rng.gen_biguint_below(p);
|
||||||
|
let public = g.modpow(&private, p);
|
||||||
|
Self {
|
||||||
|
private: PrivateKey(private),
|
||||||
|
public: PublicKey(public),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
225
src/set5.rs
225
src/set5.rs
|
@ -1,12 +1,41 @@
|
||||||
|
use crate::bytes::Bytes;
|
||||||
|
use crate::cbc;
|
||||||
use crate::rsa;
|
use crate::rsa;
|
||||||
use num_bigint::{BigUint, RandBigInt, ToBigUint};
|
use crate::sha1::Sha1;
|
||||||
|
use num_bigint::ToBigUint;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use std::fs;
|
|
||||||
|
mod challenge33 {
|
||||||
|
use num_bigint::BigUint;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
pub fn load_large_prime() -> BigUint {
|
||||||
|
let s = fs::read_to_string("data/33.txt").expect("run from git root dir");
|
||||||
|
let b: Vec<u8> = s
|
||||||
|
.chars()
|
||||||
|
.filter(|c| *c != '\n')
|
||||||
|
.map(|c| c.to_digit(16).unwrap().try_into().unwrap())
|
||||||
|
.collect();
|
||||||
|
BigUint::from_radix_be(&b, 16).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn challenge33() {
|
pub fn challenge33() {
|
||||||
#![allow(non_snake_case)]
|
fn expmod(base: u32, exp: u32, m: u32) -> u32 {
|
||||||
|
if exp == 0 {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if exp % 2 == 0 {
|
||||||
|
let s = expmod(base, exp / 2, m);
|
||||||
|
(s * s) % m
|
||||||
|
} else {
|
||||||
|
(expmod(base, exp - 1, m) * base) % m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn simple_diffie_hellman() {
|
fn simple_diffie_hellman() {
|
||||||
|
#![allow(non_snake_case)]
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
// Set a variable "p" to 37 and "g" to 5.
|
// Set a variable "p" to 37 and "g" to 5.
|
||||||
|
@ -16,18 +45,18 @@ pub fn challenge33() {
|
||||||
// Generate "a", a random number mod 37. Now generate "A", which is "g" raised to the "a"
|
// Generate "a", a random number mod 37. Now generate "A", which is "g" raised to the "a"
|
||||||
// power mode 37 --- A = (g**a) % p.
|
// power mode 37 --- A = (g**a) % p.
|
||||||
let a: u32 = rng.gen::<u32>() % p;
|
let a: u32 = rng.gen::<u32>() % p;
|
||||||
let A = rsa::expmod(g, a, p);
|
let A = expmod(g, a, p);
|
||||||
|
|
||||||
// Do the same for "b" and "B".
|
// Do the same for "b" and "B".
|
||||||
let b: u32 = rng.gen::<u32>() % p;
|
let b: u32 = rng.gen::<u32>() % p;
|
||||||
let B = rsa::expmod(g, b, p);
|
let B = expmod(g, b, p);
|
||||||
|
|
||||||
// "A" and "B" are public keys. Generate a session key with them; set "s" to "B" raised to
|
// "A" and "B" are public keys. Generate a session key with them; set "s" to "B" raised to
|
||||||
// the "a" power mod 37 --- s = (B**a) % p.
|
// the "a" power mod 37 --- s = (B**a) % p.
|
||||||
let s = rsa::expmod(B, a, p);
|
let s = expmod(B, a, p);
|
||||||
|
|
||||||
// Do the same with A**b, check that you come up with the same "s".
|
// Do the same with A**b, check that you come up with the same "s".
|
||||||
let s_ = rsa::expmod(A, b, p);
|
let s_ = expmod(A, b, p);
|
||||||
|
|
||||||
assert_eq!(s, s_, "crypto is broken");
|
assert_eq!(s, s_, "crypto is broken");
|
||||||
// To turn "s" into a key, you can just hash it to create 128 bits of key material (or
|
// To turn "s" into a key, you can just hash it to create 128 bits of key material (or
|
||||||
|
@ -35,25 +64,14 @@ pub fn challenge33() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serious_diffie_hellman() {
|
fn serious_diffie_hellman() {
|
||||||
let mut rng = rand::thread_rng();
|
let p = challenge33::load_large_prime();
|
||||||
|
|
||||||
let s = fs::read_to_string("data/33.txt").expect("run from git root dir");
|
|
||||||
let b: Vec<u8> = s
|
|
||||||
.chars()
|
|
||||||
.filter(|c| *c != '\n')
|
|
||||||
.map(|c| c.to_digit(16).unwrap().try_into().unwrap())
|
|
||||||
.collect();
|
|
||||||
let p = BigUint::from_radix_be(&b, 16).unwrap();
|
|
||||||
let g = 2_u8.to_biguint().unwrap();
|
let g = 2_u8.to_biguint().unwrap();
|
||||||
|
|
||||||
let a = rng.gen_biguint_below(&p);
|
let a = rsa::Keypair::make(&p, &g);
|
||||||
let A = g.modpow(&a, &p);
|
let b = rsa::Keypair::make(&p, &g);
|
||||||
|
|
||||||
let b = rng.gen_biguint_below(&p);
|
let s = b.public.0.modpow(&a.private.0, &p);
|
||||||
let B = g.modpow(&b, &p);
|
let s_ = a.public.0.modpow(&b.private.0, &p);
|
||||||
|
|
||||||
let s = B.modpow(&a, &p);
|
|
||||||
let s_ = A.modpow(&b, &p);
|
|
||||||
assert_eq!(s, s_, "crypto is broken");
|
assert_eq!(s, s_, "crypto is broken");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +80,163 @@ pub fn challenge33() {
|
||||||
println!("[okay] Challenge 33: implemented Diffie-Hellman");
|
println!("[okay] Challenge 33: implemented Diffie-Hellman");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn challenge34() {
|
mod challenge34 {
|
||||||
println!("[xxxx] Challenge 34: tbd");
|
use crate::bytes::Bytes;
|
||||||
|
use crate::cbc;
|
||||||
|
use crate::rsa;
|
||||||
|
use crate::sha1::Sha1;
|
||||||
|
use num_bigint::BigUint;
|
||||||
|
|
||||||
|
pub struct Bot {
|
||||||
|
p: BigUint,
|
||||||
|
keypair: rsa::Keypair,
|
||||||
|
pub s: Option<BigUint>,
|
||||||
|
pub key: Option<Bytes>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bot {
|
||||||
|
pub fn new(p: BigUint, g: &BigUint) -> Self {
|
||||||
|
let keypair = rsa::Keypair::make(&p, g);
|
||||||
|
Bot {
|
||||||
|
p: p,
|
||||||
|
keypair: keypair,
|
||||||
|
s: None,
|
||||||
|
key: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_public_key(&self) -> rsa::PublicKey {
|
||||||
|
self.keypair.public.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exchange_keys(&mut self, public: &rsa::PublicKey) -> rsa::PublicKey {
|
||||||
|
let s = public.0.modpow(&self.keypair.private.0, &self.p);
|
||||||
|
self.s = Some(s);
|
||||||
|
self.make_encryption_key();
|
||||||
|
self.keypair.public.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_encryption_key(&mut self) {
|
||||||
|
assert!(self.s.is_some(), "keys have not been exchanged");
|
||||||
|
if self.key.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut sha1 = Sha1::default();
|
||||||
|
let key = &sha1.hash(&Bytes(self.s.clone().unwrap().to_bytes_be())).0[0..16];
|
||||||
|
self.key = Some(Bytes(key.to_vec()));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encrypt(&mut self, message: &Bytes) -> Bytes {
|
||||||
|
assert!(self.key.is_some(), "keys have not been exchanged");
|
||||||
|
|
||||||
|
const BLOCK_SIZE: usize = 16;
|
||||||
|
let key = self.key.as_ref().unwrap();
|
||||||
|
let mut iv = Bytes::random(16);
|
||||||
|
let mut message = Bytes(message.0.clone());
|
||||||
|
message.pad_pkcs7(BLOCK_SIZE);
|
||||||
|
|
||||||
|
// AES-CBC(SHA1(s)[0:16], iv=random(16), msg) + iv
|
||||||
|
let mut cipher = cbc::encrypt(&key, &iv, &message).0;
|
||||||
|
cipher.append(&mut iv.0);
|
||||||
|
Bytes(cipher)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decrypt(&mut self, cipher: &Bytes) -> Bytes {
|
||||||
|
const BLOCK_SIZE: usize = 16;
|
||||||
|
assert!(self.key.is_some(), "keys have not been exchanged");
|
||||||
|
|
||||||
|
let key = self.key.as_ref().unwrap();
|
||||||
|
let cipher_len = cipher.len();
|
||||||
|
|
||||||
|
let iv = Bytes(cipher.0[(cipher_len - BLOCK_SIZE)..cipher_len].to_vec());
|
||||||
|
let cipher = Bytes(cipher.0[0..cipher_len - BLOCK_SIZE].to_vec());
|
||||||
|
let mut message = cbc::decrypt(&key, &iv, &cipher);
|
||||||
|
message.remove_pkcs7(BLOCK_SIZE);
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn challenge34() {
|
||||||
|
fn echo() {
|
||||||
|
let p = challenge33::load_large_prime();
|
||||||
|
let g = 2_u8.to_biguint().unwrap();
|
||||||
|
|
||||||
|
let mut a = challenge34::Bot::new(p.clone(), &g);
|
||||||
|
|
||||||
|
// A->B: Send "p", "g", "A"
|
||||||
|
let mut b = challenge34::Bot::new(p.clone(), &g);
|
||||||
|
|
||||||
|
// B->A: Send "B"
|
||||||
|
a.exchange_keys(&b.exchange_keys(&a.get_public_key()));
|
||||||
|
assert_eq!(a.s, b.s, "crypto is broken");
|
||||||
|
|
||||||
|
// A->B: Send AES-CBC(SHA1(s)[0:16], iv=random(16), msg) + iv
|
||||||
|
let message_a = Bytes::from_utf8("Will this work? Let's make it longer.");
|
||||||
|
let cipher_a = a.encrypt(&message_a);
|
||||||
|
|
||||||
|
// B->A: Send AES-CBC(SHA1(s)[0:16], iv=random(16), A's msg) + iv
|
||||||
|
let message_b = b.decrypt(&cipher_a);
|
||||||
|
let cipher_b = b.encrypt(&message_b);
|
||||||
|
let roundtrip = a.decrypt(&cipher_b);
|
||||||
|
assert_eq!(message_a, roundtrip, "we broke it fam");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn echo_mitm() {
|
||||||
|
let p = challenge33::load_large_prime();
|
||||||
|
let g = 2_u8.to_biguint().unwrap();
|
||||||
|
let p_public = rsa::PublicKey(p.clone());
|
||||||
|
|
||||||
|
// A->M Send "p", "g", "A"
|
||||||
|
let mut a = challenge34::Bot::new(p.clone(), &g);
|
||||||
|
let mut m = challenge34::Bot::new(p.clone(), &g);
|
||||||
|
m.exchange_keys(&a.get_public_key());
|
||||||
|
|
||||||
|
// M->B Send "p", "g", "p"
|
||||||
|
// B->M Send "B"
|
||||||
|
let mut b = challenge34::Bot::new(p.clone(), &g);
|
||||||
|
b.exchange_keys(&p_public);
|
||||||
|
|
||||||
|
// M->A Send "p"
|
||||||
|
a.exchange_keys(&p_public);
|
||||||
|
|
||||||
|
// A->M Send AES-CBC(SHA1(s)[0:16], iv=random(16), msg) + iv
|
||||||
|
// M->B Relay that to B
|
||||||
|
let message_a = Bytes::from_utf8("Will this work again? I don't doubt it.");
|
||||||
|
let cipher_a = a.encrypt(&message_a);
|
||||||
|
|
||||||
|
// B->M Send AES-CBC(SHA1(s)[0:16], iv=random(16), A's msg) + iv
|
||||||
|
let message_b = b.decrypt(&cipher_a);
|
||||||
|
let cipher_b = b.encrypt(&message_b);
|
||||||
|
|
||||||
|
// M->A Relay that to A
|
||||||
|
let roundtrip = a.decrypt(&cipher_b);
|
||||||
|
assert_eq!(message_a, roundtrip, "we broke it fam");
|
||||||
|
|
||||||
|
let restored_a = attack(&cipher_a);
|
||||||
|
let restored_b = attack(&cipher_b);
|
||||||
|
assert_eq!(message_a, restored_a, "attack didn't work");
|
||||||
|
assert_eq!(message_b, restored_b, "attack didn't work");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attack(cipher: &Bytes) -> Bytes {
|
||||||
|
const BLOCK_SIZE: usize = 16;
|
||||||
|
|
||||||
|
// Do the DH math on this quickly to see what that does to the predictability of the key.
|
||||||
|
// Math: with p = A; s = (A ** b) % p becomes (p ** b) % p = 0 => s = 0
|
||||||
|
let s = 0_u8.to_biguint().unwrap();
|
||||||
|
let mut sha1 = Sha1::default();
|
||||||
|
let key = Bytes(sha1.hash(&Bytes(s.to_bytes_be())).0[0..16].to_vec());
|
||||||
|
|
||||||
|
let cipher_len = cipher.len();
|
||||||
|
let iv = Bytes(cipher.0[(cipher_len - BLOCK_SIZE)..cipher_len].to_vec());
|
||||||
|
let cipher = Bytes(cipher.0[0..cipher_len - BLOCK_SIZE].to_vec());
|
||||||
|
let mut message = cbc::decrypt(&key, &iv, &cipher);
|
||||||
|
message.remove_pkcs7(BLOCK_SIZE);
|
||||||
|
message
|
||||||
|
}
|
||||||
|
|
||||||
|
echo();
|
||||||
|
echo_mitm();
|
||||||
|
println!("[okay] Challenge 34: implement MITM key-fixing attack on DH");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue