X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=srp%2Fsrc%2Fclient.rs;h=ee8fd8d2285cc7d6f8ceecef702ff35dd1cfb4e0;hb=6d963225520f0d8e5948457b8ba25bd563382f5e;hp=815b195327940c6d12c948a1106bd3f3bd50934a;hpb=689dc0ab6af950b027b4bab96f73c427d2c42d6e;p=PAKEs.git diff --git a/srp/src/client.rs b/srp/src/client.rs index 815b195..ee8fd8d 100644 --- a/srp/src/client.rs +++ b/srp/src/client.rs @@ -2,275 +2,242 @@ //! //! # Usage //! First create SRP client struct by passing to it SRP parameters (shared -//! between client and server) and randomly generated `a`: +//! between client and server). //! -//! ```ignore -//! use srp::groups::G_2048; -//! use sha2::Sha256; +//! You can use SHA1 from SRP-6a, but it's highly recommended to use specialized +//! password hashing algorithm instead (e.g. PBKDF2, argon2 or scrypt). //! -//! let mut a = [0u8; 64]; -//! rng.fill_bytes(&mut a); -//! let client = SrpClient::::new(&a, &G_2048); +//! ```rust +//! use crate::srp::groups::G_2048; +//! use sha2::Sha256; // Note: You should probably use a proper password KDF +//! # use crate::srp::client::SrpClient; +//! +//! let client = SrpClient::::new(&G_2048); //! ``` //! //! Next send handshake data (username and `a_pub`) to the server and receive //! `salt` and `b_pub`: //! -//! ```ignore -//! let a_pub = client.get_a_pub(); -//! let (salt, b_pub) = conn.send_handshake(username, a_pub); +//! ```rust +//! # let client = crate::srp::client::SrpClient::::new(&crate::srp::groups::G_2048); +//! # fn server_response()-> (Vec, Vec) { (vec![], vec![]) } +//! +//! let mut a = [0u8; 64]; +//! // rng.fill_bytes(&mut a); +//! let a_pub = client.compute_public_ephemeral(&a); +//! let (salt, b_pub) = server_response(); //! ``` //! -//! Compute private key using `salt` with any password hashing function. -//! You can use method from SRP-6a, but it's recommended to use specialized -//! password hashing algorithm instead (e.g. PBKDF2, argon2 or scrypt). -//! Next create verifier instance, note that `get_verifier` consumes client and -//! can return error in case of malicious `b_pub`. +//! Process the server response and create verifier instance. +//! process_reply can return error in case of malicious `b_pub`. +//! +//! ```rust +//! # let client = crate::srp::client::SrpClient::::new(&crate::srp::groups::G_2048); +//! # let a = [0u8; 64]; +//! # let username = b"username"; +//! # let password = b"password"; +//! # let salt = b"salt"; +//! # let b_pub = b"b_pub"; //! -//! ```ignore -//! let private_key = srp_private_key::(username, password, salt); -//! let verifier = client.get_verifier(&private_key, &b_pub)?; +//! let private_key = (username, password, salt); +//! let verifier = client.process_reply(&a, username, password, salt, b_pub); //! ``` //! //! Finally verify the server: first generate user proof, //! send it to the server and verify server proof in the reply. Note that //! `verify_server` method will return error in case of incorrect server reply. //! -//! ```ignore -//! let user_proof = verifier.get_proof(); -//! let server_proof = conn.send_proof(user_proof); -//! let key = verifier.verify_server(server_proof)?; +//! ```rust +//! # let client = crate::srp::client::SrpClient::::new(&crate::srp::groups::G_2048); +//! # let verifier = client.process_reply(b"", b"", b"", b"", b"1").unwrap(); +//! # fn send_proof(_: &[u8]) -> Vec { vec![173, 202, 13, 26, 207, 73, 0, 46, 121, 238, 48, 170, 96, 146, 60, 49, 88, 76, 12, 184, 152, 76, 207, 220, 140, 205, 190, 189, 117, 6, 131, 63] } +//! +//! let client_proof = verifier.proof(); +//! let server_proof = send_proof(client_proof); +//! verifier.verify_server(&server_proof).unwrap(); //! ``` //! -//! `key` contains shared secret key between user and the server. Alternatively -//! you can directly extract shared secret key using `get_key()` method and -//! handle authentication through different (secure!) means (e.g. by using -//! authenticated cipher mode). +//! `key` contains shared secret key between user and the server. You can extract shared secret +//! key using `key()` method. +//! ```rust +//! # let client = crate::srp::client::SrpClient::::new(&crate::srp::groups::G_2048); +//! # let verifier = client.process_reply(b"", b"", b"", b"", b"1").unwrap(); +//! +//! verifier.key(); +//!``` +//! //! //! For user registration on the server first generate salt (e.g. 32 bytes long) //! and get password verifier which depends on private key. Send username, salt //! and password verifier over protected channel to protect against //! Man-in-the-middle (MITM) attack for registration. //! -//! ```ignore -//! let pwd_verifier = client.get_password_verifier(&private_key); -//! conn.send_registration_data(username, salt, pwd_verifier); +//! ```rust +//! # let client = crate::srp::client::SrpClient::::new(&crate::srp::groups::G_2048); +//! # let username = b"username"; +//! # let password = b"password"; +//! # let salt = b"salt"; +//! # fn send_registration_data(_: &[u8], _: &[u8], _: &[u8]) {} +//! +//! let pwd_verifier = client.compute_verifier(username, password, salt); +//! send_registration_data(username, salt, &pwd_verifier); //! ``` use std::marker::PhantomData; use digest::{Digest, Output}; use num_bigint::BigUint; +use subtle::ConstantTimeEq; use crate::types::{SrpAuthError, SrpGroup}; +use crate::utils::{compute_k, compute_m1, compute_m2, compute_u}; /// SRP client state before handshake with the server. pub struct SrpClient<'a, D: Digest> { params: &'a SrpGroup, - - a: BigUint, - a_pub: BigUint, - d: PhantomData, } /// SRP client state after handshake with the server. pub struct SrpClientVerifier { - proof: Output, - server_proof: Output, - key: Output, -} - -/// Compute user private key as described in the RFC 5054. Consider using proper -/// password hashing algorithm instead. -pub fn srp_private_key(username: &[u8], password: &[u8], salt: &[u8]) -> Output { - let p = { - let mut d = D::new(); - d.update(username); - d.update(b":"); - d.update(password); - d.finalize() - }; - let mut d = D::new(); - d.update(salt); - d.update(p.as_slice()); - d.finalize() + m1: Output, + m2: Output, + key: Vec, } impl<'a, D: Digest> SrpClient<'a, D> { /// Create new SRP client instance. - pub fn new(a: &[u8], params: &'a SrpGroup) -> Self { - let a = BigUint::from_bytes_be(a); - let a_pub = params.modpow(&a); - + pub fn new(params: &'a SrpGroup) -> Self { Self { params, - a, - a_pub, d: Default::default(), } } - /// Get password verfier for user registration on the server - pub fn get_password_verifier(&self, private_key: &[u8]) -> Vec { - let x = BigUint::from_bytes_be(private_key); - let v = self.params.modpow(&x); - v.to_bytes_be() + pub fn compute_a_pub(&self, a: &BigUint) -> BigUint { + self.params.g.modpow(a, &self.params.n) } - fn calc_key(&self, b_pub: &BigUint, x: &BigUint, u: &BigUint) -> Output { - let n = &self.params.n; - let k = self.params.compute_k::(); - let interm = (k * self.params.modpow(x)) % n; - // Because we do operation in modulo N we can get: (kv + g^b) < kv - let v = if *b_pub > interm { - (b_pub - &interm) % n - } else { - (n + b_pub - &interm) % n - }; - // S = |B - kg^x| ^ (a + ux) - let s = v.modpow(&(&self.a + (u * x) % n), n); - D::digest(&s.to_bytes_be()) + // H( | ":" | ) + pub fn compute_identity_hash(username: &[u8], password: &[u8]) -> Output { + let mut d = D::new(); + d.update(username); + d.update(b":"); + d.update(password); + d.finalize() } - /// Process server reply to the handshake. - pub fn process_reply( - self, - private_key: &[u8], - b_pub: &[u8], - ) -> Result, SrpAuthError> { - let u = { - let mut d = D::new(); - d.update(&self.a_pub.to_bytes_be()); - d.update(b_pub); - let h = d.finalize(); - BigUint::from_bytes_be(h.as_slice()) - }; - - let b_pub = BigUint::from_bytes_be(b_pub); + // x = H( | H( | ":" | )) + pub fn compute_x(identity_hash: &[u8], salt: &[u8]) -> BigUint { + let mut x = D::new(); + x.update(salt); + x.update(identity_hash); + BigUint::from_bytes_be(&x.finalize()) + } - // Safeguard against malicious B - if &b_pub % &self.params.n == BigUint::default() { - return Err(SrpAuthError { - description: "Malicious b_pub value", - }); - } + // (B - (k * g^x)) ^ (a + (u * x)) % N + pub fn compute_premaster_secret( + &self, + b_pub: &BigUint, + k: &BigUint, + x: &BigUint, + a: &BigUint, + u: &BigUint, + ) -> BigUint { + // (k * g^x) + let base = (k * (self.params.g.modpow(x, &self.params.n))) % &self.params.n; + // Because we do operation in modulo N we can get: b_pub > base. That's not good. So we add N to b_pub to make sure. + // B - kg^x + let base = ((&self.params.n + b_pub) - &base) % &self.params.n; + let exp = (u * x) + a; + // S = (B - kg^x) ^ (a + ux) + // or + // S = base ^ exp + base.modpow(&exp, &self.params.n) + } - let x = BigUint::from_bytes_be(private_key); - let key = self.calc_key(&b_pub, &x, &u); - // M1 = H(A, B, K) - let proof = { - let mut d = D::new(); - d.update(&self.a_pub.to_bytes_be()); - d.update(&b_pub.to_bytes_be()); - d.update(&key); - d.finalize() - }; + // v = g^x % N + pub fn compute_v(&self, x: &BigUint) -> BigUint { + self.params.g.modpow(x, &self.params.n) + } - // M2 = H(A, M1, K) - let server_proof = { - let mut d = D::new(); - d.update(&self.a_pub.to_bytes_be()); - d.update(&proof); - d.update(&key); - d.finalize() - }; + /// Get password verifier (v in RFC5054) for user registration on the server. + pub fn compute_verifier(&self, username: &[u8], password: &[u8], salt: &[u8]) -> Vec { + let identity_hash = Self::compute_identity_hash(username, password); + let x = Self::compute_x(identity_hash.as_slice(), salt); + self.compute_v(&x).to_bytes_be() + } - Ok(SrpClientVerifier { - proof, - server_proof, - key, - }) + /// Get public ephemeral value for handshaking with the server. + /// g^a % N + pub fn compute_public_ephemeral(&self, a: &[u8]) -> Vec { + self.compute_a_pub(&BigUint::from_bytes_be(a)).to_bytes_be() } - /// Process server reply to the handshake with username and salt. - #[allow(non_snake_case)] - pub fn process_reply_with_username_and_salt( - self, + /// Process server reply to the handshake. + /// a is a random value, + /// username, password is supplied by the user + /// salt and b_pub come from the server + pub fn process_reply( + &self, + a: &[u8], username: &[u8], + password: &[u8], salt: &[u8], - private_key: &[u8], b_pub: &[u8], ) -> Result, SrpAuthError> { - let u = { - let mut d = D::new(); - d.update(&self.a_pub.to_bytes_be()); - d.update(b_pub); - let h = d.finalize(); - BigUint::from_bytes_be(h.as_slice()) - }; - + let a = BigUint::from_bytes_be(a); + let a_pub = self.compute_a_pub(&a); let b_pub = BigUint::from_bytes_be(b_pub); // Safeguard against malicious B if &b_pub % &self.params.n == BigUint::default() { - return Err(SrpAuthError { - description: "Malicious b_pub value", - }); + return Err(SrpAuthError::IllegalParameter("b_pub".to_owned())); } - let x = BigUint::from_bytes_be(private_key); - let key = self.calc_key(&b_pub, &x, &u); - // M1 = H(H(N)^H(g), H(I), salt, A, B, K) - let proof = { - let mut d = D::new(); - d.update(username); - let h = d.finalize_reset(); - let I: &[u8] = h.as_slice(); + let u = compute_u::(&a_pub.to_bytes_be(), &b_pub.to_bytes_be()); + let k = compute_k::(self.params); + let identity_hash = Self::compute_identity_hash(username, password); + let x = Self::compute_x(identity_hash.as_slice(), salt); + + let key = self.compute_premaster_secret(&b_pub, &k, &x, &a, &u); - d.update(self.params.compute_hash_n_xor_hash_g::()); - d.update(I); - d.update(salt); - d.update(&self.a_pub.to_bytes_be()); - d.update(&b_pub.to_bytes_be()); - d.update(&key.to_vec()); - d.finalize() - }; + let m1 = compute_m1::( + &a_pub.to_bytes_be(), + &b_pub.to_bytes_be(), + &key.to_bytes_be(), + ); - // M2 = H(A, M1, K) - let server_proof = { - let mut d = D::new(); - d.update(&self.a_pub.to_bytes_be()); - d.update(&proof); - d.update(&key); - d.finalize() - }; + let m2 = compute_m2::(&a_pub.to_bytes_be(), &m1, &key.to_bytes_be()); Ok(SrpClientVerifier { - proof, - server_proof, - key, + m1, + m2, + key: key.to_bytes_be(), }) } - - /// Get public ephemeral value for handshaking with the server. - pub fn get_a_pub(&self) -> Vec { - self.a_pub.to_bytes_be() - } } impl SrpClientVerifier { /// Get shared secret key without authenticating server, e.g. for using with /// authenticated encryption modes. DO NOT USE this method without /// some kind of secure authentication - pub fn get_key(self) -> Output { - self.key + pub fn key(&self) -> &[u8] { + &self.key } /// Verification data for sending to the server. - pub fn get_proof(&self) -> Output { - self.proof.clone() + pub fn proof(&self) -> &[u8] { + self.m1.as_slice() } - /// Verify server reply to verification data. It will return shared secret - /// key in case of success. - pub fn verify_server(self, reply: &[u8]) -> Result, SrpAuthError> { - if self.server_proof.as_slice() != reply { - Err(SrpAuthError { - description: "Incorrect server proof", - }) + /// Verify server reply to verification data. + pub fn verify_server(&self, reply: &[u8]) -> Result<(), SrpAuthError> { + if self.m2.ct_eq(reply).unwrap_u8() != 1 { + // aka == 0 + Err(SrpAuthError::BadRecordMac("server".to_owned())) } else { - Ok(self.key) + Ok(()) } } }