Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 69 additions & 1 deletion srp/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ use subtle::ConstantTimeEq;

use crate::types::{SrpAuthError, SrpGroup};
use crate::utils::{
compute_hash, compute_k, compute_m1, compute_m1_rfc5054, compute_m2, compute_u,
compute_hash, compute_k, compute_k_nopad, compute_m1, compute_m1_csrp, compute_m1_rfc5054,
compute_m2, compute_u,
};

/// SRP server state
Expand Down Expand Up @@ -126,6 +127,13 @@ impl<'a, D: Digest> SrpServer<'a, D> {
(inter + self.params.g.modpow(b, &self.params.n)) % &self.params.n
}

// k*v + g^b
#[must_use]
pub fn compute_b_pub_csrp(&self, b: &BigUint, k: &BigUint, v: &BigUint) -> BigUint {
let inter = k * v;
inter + self.params.g.modpow(b, &self.params.n)
}

// <premaster secret> = (A * v^u) ^ b % N
#[must_use]
pub fn compute_premaster_secret(
Expand All @@ -151,6 +159,16 @@ impl<'a, D: Digest> SrpServer<'a, D> {
.to_bytes_be()
}

#[must_use]
pub fn compute_public_ephemeral_csrp(&self, b: &[u8], v: &[u8]) -> Vec<u8> {
self.compute_b_pub_csrp(
&BigUint::from_bytes_be(b),
&compute_k_nopad::<D>(self.params),
&BigUint::from_bytes_be(v),
)
.to_bytes_be()
}

/// Process client reply to the handshake.
/// b is a random value,
/// v is the provided during initial user registration
Expand Down Expand Up @@ -240,6 +258,56 @@ impl<'a, D: Digest> SrpServer<'a, D> {
session_key: session_key.to_vec(),
})
}

/// Process client reply to the handshake according to RFC 5054.
/// b is a random value,
/// v is the provided during initial user registration
pub fn process_reply_csrp(
&self,
username: &[u8],
salt: &[u8],
b: &[u8],
v: &[u8],
a_pub: &[u8],
) -> Result<SrpServerVerifierRfc5054<D>, SrpAuthError> {
let b = BigUint::from_bytes_be(b);
let v = BigUint::from_bytes_be(v);
let a_pub = BigUint::from_bytes_be(a_pub);

let k = compute_k_nopad::<D>(self.params);
let b_pub = self.compute_b_pub_csrp(&b, &k, &v);

// Safeguard against malicious A
if &a_pub % &self.params.n == BigUint::default() {
return Err(SrpAuthError::IllegalParameter("a_pub".to_owned()));
}

let u = compute_u::<D>(&a_pub.to_bytes_be(), &b_pub.to_bytes_be());

let premaster_secret = self
.compute_premaster_secret(&a_pub, &v, &u, &b)
.to_bytes_be();

let session_key = compute_hash::<D>(&premaster_secret);

let m1 = compute_m1_csrp::<D>(
self.params,
username,
salt,
&a_pub.to_bytes_be(),
&b_pub.to_bytes_be(),
session_key.as_slice(),
);

let m2 = compute_m2::<D>(&a_pub.to_bytes_be(), &m1, session_key.as_slice());

Ok(SrpServerVerifierRfc5054 {
m1,
m2,
key: premaster_secret,
session_key: session_key.to_vec(),
})
}
}

impl<D: Digest> SrpServerVerifier<D> {
Expand Down
47 changes: 47 additions & 0 deletions srp/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ pub fn compute_k<D: Digest>(params: &SrpGroup) -> BigUint {
BigUint::from_bytes_be(d.finalize().as_slice())
}

// k = H(N | g)
#[must_use]
pub fn compute_k_nopad<D: Digest>(params: &SrpGroup) -> BigUint {
let n = params.n.to_bytes_be();
let g_bytes = params.g.to_bytes_be();

let mut d = D::new();
d.update(&n);
d.update(&g_bytes);
BigUint::from_bytes_be(d.finalize().as_slice())
}

// H(N) XOR H(PAD(g))
#[must_use]
pub fn compute_hash_n_xor_hash_g<D: Digest>(params: &SrpGroup) -> Vec<u8> {
Expand All @@ -45,6 +57,21 @@ pub fn compute_hash_n_xor_hash_g<D: Digest>(params: &SrpGroup) -> Vec<u8> {
.collect()
}

// H(N) XOR H(g)
#[must_use]
pub fn compute_hash_n_xor_hash_g_nopad<D: Digest>(params: &SrpGroup) -> Vec<u8> {
let n = params.n.to_bytes_be();
let g_bytes = params.g.to_bytes_be();

let h_n = compute_hash::<D>(&n).to_vec();
let h_g = compute_hash::<D>(&g_bytes).to_vec();

h_n.iter()
.zip(h_g.iter())
.map(|(&x1, &x2)| x1 ^ x2)
.collect()
}

// M1 = H(A, B, K) this doesn't follow the spec but apparently no one does for M1
#[must_use]
pub fn compute_m1<D: Digest>(a_pub: &[u8], b_pub: &[u8], key: &[u8]) -> Output<D> {
Expand Down Expand Up @@ -82,6 +109,26 @@ pub fn compute_m1_rfc5054<D: Digest>(
d.finalize()
}

// M1 = H(H(N) XOR H(g) | H(U) | s | A | B | K) following CSRP legacy mode
#[must_use]
pub fn compute_m1_csrp<D: Digest>(
params: &SrpGroup,
username: &[u8],
salt: &[u8],
a_pub: &[u8],
b_pub: &[u8],
key: &[u8],
) -> Output<D> {
let mut d = D::new();
d.update(compute_hash_n_xor_hash_g_nopad::<D>(params));
d.update(compute_hash::<D>(username));
d.update(salt);
d.update(a_pub);
d.update(b_pub);
d.update(key);
d.finalize()
}

// M2 = H(A, M1, K)
#[must_use]
pub fn compute_m2<D: Digest>(a_pub: &[u8], m1: &Output<D>, key: &[u8]) -> Output<D> {
Expand Down