srp6a 0.1.0

Implementation of SRP 6a (Secure Remote Password) according to RFC 5054 (https://datatracker.ietf.org/doc/html/rfc5054)
Documentation
use digest::Digest;
use num_bigint::BigUint;

use crate::group::Group;

pub fn compute_k<D: Digest>(group: &Group) -> BigUint {
    let n_bytes = group.n.to_bytes_be();
    let g_bytes = group.g.to_bytes_be();

    let padding_g_bytes = zero_pad(g_bytes, group.length_n / 8);

    let mut hasher = D::new();
    hasher.update(&n_bytes);
    hasher.update(&padding_g_bytes);
    let hash = hasher.finalize();

    BigUint::from_bytes_be(&hash)
}

pub fn zero_pad(mut source: Vec<u8>, length: usize) -> Vec<u8> {
    if source.len() >= length {
        source
    } else {
        let mut padded = vec![0u8; length - source.len()];
        padded.append(&mut source);
        padded
    }
}

pub fn compute_client_proof<D: Digest>(
    group: &Group,
    username: &[u8],
    salt: &[u8],
    public_client_key: &BigUint,
    public_server_key: &BigUint,
    session_key: &BigUint,
) -> Vec<u8> {
    let n_hash = D::digest(group.n.to_bytes_be());
    let g_hash = D::digest(group.g.to_bytes_be());
    let xor_hash: Vec<u8> = n_hash
        .iter()
        .zip(g_hash.iter())
        .map(|(a, b)| a ^ b)
        .collect();

    let i_hash = D::digest(username);

    // M1 = H(xor_hash | H(I) | salt | A | B | K)
    let mut hasher = D::new();
    hasher.update(&xor_hash);
    hasher.update(&i_hash);
    hasher.update(salt);
    hasher.update(public_client_key.to_bytes_be());
    hasher.update(public_server_key.to_bytes_be());
    hasher.update(session_key.to_bytes_be());

    hasher.finalize().to_vec()
}

pub fn compute_server_proof<D: Digest>(
    public_client_key: &BigUint,
    client_proof: &[u8],
    session_key: &BigUint,
) -> Vec<u8> {
    let mut hasher = D::new();
    hasher.update(public_client_key.to_bytes_be());
    hasher.update(client_proof);
    hasher.update(session_key.to_bytes_be());
    hasher.finalize().to_vec()
}