]> git.lizzy.rs Git - PAKEs.git/blobdiff - srp/src/server.rs
srp: rebuild library (#79)
[PAKEs.git] / srp / src / server.rs
index 299c1ced60fc3920afe67a703e33f673e7dc45f3..0ecc17cf05c401c6b2737b9642e032ca76308990 100644 (file)
 //!
 //! # 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 % &params.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) % &params.n;
-        let k = params.compute_k::<D>();
-        // kv + g^b
-        let interm = (k * &v) % &params.n;
-        let b_pub = (interm + &params.modpow(&b)) % &params.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, &params.n)) % &params.n;
-            let s = t.modpow(&b, &params.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(())
         }
     }
 }