diff --git a/src/main.rs b/src/main.rs index ec6b617..4962ca6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -64,4 +64,5 @@ fn main() { } set5::challenge36(); set5::challenge37(); + set5::challenge38(); } diff --git a/src/set5.rs b/src/set5.rs index b01494e..dc19384 100644 --- a/src/set5.rs +++ b/src/set5.rs @@ -350,23 +350,84 @@ pub fn challenge36() -> Option<()> { // I don't have HMAC-SHA256, so I will use HMAC-SHA1 instead. // 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 - let mac_client = sha1::hmac_sha1(&Bytes(k_client.to_vec()), &salt); - assert_eq!(mac_server, mac_client, "HMAC verification failed"); + let mac = sha1::hmac_sha1(&Bytes(k_client.to_vec()), &salt); + assert!(server.log_in(email, &mac)?); println!("[okay] Challenge 36: implement secure remote password"); Some(()) } -pub fn challenge37() { - // Get your SRP working in an actual client-server setting. "Log in" with a - // valid password using the protocol. +pub fn challenge37() -> Option<()> { + let mut rng = rand::thread_rng(); - // Now log in without your password by having the client send 0 as its "A" - // value. What does this to the "S" value that both sides compute? + let mut server = srp::Server::default(); + let email = "john1337@wayne.com"; + let password = "horse planet carpet country"; + let parameters = server.register(email, password)?; - // Now log in without your password by having the client send N, N*2, &c. - println!("[xxxx] Challenge 37: TBD"); + let mut log_in_with_password = || -> Option { + // Get your SRP working in an actual client-server setting. "Log in" with a + // valid password using the protocol. + 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)?; + + // Client + 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()); + + let salt = Bytes(session_keys.salt.to_be_bytes().to_vec()); + let mac = sha1::hmac_sha1(&Bytes(k_client.to_vec()), &salt); + server.log_in(email, &mac) + }; + + assert!(log_in_with_password()?); + + let mut log_in_without_password_a_zero = || -> Option { + // Now log in without your password by having the client send 0 as its "A" + // value. + let a_public = 0_u8.to_biguint()?; + let session_keys = server.exchange(email, &a_public)?; + + // What does this to the "S" value that both sides compute? It becomes zero. + let s_client: BigUint = 0_u8.to_biguint()?; + let k_client = sha256(&s_client.to_bytes_be()); + + let salt = Bytes(session_keys.salt.to_be_bytes().to_vec()); + let mac = sha1::hmac_sha1(&Bytes(k_client.to_vec()), &salt); + server.log_in(email, &mac) + }; + + assert!(log_in_without_password_a_zero()?); + + let mut log_in_without_password_multiple_n = || -> Option { + // Now log in without your password by having the client send N, N*2, &c. + let n = ¶meters.n; + let a_public = n * 2_u8.to_biguint()?; + let session_keys = server.exchange(email, &a_public)?; + + // If A is a multiple of N, the term $(A * v**u) ** b % N$ also becomes zero. + let s_client: BigUint = 0_u8.to_biguint()?; + let k_client = sha256(&s_client.to_bytes_be()); + + let salt = Bytes(session_keys.salt.to_be_bytes().to_vec()); + let mac = sha1::hmac_sha1(&Bytes(k_client.to_vec()), &salt); + server.log_in(email, &mac) + }; + + assert!(log_in_without_password_multiple_n()?); + + println!("[okay] Challenge 37: break SRP with zero key"); + Some(()) +} + +pub fn challenge38() -> Option<()> { + println!("[xxxx] Challenge 38: TBD"); + Some(()) } diff --git a/src/srp.rs b/src/srp.rs index 9e6d876..20c215c 100644 --- a/src/srp.rs +++ b/src/srp.rs @@ -1,4 +1,6 @@ +use crate::bytes::Bytes; use crate::set5; +use crate::sha1; use num_bigint::BigUint; use num_bigint::RandBigInt; use num_bigint::ToBigUint; @@ -122,4 +124,17 @@ impl Server { let k_server = sha256(&s_server.to_bytes_be()); Some(k_server) } + + pub fn log_in(&mut self, email: &str, mac_client: &Bytes) -> Option { + let k = self.get_k(email)?; + let password_entry = self.entries.get(email)?; + let salt = Bytes(password_entry.salt.to_be_bytes().to_vec()); + + // S->C Send "OK" if HMAC-SHA255(K, salt) validates + let mac_server = sha1::hmac_sha1(&Bytes(k.to_vec()), &salt); + if mac_server == *mac_client { + return Some(true); + } + Some(false) + } }