Skip to content

Commit

Permalink
Implement proof generation as per RFC2945.
Browse files Browse the repository at this point in the history
This is a backward-incompatible change.

For details, refer to:

 - https://tools.ietf.org/html/rfc2945
 - https://tools.ietf.org/html/rfc5054
 - http://srp.stanford.edu/design.html
 - RustCrypto#20

This resolves issue RustCrypto#20.
  • Loading branch information
Leandros committed Jan 4, 2022
1 parent 689dc0a commit be69d76
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 14 deletions.
26 changes: 24 additions & 2 deletions srp/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ impl<'a, D: Digest> SrpClient<'a, D> {
/// Process server reply to the handshake.
pub fn process_reply(
self,
username: &[u8],
salt: &[u8],
private_key: &[u8],
b_pub: &[u8],
) -> Result<SrpClientVerifier<D>, SrpAuthError> {
Expand All @@ -158,13 +160,33 @@ impl<'a, D: Digest> SrpClient<'a, D> {

let x = BigUint::from_bytes_be(private_key);
let key = self.calc_key(&b_pub, &x, &u);
// M1 = H(A, B, K)
// M = H(H(N) XOR H(g) | H(U) | s | A | B | K)
let proof = {
let hn = {
let n = &self.params.n;
let mut d = D::new();
d.input(n.to_bytes_be());
BigUint::from_bytes_be(&d.result())
};
let hg = {
let g = &self.params.g;
let mut d = D::new();
d.input(g.to_bytes_be());
BigUint::from_bytes_be(&d.result())
};
let hu = {
let mut d = D::new();
d.input(username);
d.result()
};
let mut d = D::new();
d.update((hn ^ hg).to_bytes_be());
d.update(hu);
d.update(salt);
d.update(&self.a_pub.to_bytes_be());
d.update(&b_pub.to_bytes_be());
d.update(&key);
d.finalize()
d.result()
};

// M2 = H(A, M1, K)
Expand Down
53 changes: 42 additions & 11 deletions srp/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,23 +50,26 @@ pub struct UserRecord<'a> {
}

/// SRP server state
pub struct SrpServer<D: Digest> {
pub struct SrpServer<'a, D: Digest> {
user: &'a UserRecord<'a>,
b: BigUint,
a_pub: BigUint,
b_pub: BigUint,

key: Output<D>,

d: PhantomData<D>,

params: &'a SrpGroup,
}

impl<D: Digest> SrpServer<D> {
impl<'a, D: Digest> SrpServer<'a, D> {
/// Create new server state.
pub fn new(
user: &UserRecord<'_>,
user: &'a UserRecord,
a_pub: &[u8],
b: &[u8],
params: &SrpGroup,
params: &'a SrpGroup,
) -> Result<Self, SrpAuthError> {
let a_pub = BigUint::from_bytes_be(a_pub);
// Safeguard against malicious A
Expand Down Expand Up @@ -97,11 +100,13 @@ impl<D: Digest> SrpServer<D> {
D::digest(&s.to_bytes_be())
};
Ok(Self {
user,
b,
a_pub,
b_pub,
key,
d,
params,
})
}

Expand All @@ -123,14 +128,40 @@ impl<D: Digest> SrpServer<D> {

/// Process user proof of having the same shared secret and compute
/// server proof for sending to the user.
pub fn verify(&self, user_proof: &[u8]) -> Result<Output<D>, SrpAuthError> {
// M = H(A, B, K)
let mut d = D::new();
d.update(&self.a_pub.to_bytes_be());
d.update(&self.b_pub.to_bytes_be());
d.update(&self.key);
pub fn verify(
&self,
user_proof: &[u8],
) -> Result<GenericArray<u8, D::OutputSize>, SrpAuthError> {
// M = H(H(N) XOR H(g) | H(U) | s | A | B | K)
let proof = {
let hn = {
let n = &self.params.n;
let mut d = D::new();
d.update(n.to_bytes_be());
BigUint::from_bytes_be(&d.result())
};
let hg = {
let g = &self.params.g;
let mut d = D::new();
d.update(g.to_bytes_be());
BigUint::from_bytes_be(&d.result())
};
let hu = {
let mut d = D::new();
d.update(self.user.username);
d.result()
};
let mut d = D::new();
d.update((hn ^ hg).to_bytes_be());
d.update(hu);
d.update(self.user.salt);
d.update(&self.a_pub.to_bytes_be());
d.update(&self.b_pub.to_bytes_be());
d.update(&self.key);;
d.result()
};

if user_proof == d.finalize().as_slice() {
if user_proof == proof.as_slice() {
// H(A, M, K)
let mut d = D::new();
d.update(&self.a_pub.to_bytes_be());
Expand Down
4 changes: 3 additions & 1 deletion srp/tests/srp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ fn auth_test(reg_pwd: &[u8], auth_pwd: &[u8]) {

// Client processes handshake reply
let auth_priv_key = srp_private_key::<Sha256>(username, auth_pwd, salt);
let client2 = client.process_reply(&auth_priv_key, &b_pub).unwrap();
let client2 = client
.process_reply(user.username, user.salt, &auth_priv_key, &b_pub)
.unwrap();
let proof = client2.get_proof();

// Server processes verification data
Expand Down

0 comments on commit be69d76

Please sign in to comment.