Refactor challenge 36 to match SRP protocol better
This commit is contained in:
@@ -4,7 +4,6 @@
|
||||
#![allow(clippy::items_after_statements)]
|
||||
#![allow(clippy::many_single_char_names)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
#![allow(clippy::unnested_or_patterns)]
|
||||
mod bytes;
|
||||
mod bytes_base64;
|
||||
mod cbc;
|
||||
@@ -21,6 +20,7 @@ mod set3;
|
||||
mod set4;
|
||||
mod set5;
|
||||
mod sha1;
|
||||
mod srp;
|
||||
mod utils;
|
||||
|
||||
fn main() {
|
||||
|
||||
@@ -17,8 +17,7 @@ pub fn parse_key_value(text: &str) -> HashMap<String, String> {
|
||||
tokens = scan(text, 0, tokens);
|
||||
for token_chunk in tokens.chunks(4) {
|
||||
match token_chunk {
|
||||
[Token::Identifier(key), Token::Equal, Token::Identifier(value), Token::Ampersand]
|
||||
| [Token::Identifier(key), Token::Equal, Token::Identifier(value), Token::Semicolon]
|
||||
[Token::Identifier(key), Token::Equal, Token::Identifier(value), Token::Ampersand | Token::Semicolon]
|
||||
| [Token::Identifier(key), Token::Equal, Token::Identifier(value)] => {
|
||||
result.insert(key.to_string(), value.to_string());
|
||||
}
|
||||
|
||||
89
src/set5.rs
89
src/set5.rs
@@ -1,13 +1,14 @@
|
||||
use crate::bytes::Bytes;
|
||||
use crate::rsa;
|
||||
use crate::sha1;
|
||||
use crate::srp;
|
||||
use num_bigint::BigUint;
|
||||
use num_bigint::RandBigInt;
|
||||
use num_bigint::ToBigUint;
|
||||
use openssl::sha::sha256;
|
||||
use rand::Rng;
|
||||
|
||||
mod challenge33 {
|
||||
pub mod challenge33 {
|
||||
use num_bigint::BigUint;
|
||||
use std::fs;
|
||||
|
||||
@@ -317,80 +318,46 @@ pub fn challenge35() {
|
||||
println!("[okay] Challenge 35: implement MITM with malicious g attack on DH");
|
||||
}
|
||||
|
||||
pub fn challenge36() {
|
||||
pub fn challenge36() -> Option<()> {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
// C & S
|
||||
// Agree on N=[NIST Prime], g=2, k=3, I (email), P (password)
|
||||
let n = challenge33::load_large_prime();
|
||||
let g = 2_u8.to_biguint().unwrap();
|
||||
let k = 3_u8.to_biguint().unwrap();
|
||||
let _i = Bytes::from_utf8("john1337@wayne.com");
|
||||
let p = Bytes::from_utf8("horse planet carpet country");
|
||||
let mut server = srp::Server::default();
|
||||
let email = "john1337@wayne.com";
|
||||
let password = "horse planet carpet country";
|
||||
let parameters = server.register(email, password)?;
|
||||
|
||||
// S
|
||||
// Generate salt as random integer
|
||||
// Generate string xH=SHA256(salt|password)
|
||||
// Convert xH to integer x somehow (put 0x on hexdigest)
|
||||
// Generate v=g**x % N
|
||||
// Save everything but x, xH
|
||||
let salt: u32 = rng.gen();
|
||||
let mut salt_password = salt.to_be_bytes().to_vec();
|
||||
salt_password.append(&mut p.0.clone());
|
||||
let xh = sha256(&salt_password);
|
||||
let x = BigUint::from_bytes_be(xh[0..4].try_into().unwrap());
|
||||
let v = g.modpow(&x, &n);
|
||||
// Establish session
|
||||
let n = ¶meters.n;
|
||||
let g = ¶meters.g;
|
||||
let a = rng.gen_biguint_below(n);
|
||||
let a_public = g.modpow(&a, n);
|
||||
let session_keys = server.exchange(email, &a_public)?;
|
||||
|
||||
// C->S
|
||||
// Send I, A=g**a % N (a la Diffie Hellman)
|
||||
let a = rng.gen_biguint_below(&n);
|
||||
let a_public = g.modpow(&a, &n);
|
||||
|
||||
// S->C
|
||||
// Send salt, B=kv + g**b % N
|
||||
let b = rng.gen_biguint_below(&n);
|
||||
let b_public = k.clone() * v.clone() + g.modpow(&b, &n);
|
||||
|
||||
// S, C
|
||||
// Compute string uH = SHA256(A|B), u = integer of uH
|
||||
let mut a_b = a_public.to_bytes_be();
|
||||
a_b.append(&mut b_public.to_bytes_be());
|
||||
let uh = sha256(&a_b);
|
||||
let u = BigUint::from_bytes_be(uh[0..4].try_into().unwrap());
|
||||
|
||||
// C
|
||||
// Generate string xH=SHA256(salt|password)
|
||||
// Convert xH to integer x somehow (put 0x on hexdigest)
|
||||
// Generate S = (B - k * g**x)**(a + u * x) % N
|
||||
// Generate K = SHA256(S)
|
||||
let mut salt_password = salt.to_be_bytes().to_vec();
|
||||
salt_password.append(&mut p.0.clone());
|
||||
let xh = sha256(&salt_password);
|
||||
let x = BigUint::from_bytes_be(xh[0..4].try_into().unwrap());
|
||||
let s = (b_public - k * g.modpow(&x, &n)).modpow(&(a + &u * x), &n);
|
||||
let k_client = sha256(&s.to_bytes_be());
|
||||
|
||||
// S
|
||||
// Generate S = (A * v**u) ** b % N
|
||||
// Generate K = SHA256(S)
|
||||
let s = (a_public * v.modpow(&u, &n)).modpow(&b, &n);
|
||||
let k_server = sha256(&s.to_bytes_be());
|
||||
// Client
|
||||
// Generate S = (B - k * g**x)**(a + u * x) % N
|
||||
// Generate K = SHA256(S)
|
||||
let u = srp::compute_u(&a_public, &session_keys.b_public)?;
|
||||
let x = srp::hash_password(session_keys.salt, password)?;
|
||||
let b_public = &session_keys.b_public;
|
||||
let k = ¶meters.k;
|
||||
let s_client = (b_public - ((k * g.modpow(&x, n)) % n)).modpow(&(a + &u * x), n);
|
||||
let k_client = sha256(&s_client.to_bytes_be());
|
||||
|
||||
// Server
|
||||
let k_server = server.get_k(email)?;
|
||||
assert_eq!(k_client, k_server);
|
||||
|
||||
// I don't have HMAC-SHA256, so I will use HMAC-SHA1 instead.
|
||||
|
||||
// C->S
|
||||
// Send HMAC-SHA256(K, salt)
|
||||
let salt = Bytes(salt.to_be_bytes().to_vec());
|
||||
// C->S Send HMAC-SHA256(K, salt)
|
||||
let salt = Bytes(session_keys.salt.to_be_bytes().to_vec());
|
||||
let mac_server = sha1::hmac_sha1(&Bytes(k_server.to_vec()), &salt);
|
||||
|
||||
// S->C
|
||||
// Send "OK" if HMAC-SHA256(K, salt) validates
|
||||
// S->C Send "OK" if HMAC-SHA256(K, salt) validates
|
||||
let mac_client = sha1::hmac_sha1(&Bytes(k_client.to_vec()), &salt);
|
||||
assert_eq!(mac_server, mac_client, "HMAC verification failed");
|
||||
|
||||
println!("[okay] Challenge 36: implement secure remote password");
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub fn challenge37() {
|
||||
|
||||
125
src/srp.rs
Normal file
125
src/srp.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
use crate::set5;
|
||||
use num_bigint::BigUint;
|
||||
use num_bigint::RandBigInt;
|
||||
use num_bigint::ToBigUint;
|
||||
use openssl::sha::sha256;
|
||||
use rand::Rng;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Parameters {
|
||||
pub n: BigUint,
|
||||
pub g: BigUint,
|
||||
pub k: BigUint,
|
||||
}
|
||||
|
||||
pub struct SessionKeys {
|
||||
pub salt: u32,
|
||||
pub b_public: BigUint,
|
||||
}
|
||||
|
||||
struct PasswordEntry {
|
||||
salt: u32,
|
||||
n: BigUint,
|
||||
g: BigUint,
|
||||
k: BigUint,
|
||||
v: BigUint,
|
||||
a_public: Option<BigUint>,
|
||||
b: Option<BigUint>,
|
||||
b_public: Option<BigUint>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Server {
|
||||
entries: HashMap<String, PasswordEntry>,
|
||||
}
|
||||
|
||||
pub fn compute_u(a_public: &BigUint, b_public: &BigUint) -> Option<BigUint> {
|
||||
let mut a_b = a_public.to_bytes_be();
|
||||
a_b.append(&mut b_public.to_bytes_be());
|
||||
let uh = sha256(&a_b);
|
||||
let u = BigUint::from_bytes_be(uh[0..4].try_into().ok()?);
|
||||
Some(u)
|
||||
}
|
||||
|
||||
pub fn hash_password(salt: u32, password: &str) -> Option<BigUint> {
|
||||
// Generate string xH=SHA256(salt|password)
|
||||
// Convert xH to integer x somehow (put 0x on hexdigest)
|
||||
let mut salt_password: Vec<u8> = salt.to_be_bytes().to_vec();
|
||||
salt_password.append(&mut password.as_bytes().to_vec());
|
||||
let xh = sha256(&salt_password);
|
||||
let x = BigUint::from_bytes_be(xh[0..4].try_into().ok()?);
|
||||
Some(x)
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn register(&mut self, email: &str, password: &str) -> Option<Parameters> {
|
||||
if self.entries.contains_key(email) {
|
||||
return None;
|
||||
}
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
// Agree on N=[NIST Prime], g=2, k=3
|
||||
let n = set5::challenge33::load_large_prime();
|
||||
let g = 2_u8.to_biguint()?;
|
||||
let k = 3_u8.to_biguint()?;
|
||||
|
||||
// Generate salt as random integer
|
||||
let salt: u32 = rng.gen();
|
||||
let x = hash_password(salt, password)?;
|
||||
|
||||
// Generate v=g**x % N
|
||||
// Save everything but x, xH
|
||||
let v = g.modpow(&x, &n);
|
||||
|
||||
let password_entry = PasswordEntry {
|
||||
salt,
|
||||
n: n.clone(),
|
||||
g: g.clone(),
|
||||
k: k.clone(),
|
||||
v,
|
||||
a_public: None,
|
||||
b: None,
|
||||
b_public: None,
|
||||
};
|
||||
self.entries.insert(email.to_string(), password_entry);
|
||||
Some(Parameters { n, g, k })
|
||||
}
|
||||
|
||||
pub fn exchange(&mut self, email: &str, a_public: &BigUint) -> Option<SessionKeys> {
|
||||
// C->S Send I, A=g**a % N (a la Diffie Hellman)
|
||||
let mut password_entry = self.entries.get_mut(email)?;
|
||||
password_entry.a_public = Some(a_public.clone());
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let n = &password_entry.n;
|
||||
let k = &password_entry.k;
|
||||
let v = &password_entry.v;
|
||||
let g = &password_entry.g;
|
||||
|
||||
// S->C Send salt, B=kv + g**b % N
|
||||
let b = rng.gen_biguint_below(n);
|
||||
let b_public = (k.clone() * v.clone() % n) + g.modpow(&b, n);
|
||||
|
||||
password_entry.b = Some(b);
|
||||
password_entry.b_public = Some(b_public.clone());
|
||||
Some(SessionKeys {
|
||||
salt: password_entry.salt,
|
||||
b_public,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_k(&mut self, email: &str) -> Option<[u8; 32]> {
|
||||
let password_entry = self.entries.get(email)?;
|
||||
let a_public = password_entry.a_public.as_ref()?;
|
||||
let u = compute_u(a_public, password_entry.b_public.as_ref()?)?;
|
||||
let n = &password_entry.n;
|
||||
let v = &password_entry.v;
|
||||
let b = &password_entry.b.as_ref()?;
|
||||
|
||||
// Generate S = (A * v**u) ** b % N
|
||||
// Generate K = SHA256(S)
|
||||
let s_server = (a_public * v.modpow(&u, n)).modpow(b, n);
|
||||
let k_server = sha256(&s_server.to_bytes_be());
|
||||
Some(k_server)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user