use crate::srp::{SrpParams, hex_padded};
use num_bigint::BigUint;
fn hash_str(p: &SrpParams, text: &str) -> BigUint {
BigUint::from_bytes_be(&p.hash.digest(text.as_bytes()))
}
fn hash_str_hex(p: &SrpParams, text: &str) -> String {
p.hash
.digest(text.as_bytes())
.iter()
.map(|byte| format!("{byte:02x}"))
.collect()
}
pub fn compute_x(p: &SrpParams, salt: &str, identity: &str, password: &str) -> BigUint {
let inner = hash_str_hex(p, &format!("{identity}:{password}"));
hash_str(p, &format!("{salt}{inner}").to_uppercase())
}
pub fn compute_u(p: &SrpParams, a_pub_hex: &str, b_str: &str) -> BigUint {
hash_str(p, &format!("{a_pub_hex}{b_str}"))
}
pub fn compute_m1(p: &SrpParams, a_pub_hex: &str, b_str: &str, s_hex: &str) -> BigUint {
hash_str(p, &format!("{a_pub_hex}{b_str}{s_hex}"))
}
pub fn compute_m2(p: &SrpParams, a_pub_hex: &str, m1: &BigUint, s_hex: &str) -> BigUint {
let m1_hex = hex_padded(m1, p.hash.hex_width());
hash_str(p, &format!("{a_pub_hex}{m1_hex}{s_hex}"))
}
pub fn session_key(p: &SrpParams, s_hex: &str) -> String {
hash_str_hex(p, s_hex)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::srp::{HashAlg, RFC5054_1024_PRIME_HEX, SrpParams, Variant, hex};
const SALT: &str = "0f1e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0";
const IDENTITY: &str = "user@example.test";
const PASSWORD: &str = "correct horse battery staple";
const A_PUB: &str = "afc09d0e8064973db60bf3ae47e25e908557d52b694a7017bde933ba5805bfc5d09eb35be03567c21b1e11831117e57724645fe15632be462702d13ac571ad9e7291cee017c5caf720c98e9f268169787236a7a4cc1fa7eb114a252b353e0cf8d26eef8df718a8ed76577c3aace096c6b3c38e3cdd7f3ad107164eb87e807bfd";
const B_STR: &str = "ee9f2db217673d527389de972634c74b6a1eff01caf11b03d3b42559958189e839350fe5dde116bc79fc7f41a1728da87f9740a81c559a7812f1e8b6be4192c0a4a33546483f1059c8f976757f53ff14e2b05810864b4836bf831ab99a1b11863ff2fff3a7bd0393d56c280a4a4c2b752f72d190b5b64c740f5bd8acbdf03b5c";
const S_HEX: &str = "913471403f02102b47833c0483e9e096dcd8e2c087351992acf9721a0e13354d3a638400fa754109e3e5539f8f8d6ab6cbf0c64e062f200b47dd735a24d121a9fd033fbe9953c7f4e3fa139f3080c072c07a25a254989b5d41bfa0f1bd42c7c9c05c1c801f9142818ccff784b5c8d3cd6fcf0c7c3d8968a45d75da7d43e0e5f4";
fn params() -> SrpParams {
SrpParams {
variant: Variant::HexString,
prime: num_bigint::BigUint::parse_bytes(RFC5054_1024_PRIME_HEX.as_bytes(), 16)
.expect("valid prime"),
generator: num_bigint::BigUint::from(2u32),
hash: HashAlg::Sha256,
}
}
#[test]
fn x_hashes_the_salt_string_verbatim() {
assert_eq!(
hex(&compute_x(¶ms(), SALT, IDENTITY, PASSWORD)),
"e58fd6ff10b6704c9f1194b98c90b557ad7de68fa2399f3622e77873acc11589"
);
}
#[test]
fn u_hashes_the_two_hex_strings() {
assert_eq!(
hex(&compute_u(¶ms(), A_PUB, B_STR)),
"a4323f8727676fbde7a0371a7cd44ff0b285e747f8a9f5ebd34a4c94f5ef88ba"
);
}
#[test]
fn m1_hashes_a_b_and_s() {
assert_eq!(
hex(&compute_m1(¶ms(), A_PUB, B_STR, S_HEX)),
"e78fe1c4f3e4f430bc068aa70565c0cedb27ec7558522019845ea9c90a0b997"
);
}
#[test]
fn m2_pads_m1_to_the_digest_width() {
let m1 = compute_m1(¶ms(), A_PUB, B_STR, S_HEX);
assert_eq!(
hex(&compute_m2(¶ms(), A_PUB, &m1, S_HEX)),
"1b8e7efaf3c35c057d67ba1a620b735018a59d1a496ae093655d38d007557a2e"
);
}
#[test]
fn session_key_is_the_hash_of_the_hex_of_s() {
assert_eq!(
session_key(¶ms(), S_HEX),
"d98140c85492caa3c6536a1abd7ea09696097e3056f65b8628c9a6b90b3e01e1"
);
}
}