diff options
author | Josh Brown <josh9051@gmail.com> | 2022-01-22 09:38:33 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-22 07:38:33 -0700 |
commit | 6d963225520f0d8e5948457b8ba25bd563382f5e (patch) | |
tree | 12c05900061a93c74242d37f5dc1935977c9bd8c /srp/src/server.rs | |
parent | 689dc0ab6af950b027b4bab96f73c427d2c42d6e (diff) | |
download | PAKEs-6d963225520f0d8e5948457b8ba25bd563382f5e.tar.xz |
srp: rebuild library (#79)
Complete rewrite of the SRP library.
Includes many improvements over the old library:
- Improved file and code organization
- Access to individual SRP computations
- Consistent sever and client API
- Simpler API
- Improved documentation with tests in documentation
- New tests for compatibility with the RFC
- Bumps dependencies
- Timing safe verification comparisons
- Modernized error handling
Diffstat (limited to 'srp/src/server.rs')
-rw-r--r-- | srp/src/server.rs | 243 |
1 files changed, 141 insertions, 102 deletions
diff --git a/srp/src/server.rs b/srp/src/server.rs index 299c1ce..0ecc17c 100644 --- a/srp/src/server.rs +++ b/srp/src/server.rs @@ -2,145 +2,184 @@ //! //! # Usage //! First receive user's username and public value `a_pub`, retrieve from a -//! database `UserRecord` for a given username, generate `b` (e.g. 512 bits -//! long) and initialize SRP server instance: +//! database the salt and verifier for a given username. Generate `b` and public value `b_pub`. //! -//! ```ignore -//! use srp::groups::G_2048; //! -//! let (username, a_pub) = conn.receive_handshake(); -//! let user = db.retrieve_user_record(username); -//! let b = [0u8; 64]; -//! rng.fill_bytes(&mut b); -//! let server = SrpServer::<Sha256>::new(&user, &a_pub, &b, &G_2048)?; +//! ```rust +//! use crate::srp::groups::G_2048; +//! use sha2::Sha256; // Note: You should probably use a proper password KDF +//! # use crate::srp::server::SrpServer; +//! # fn get_client_request()-> (Vec<u8>, Vec<u8>) { (vec![], vec![])} +//! # fn get_user(_: &[u8])-> (Vec<u8>, Vec<u8>) { (vec![], vec![])} +//! +//! let server = SrpServer::<Sha256>::new(&G_2048); +//! let (username, a_pub) = get_client_request(); +//! let (salt, v) = get_user(&username); +//! let mut b = [0u8; 64]; +//! // rng.fill_bytes(&mut b); +//! let b_pub = server.compute_public_ephemeral(&b, &v); //! ``` //! -//! Next send to user `b_pub` and `salt` from user record: +//! Next send to user `b_pub` and `salt` from user record +//! +//! Next process the user response: +//! +//! ```rust +//! # let server = crate::srp::server::SrpServer::<sha2::Sha256>::new(&crate::srp::groups::G_2048); +//! # fn get_client_response() -> Vec<u8> { vec![1] } +//! # let b = [0u8; 64]; +//! # let v = b""; //! -//! ```ignore -//! let b_pub = server.get_b_pub(); -//! conn.reply_to_handshake(&user.salt, b_pub); +//! let a_pub = get_client_response(); +//! let verifier = server.process_reply(&b, v, &a_pub).unwrap(); //! ``` //! +//! //! And finally receive user proof, verify it and send server proof in the //! reply: //! -//! ```ignore -//! let user_proof = conn.receive_proof(); -//! let server_proof = server.verify(user_proof)?; -//! conn.send_proof(server_proof); +//! ```rust +//! # let server = crate::srp::server::SrpServer::<sha2::Sha256>::new(&crate::srp::groups::G_2048); +//! # let verifier = server.process_reply(b"", b"", b"1").unwrap(); +//! # fn get_client_proof()-> Vec<u8> { vec![26, 80, 8, 243, 111, 162, 238, 171, 208, 237, 207, 46, 46, 137, 44, 213, 105, 208, 84, 224, 244, 216, 103, 145, 14, 103, 182, 56, 242, 4, 179, 57] }; +//! # fn send_proof(_: &[u8]) { }; +//! +//! let client_proof = get_client_proof(); +//! verifier.verify_client(&client_proof).unwrap(); +//! send_proof(verifier.proof()); //! ``` //! -//! To get the shared secret use `get_key` method. As alternative to using -//! `verify` method it's also possible to use this key for authentificated -//! encryption. +//! +//! `key` contains shared secret key between user and the server. You can extract shared secret +//! key using `key()` method. +//! ```rust +//! # let server = crate::srp::server::SrpServer::<sha2::Sha256>::new(&crate::srp::groups::G_2048); +//! # let verifier = server.process_reply(b"", b"", b"1").unwrap(); +//! +//! verifier.key(); +//!``` +//! use std::marker::PhantomData; use digest::{Digest, Output}; use num_bigint::BigUint; +use subtle::ConstantTimeEq; use crate::types::{SrpAuthError, SrpGroup}; - -/// Data provided by users upon registration, usually stored in the database. -pub struct UserRecord<'a> { - pub username: &'a [u8], - pub salt: &'a [u8], - /// Password verifier - pub verifier: &'a [u8], -} +use crate::utils::{compute_k, compute_m1, compute_m2, compute_u}; /// SRP server state -pub struct SrpServer<D: Digest> { - b: BigUint, - a_pub: BigUint, - b_pub: BigUint, - - key: Output<D>, - +pub struct SrpServer<'a, D: Digest> { + params: &'a SrpGroup, d: PhantomData<D>, } -impl<D: Digest> SrpServer<D> { +/// SRP server state after handshake with the client. +pub struct SrpServerVerifier<D: Digest> { + m1: Output<D>, + m2: Output<D>, + key: Vec<u8>, +} + +impl<'a, D: Digest> SrpServer<'a, D> { /// Create new server state. - pub fn new( - user: &UserRecord<'_>, - a_pub: &[u8], + pub fn new(params: &'a SrpGroup) -> Self { + Self { + params, + d: Default::default(), + } + } + + // k*v + g^b % N + pub fn compute_b_pub(&self, b: &BigUint, k: &BigUint, v: &BigUint) -> BigUint { + let inter = (k * v) % &self.params.n; + (inter + self.params.g.modpow(b, &self.params.n)) % &self.params.n + } + + // <premaster secret> = (A * v^u) ^ b % N + pub fn compute_premaster_secret( + &self, + a_pub: &BigUint, + v: &BigUint, + u: &BigUint, + b: &BigUint, + ) -> BigUint { + // (A * v^u) + let base = (a_pub * v.modpow(u, &self.params.n)) % &self.params.n; + base.modpow(b, &self.params.n) + } + + /// Get public ephemeral value for sending to the client. + pub fn compute_public_ephemeral(&self, b: &[u8], v: &[u8]) -> Vec<u8> { + self.compute_b_pub( + &BigUint::from_bytes_be(b), + &compute_k::<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 + pub fn process_reply( + &self, b: &[u8], - params: &SrpGroup, - ) -> Result<Self, SrpAuthError> { + v: &[u8], + a_pub: &[u8], + ) -> Result<SrpServerVerifier<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::<D>(self.params); + let b_pub = self.compute_b_pub(&b, &k, &v); + // Safeguard against malicious A - if &a_pub % ¶ms.n == BigUint::default() { - return Err(SrpAuthError { - description: "Malicious a_pub value", - }); + if &a_pub % &self.params.n == BigUint::default() { + return Err(SrpAuthError::IllegalParameter("a_pub".to_owned())); } - let v = BigUint::from_bytes_be(user.verifier); - let b = BigUint::from_bytes_be(b) % ¶ms.n; - let k = params.compute_k::<D>(); - // kv + g^b - let interm = (k * &v) % ¶ms.n; - let b_pub = (interm + ¶ms.modpow(&b)) % ¶ms.n; - // H(A || B) - let u = { - let mut d = D::new(); - d.update(&a_pub.to_bytes_be()); - d.update(&b_pub.to_bytes_be()); - d.finalize() - }; - let d = Default::default(); - //(Av^u) ^ b - let key = { - let u = BigUint::from_bytes_be(u.as_slice()); - let t = (&a_pub * v.modpow(&u, ¶ms.n)) % ¶ms.n; - let s = t.modpow(&b, ¶ms.n); - D::digest(&s.to_bytes_be()) - }; - Ok(Self { - b, - a_pub, - b_pub, - key, - d, - }) - } - /// Get private `b` value. (see `new_with_b` documentation) - pub fn get_b(&self) -> Vec<u8> { - self.b.to_bytes_be() - } + let u = compute_u::<D>(&a_pub.to_bytes_be(), &b_pub.to_bytes_be()); + + let key = self.compute_premaster_secret(&a_pub, &v, &u, &b); + + let m1 = compute_m1::<D>( + &a_pub.to_bytes_be(), + &b_pub.to_bytes_be(), + &key.to_bytes_be(), + ); + + let m2 = compute_m2::<D>(&a_pub.to_bytes_be(), &m1, &key.to_bytes_be()); - /// Get public `b_pub` value for sending to the user. - pub fn get_b_pub(&self) -> Vec<u8> { - self.b_pub.to_bytes_be() + Ok(SrpServerVerifier { + m1, + m2, + key: key.to_bytes_be(), + }) } +} +impl<D: Digest> SrpServerVerifier<D> { /// Get shared secret between user and the server. (do not forget to verify /// that keys are the same!) - pub fn get_key(&self) -> Output<D> { - self.key.clone() + pub fn key(&self) -> &[u8] { + &self.key + } + + /// Verification data for sending to the client. + pub fn proof(&self) -> &[u8] { + // TODO not Output + self.m2.as_slice() } - /// 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); - - if user_proof == d.finalize().as_slice() { - // H(A, M, K) - let mut d = D::new(); - d.update(&self.a_pub.to_bytes_be()); - d.update(user_proof); - d.update(&self.key); - Ok(d.finalize()) + /// Process user proof of having the same shared secret. + pub fn verify_client(&self, reply: &[u8]) -> Result<(), SrpAuthError> { + if self.m1.ct_eq(reply).unwrap_u8() != 1 { + // aka == 0 + Err(SrpAuthError::BadRecordMac("client".to_owned())) } else { - Err(SrpAuthError { - description: "Incorrect user proof", - }) + Ok(()) } } } |