88//! password hashing algorithm instead (e.g. PBKDF2, argon2 or scrypt).
99//!
1010//! ```rust
11- //! use crate:: srp::groups::G_2048 ;
11+ //! use srp::groups::G2048 ;
1212//! use sha2::Sha256; // Note: You should probably use a proper password KDF
13- //! # use crate:: srp::client::Client;
13+ //! # use srp::client::Client;
1414//!
15- //! let client = Client::<Sha256>::new(&G_2048 );
15+ //! let client = Client::<G2048, Sha256>::new();
1616//! ```
1717//!
1818//! Next send handshake data (username and `a_pub`) to the server and receive
1919//! `salt` and `b_pub`:
2020//!
2121//! ```rust
22- //! # let client = crate:: srp::client:: Client::<sha2::Sha256>::new(&crate::srp::groups::G_2048 );
22+ //! # let client = srp::Client::<srp::G2048, sha2::Sha256>::new();
2323//! # fn server_response()-> (Vec<u8>, Vec<u8>) { (vec![], vec![]) }
2424//!
2525//! let mut a = [0u8; 64];
3232//! `process_reply` can return error in case of malicious `b_pub`.
3333//!
3434//! ```rust
35- //! # let client = crate:: srp::client:: Client::<sha2::Sha256>::new(&crate::srp::groups::G_2048 );
35+ //! # let client = srp::Client::<srp::G2048, sha2::Sha256>::new();
3636//! # let a = [0u8; 64];
3737//! # let username = b"username";
3838//! # let password = b"password";
4747//! send it to the server and verify server proof in the reply. Note that
4848//! `verify_server` method will return error in case of incorrect server reply.
4949//!
50- //! ```rust
51- //! # let client = crate:: srp::client:: Client::<sha2::Sha256>::new(&crate::srp::groups::G_2048 );
50+ //! ```ignore
51+ //! # let client = srp::Client::<srp::G2048, sha2::Sha256>::new();
5252//! # let verifier = client.process_reply(b"", b"", b"", b"", b"1").unwrap();
5353//! # fn send_proof(_: &[u8]) -> Vec<u8> { 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] }
5454//!
6060//! `key` contains shared secret key between user and the server. You can extract shared secret
6161//! key using `key()` method.
6262//! ```rust
63- //! # let client = crate:: srp::client:: Client::<sha2::Sha256>::new(&crate::srp::groups::G_2048 );
63+ //! # let client = srp::Client::<srp::G2048, sha2::Sha256>::new();
6464//! # let verifier = client.process_reply(b"", b"", b"", b"", b"1").unwrap();
6565//!
6666//! verifier.key();
7272//! M2 differently and also the `verify_server` method will return a shared session
7373//! key in case of correct server data.
7474//!
75- //! ```rust
76- //! # let client = crate:: srp::client:: Client::<sha2::Sha256>::new(&crate::srp::groups::G_2048 );
75+ //! ```ignore
76+ //! # let client = srp::Client::<srp::G2048, sha2::Sha256>::new();
7777//! # let verifier = client.process_reply_rfc5054(b"", b"", b"", b"", b"1").unwrap();
7878//! # fn send_proof(_: &[u8]) -> Vec<u8> { vec![10, 215, 214, 40, 136, 200, 122, 121, 68, 160, 38, 32, 85, 82, 128, 30, 199, 194, 126, 222, 61, 55, 2, 28, 120, 181, 155, 102, 141, 65, 17, 64] }
7979//!
8989//! Man-in-the-middle (MITM) attack for registration.
9090//!
9191//! ```rust
92- //! # let client = crate:: srp::client:: Client::<sha2::Sha256>::new(&crate::srp::groups::G_2048 );
92+ //! # let client = srp::Client::<srp::G2048, sha2::Sha256>::new();
9393//! # let username = b"username";
9494//! # let password = b"password";
9595//! # let salt = b"salt";
102102
103103use alloc:: { borrow:: ToOwned , vec:: Vec } ;
104104use core:: marker:: PhantomData ;
105- use crypto_bigint:: { BoxedUint , ConcatenatingMul , Resize , modular:: BoxedMontyForm } ;
105+ use crypto_bigint:: { BoxedUint , ConcatenatingMul , Odd , Resize , modular:: BoxedMontyForm } ;
106106use digest:: { Digest , Output } ;
107107use subtle:: ConstantTimeEq ;
108108
@@ -113,42 +113,23 @@ use crate::{
113113} ;
114114
115115/// SRP client state before handshake with the server.
116- pub struct Client < D : Digest > {
117- params : & ' static Group ,
116+ pub struct Client < G : Group , D : Digest > {
117+ g : BoxedMontyForm ,
118118 username_in_x : bool ,
119- d : PhantomData < D > ,
119+ d : PhantomData < ( G , D ) > ,
120120}
121121
122- /// SRP client state after handshake with the server.
123- pub struct ClientVerifier < D : Digest > {
124- m1 : Output < D > ,
125- m2 : Output < D > ,
126- key : Vec < u8 > ,
127- }
128-
129- /// RFC 5054 SRP client state after handshake with the server.
130- pub struct ClientVerifierRfc5054 < D : Digest > {
131- m1 : Output < D > ,
132- m2 : Output < D > ,
133- key : Vec < u8 > ,
134- session_key : Vec < u8 > ,
135- }
136-
137- impl < D : Digest > Client < D > {
122+ impl < G : Group , D : Digest > Client < G , D > {
138123 /// Create new SRP client instance.
139124 #[ must_use]
140- pub const fn new ( params : & ' static Group ) -> Self {
141- Self {
142- params,
143- username_in_x : true ,
144- d : PhantomData ,
145- }
125+ pub fn new ( ) -> Self {
126+ Self :: new_with_options ( true )
146127 }
147128
148129 #[ must_use]
149- pub const fn new_with_options ( params : & ' static Group , username_in_x : bool ) -> Self {
130+ pub fn new_with_options ( username_in_x : bool ) -> Self {
150131 Self {
151- params ,
132+ g : G :: g ( ) ,
152133 username_in_x,
153134 d : PhantomData ,
154135 }
@@ -157,7 +138,7 @@ impl<D: Digest> Client<D> {
157138 // v = g^x % N
158139 #[ must_use]
159140 pub fn compute_g_x ( & self , x : & BoxedUint ) -> BoxedUint {
160- self . params . g . pow ( x) . retrieve ( )
141+ self . g . pow ( x) . retrieve ( )
161142 }
162143
163144 // H(<username> | ":" | <raw password>)
@@ -189,17 +170,11 @@ impl<D: Digest> Client<D> {
189170 a : & BoxedUint ,
190171 u : & BoxedUint ,
191172 ) -> BoxedUint {
192- let b_pub = BoxedMontyForm :: new (
193- b_pub. resize ( self . params . n . modulus ( ) . bits_precision ( ) ) ,
194- & self . params . n ,
195- ) ;
196- let k = BoxedMontyForm :: new (
197- k. resize ( self . params . n . modulus ( ) . bits_precision ( ) ) ,
198- & self . params . n ,
199- ) ;
173+ let b_pub = self . monty_form ( b_pub) ;
174+ let k = self . monty_form ( k) ;
200175
201176 // B - kg^x
202- let base = b_pub - k * self . params . g . pow ( x) ;
177+ let base = b_pub - k * self . g . pow ( x) ;
203178
204179 // S = (B - kg^x) ^ (a + ux)
205180 // or
@@ -248,7 +223,7 @@ impl<D: Digest> Client<D> {
248223 & a_pub. to_be_bytes_trimmed_vartime ( ) ,
249224 & b_pub. to_be_bytes_trimmed_vartime ( ) ,
250225 ) ;
251- let k = compute_k :: < D > ( self . params ) ;
226+ let k = compute_k :: < D > ( & self . g ) ;
252227 let identity_hash = Self :: compute_identity_hash ( self . identity_username ( username) , password) ;
253228 let x = Self :: compute_x ( identity_hash. as_slice ( ) , salt) ;
254229
@@ -296,7 +271,7 @@ impl<D: Digest> Client<D> {
296271 & a_pub. to_be_bytes_trimmed_vartime ( ) ,
297272 & b_pub. to_be_bytes_trimmed_vartime ( ) ,
298273 ) ;
299- let k = compute_k :: < D > ( self . params ) ;
274+ let k = compute_k :: < D > ( & self . g ) ;
300275 let identity_hash = Self :: compute_identity_hash ( self . identity_username ( username) , password) ;
301276 let x = Self :: compute_x ( identity_hash. as_slice ( ) , salt) ;
302277
@@ -307,7 +282,7 @@ impl<D: Digest> Client<D> {
307282 let session_key = compute_hash :: < D > ( & premaster_secret) ;
308283
309284 let m1 = compute_m1_rfc5054 :: < D > (
310- self . params ,
285+ & self . g ,
311286 username,
312287 salt,
313288 & a_pub. to_be_bytes_trimmed_vartime ( ) ,
@@ -329,21 +304,39 @@ impl<D: Digest> Client<D> {
329304 } )
330305 }
331306
307+ /// Conditionally include username in the computation of `x`.
308+ fn identity_username < ' a > ( & self , username : & ' a [ u8 ] ) -> & ' a [ u8 ] {
309+ if self . username_in_x { username } else { & [ ] }
310+ }
311+
312+ /// Convert an integer into the Montgomery domain, returning a [`BoxedMontyForm`] modulo `N`.
313+ fn monty_form ( & self , x : & BoxedUint ) -> BoxedMontyForm {
314+ let precision = self . n ( ) . bits_precision ( ) ;
315+ BoxedMontyForm :: new ( x. resize ( precision) , self . g . params ( ) )
316+ }
317+
318+ /// Get the modulus `N`.
319+ fn n ( & self ) -> & Odd < BoxedUint > {
320+ self . g . params ( ) . modulus ( )
321+ }
322+
332323 /// Ensure `b_pub` is non-zero and therefore not maliciously crafted.
333324 fn validate_b_pub ( & self , b_pub : & BoxedUint ) -> Result < ( ) , AuthError > {
334- let n = self . params . n . modulus ( ) . as_nz_ref ( ) ;
325+ let n = self . n ( ) . as_nz_ref ( ) ;
335326
336327 if ( b_pub. resize ( n. bits_precision ( ) ) % n) . is_zero ( ) . into ( ) {
337328 return Err ( AuthError :: IllegalParameter ( "b_pub" . to_owned ( ) ) ) ;
338329 }
339330
340331 Ok ( ( ) )
341332 }
333+ }
342334
343- /// Conditionally include username in the computation of `x`.
344- fn identity_username < ' a > ( & self , username : & ' a [ u8 ] ) -> & ' a [ u8 ] {
345- if self . username_in_x { username } else { & [ ] }
346- }
335+ /// SRP client state after handshake with the server.
336+ pub struct ClientVerifier < D : Digest > {
337+ m1 : Output < D > ,
338+ m2 : Output < D > ,
339+ key : Vec < u8 > ,
347340}
348341
349342impl < D : Digest > ClientVerifier < D > {
@@ -369,6 +362,14 @@ impl<D: Digest> ClientVerifier<D> {
369362 }
370363}
371364
365+ /// RFC 5054 SRP client state after handshake with the server.
366+ pub struct ClientVerifierRfc5054 < D : Digest > {
367+ m1 : Output < D > ,
368+ m2 : Output < D > ,
369+ key : Vec < u8 > ,
370+ session_key : Vec < u8 > ,
371+ }
372+
372373impl < D : Digest > ClientVerifierRfc5054 < D > {
373374 /// Get shared secret key without authenticating server, e.g. for using with
374375 /// authenticated encryption modes. DO NOT USE this method without
0 commit comments