From be69d76d7fabc5146b8a3b6f9578db7ea91a865d Mon Sep 17 00:00:00 2001 From: Arvid Gerstmann Date: Tue, 4 Jan 2022 15:34:19 +0100 Subject: [PATCH] Implement proof generation as per RFC2945. 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 - https://github.com/RustCrypto/PAKEs/issues/20 This resolves issue #20. --- srp/src/client.rs | 26 +++++++++++++++++++++-- srp/src/server.rs | 53 +++++++++++++++++++++++++++++++++++++---------- srp/tests/srp.rs | 4 +++- 3 files changed, 69 insertions(+), 14 deletions(-) diff --git a/srp/src/client.rs b/srp/src/client.rs index 815b195..57c1649 100644 --- a/srp/src/client.rs +++ b/srp/src/client.rs @@ -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, SrpAuthError> { @@ -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) diff --git a/srp/src/server.rs b/srp/src/server.rs index 299c1ce..354b9de 100644 --- a/srp/src/server.rs +++ b/srp/src/server.rs @@ -50,7 +50,8 @@ pub struct UserRecord<'a> { } /// SRP server state -pub struct SrpServer { +pub struct SrpServer<'a, D: Digest> { + user: &'a UserRecord<'a>, b: BigUint, a_pub: BigUint, b_pub: BigUint, @@ -58,15 +59,17 @@ pub struct SrpServer { key: Output, d: PhantomData, + + params: &'a SrpGroup, } -impl SrpServer { +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 { let a_pub = BigUint::from_bytes_be(a_pub); // Safeguard against malicious A @@ -97,11 +100,13 @@ impl SrpServer { D::digest(&s.to_bytes_be()) }; Ok(Self { + user, b, a_pub, b_pub, key, d, + params, }) } @@ -123,14 +128,40 @@ impl SrpServer { /// 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, 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, 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()); diff --git a/srp/tests/srp.rs b/srp/tests/srp.rs index bf6c30f..8268896 100644 --- a/srp/tests/srp.rs +++ b/srp/tests/srp.rs @@ -36,7 +36,9 @@ fn auth_test(reg_pwd: &[u8], auth_pwd: &[u8]) { // Client processes handshake reply let auth_priv_key = srp_private_key::(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