1 //! SRP client implementation.
4 //! First create SRP client struct by passing to it SRP parameters (shared
5 //! between client and server) and randomly generated `a`:
8 //! use srp::groups::G_2048;
11 //! let mut a = [0u8; 64];
12 //! rng.fill_bytes(&mut a);
13 //! let client = SrpClient::<Sha256>::new(&a, &G_2048);
16 //! Next send handshake data (username and `a_pub`) to the server and receive
17 //! `salt` and `b_pub`:
20 //! let a_pub = client.get_a_pub();
21 //! let (salt, b_pub) = conn.send_handshake(username, a_pub);
24 //! Compute private key using `salt` with any password hashing function.
25 //! You can use method from SRP-6a, but it's recommended to use specialized
26 //! password hashing algorithm instead (e.g. PBKDF2, argon2 or scrypt).
27 //! Next create verifier instance, note that `get_verifier` consumes client and
28 //! can return error in case of malicious `b_pub`.
31 //! let private_key = srp_private_key::<Sha256>(username, password, salt);
32 //! let verifier = client.get_verifier(&private_key, &b_pub)?;
35 //! Finally verify the server: first generate user proof,
36 //! send it to the server and verify server proof in the reply. Note that
37 //! `verify_server` method will return error in case of incorrect server reply.
40 //! let user_proof = verifier.get_proof();
41 //! let server_proof = conn.send_proof(user_proof);
42 //! let key = verifier.verify_server(server_proof)?;
45 //! `key` contains shared secret key between user and the server. Alternatively
46 //! you can directly extract shared secret key using `get_key()` method and
47 //! handle authentication through different (secure!) means (e.g. by using
48 //! authenticated cipher mode).
50 //! For user registration on the server first generate salt (e.g. 32 bytes long)
51 //! and get password verifier which depends on private key. Send username, salt
52 //! and password verifier over protected channel to protect against
53 //! Man-in-the-middle (MITM) attack for registration.
56 //! let pwd_verifier = client.get_password_verifier(&private_key);
57 //! conn.send_registration_data(username, salt, pwd_verifier);
60 use std::marker::PhantomData;
62 use digest::{Digest, Output};
63 use num_bigint::BigUint;
65 use crate::tools::powm;
66 use crate::types::{SrpAuthError, SrpGroup};
68 /// SRP client state before handshake with the server.
69 pub struct SrpClient<'a, D: Digest> {
78 /// SRP client state after handshake with the server.
79 pub struct SrpClientVerifier<D: Digest> {
81 server_proof: Output<D>,
85 /// Compute user private key as described in the RFC 5054. Consider using proper
86 /// password hashing algorithm instead.
87 pub fn srp_private_key<D: Digest>(username: &[u8], password: &[u8], salt: &[u8]) -> Output<D> {
97 d.update(p.as_slice());
101 impl<'a, D: Digest> SrpClient<'a, D> {
102 /// Create new SRP client instance.
103 pub fn new(a: &[u8], params: &'a SrpGroup) -> Self {
104 let a = BigUint::from_bytes_be(a);
105 let a_pub = params.powm(&a);
111 d: Default::default(),
115 /// Get password verfier for user registration on the server
116 pub fn get_password_verifier(&self, private_key: &[u8]) -> Vec<u8> {
117 let x = BigUint::from_bytes_be(private_key);
118 let v = self.params.powm(&x);
122 fn calc_key(&self, b_pub: &BigUint, x: &BigUint, u: &BigUint) -> Output<D> {
123 let n = &self.params.n;
124 let k = self.params.compute_k::<D>();
125 let interm = (k * self.params.powm(x)) % n;
126 // Because we do operation in modulo N we can get: (kv + g^b) < kv
127 let v = if *b_pub > interm {
128 (b_pub - &interm) % n
130 (n + b_pub - &interm) % n
132 // S = |B - kg^x| ^ (a + ux)
133 let s = powm(&v, &(&self.a + (u * x) % n), n);
134 D::digest(&s.to_bytes_be())
137 /// Process server reply to the handshake.
138 pub fn process_reply(
142 ) -> Result<SrpClientVerifier<D>, SrpAuthError> {
144 let mut d = D::new();
145 d.update(&self.a_pub.to_bytes_be());
147 let h = d.finalize();
148 BigUint::from_bytes_be(h.as_slice())
151 let b_pub = BigUint::from_bytes_be(b_pub);
153 // Safeguard against malicious B
154 if &b_pub % &self.params.n == BigUint::default() {
155 return Err(SrpAuthError {
156 description: "Malicious b_pub value",
160 let x = BigUint::from_bytes_be(private_key);
161 let key = self.calc_key(&b_pub, &x, &u);
164 let mut d = D::new();
165 d.update(&self.a_pub.to_bytes_be());
166 d.update(&b_pub.to_bytes_be());
173 let mut d = D::new();
174 d.update(&self.a_pub.to_bytes_be());
180 Ok(SrpClientVerifier {
187 /// Process server reply to the handshake with username and salt.
188 #[allow(non_snake_case)]
189 pub fn process_reply_with_username_and_salt(
195 ) -> Result<SrpClientVerifier<D>, SrpAuthError> {
197 let mut d = D::new();
198 d.update(&self.a_pub.to_bytes_be());
200 let h = d.finalize();
201 BigUint::from_bytes_be(h.as_slice())
204 let b_pub = BigUint::from_bytes_be(b_pub);
206 // Safeguard against malicious B
207 if &b_pub % &self.params.n == BigUint::default() {
208 return Err(SrpAuthError {
209 description: "Malicious b_pub value",
213 let x = BigUint::from_bytes_be(private_key);
214 let key = self.calc_key(&b_pub, &x, &u);
215 // M1 = H(H(N)^H(g), H(I), salt, A, B, K)
217 let mut d = D::new();
219 let h = d.finalize_reset();
220 let I: &[u8] = h.as_slice();
222 d.update(self.params.compute_hash_n_xor_hash_g::<D>());
225 d.update(&self.a_pub.to_bytes_be());
226 d.update(&b_pub.to_bytes_be());
227 d.update(&key.to_vec());
233 let mut d = D::new();
234 d.update(&self.a_pub.to_bytes_be());
240 Ok(SrpClientVerifier {
247 /// Get public ephemeral value for handshaking with the server.
248 pub fn get_a_pub(&self) -> Vec<u8> {
249 self.a_pub.to_bytes_be()
253 impl<D: Digest> SrpClientVerifier<D> {
254 /// Get shared secret key without authenticating server, e.g. for using with
255 /// authenticated encryption modes. DO NOT USE this method without
256 /// some kind of secure authentication
257 pub fn get_key(self) -> Output<D> {
261 /// Verification data for sending to the server.
262 pub fn get_proof(&self) -> Output<D> {
266 /// Verify server reply to verification data. It will return shared secret
267 /// key in case of success.
268 pub fn verify_server(self, reply: &[u8]) -> Result<Output<D>, SrpAuthError> {
269 if self.server_proof.as_slice() != reply {
271 description: "Incorrect server proof",