141 lines
4.1 KiB
Rust
141 lines
4.1 KiB
Rust
use crate::bytes::Bytes;
|
|
use crate::set5;
|
|
use crate::sha1;
|
|
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)
|
|
}
|
|
|
|
pub fn log_in(&mut self, email: &str, mac_client: &Bytes) -> Option<bool> {
|
|
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)
|
|
}
|
|
}
|