]> git.lizzy.rs Git - PAKEs.git/blob - srp/src/client.rs
a9f7564d5831489151270de884d35a0efc6f922d
[PAKEs.git] / srp / src / client.rs
1 //! SRP client implementation.
2 //!
3 //! # Usage
4 //! First create SRP client struct by passing to it SRP parameters (shared
5 //! between client and server) and randomly generated `a`:
6 //!
7 //! ```ignore
8 //! use srp::groups::G_2048;
9 //! use sha2::Sha256;
10 //!
11 //! let mut a = [0u8; 64];
12 //! rng.fill_bytes(&mut a);
13 //! let client = SrpClient::<Sha256>::new(&a, &G_2048);
14 //! ```
15 //!
16 //! Next send handshake data (username and `a_pub`) to the server and receive
17 //! `salt` and `b_pub`:
18 //!
19 //! ```ignore
20 //! let a_pub = client.get_a_pub();
21 //! let (salt, b_pub) = conn.send_handshake(username, a_pub);
22 //! ```
23 //!
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`.
29 //!
30 //! ```ignore
31 //! let private_key = srp_private_key::<Sha256>(username, password, salt);
32 //! let verifier = client.get_verifier(&private_key, &b_pub)?;
33 //! ```
34 //!
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.
38 //!
39 //! ```ignore
40 //! let user_proof = verifier.get_proof();
41 //! let server_proof = conn.send_proof(user_proof);
42 //! let key = verifier.verify_server(server_proof)?;
43 //! ```
44 //!
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).
49 //!
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 useranme, salt
52 //! and password verifier over protected channel to protect against
53 //! Man-in-the-middle (MITM) attack for registration.
54 //!
55 //! ```ignore
56 //! let pwd_verifier = client.get_password_verifier(&private_key);
57 //! conn.send_registration_data(username, salt, pwd_verifier);
58 //! ```
59 use std::marker::PhantomData;
60
61 use digest::{Digest, Output};
62 use num_bigint::BigUint;
63
64 use crate::tools::powm;
65 use crate::types::{SrpAuthError, SrpGroup};
66
67 /// SRP client state before handshake with the server.
68 pub struct SrpClient<'a, D: Digest> {
69     params: &'a SrpGroup,
70
71     a: BigUint,
72     a_pub: BigUint,
73
74     d: PhantomData<D>,
75 }
76
77 /// SRP client state after handshake with the server.
78 pub struct SrpClientVerifier<D: Digest> {
79     proof: Output<D>,
80     server_proof: Output<D>,
81     key: Output<D>,
82 }
83
84 /// Compute user private key as described in the RFC 5054. Consider using proper
85 /// password hashing algorithm instead.
86 pub fn srp_private_key<D: Digest>(username: &[u8], password: &[u8], salt: &[u8]) -> Output<D> {
87     let p = {
88         let mut d = D::new();
89         d.update(username);
90         d.update(b":");
91         d.update(password);
92         d.finalize()
93     };
94     let mut d = D::new();
95     d.update(salt);
96     d.update(p.as_slice());
97     d.finalize()
98 }
99
100 impl<'a, D: Digest> SrpClient<'a, D> {
101     /// Create new SRP client instance.
102     pub fn new(a: &[u8], params: &'a SrpGroup) -> Self {
103         let a = BigUint::from_bytes_be(a);
104         let a_pub = params.powm(&a);
105
106         Self {
107             params,
108             a,
109             a_pub,
110             d: Default::default(),
111         }
112     }
113
114     /// Get password verfier for user registration on the server
115     pub fn get_password_verifier(&self, private_key: &[u8]) -> Vec<u8> {
116         let x = BigUint::from_bytes_be(private_key);
117         let v = self.params.powm(&x);
118         v.to_bytes_be()
119     }
120
121     fn calc_key(&self, b_pub: &BigUint, x: &BigUint, u: &BigUint) -> Output<D> {
122         let n = &self.params.n;
123         let k = self.params.compute_k::<D>();
124         let interm = (k * self.params.powm(x)) % n;
125         // Because we do operation in modulo N we can get: (kv + g^b) < kv
126         let v = if *b_pub > interm {
127             (b_pub - &interm) % n
128         } else {
129             (n + b_pub - &interm) % n
130         };
131         // S = |B - kg^x| ^ (a + ux)
132         let s = powm(&v, &(&self.a + (u * x) % n), n);
133         D::digest(&s.to_bytes_be())
134     }
135
136     /// Process server reply to the handshake.
137     pub fn process_reply(
138         self,
139         private_key: &[u8],
140         b_pub: &[u8],
141     ) -> Result<SrpClientVerifier<D>, SrpAuthError> {
142         let u = {
143             let mut d = D::new();
144             d.update(&self.a_pub.to_bytes_be());
145             d.update(b_pub);
146             let h = d.finalize();
147             BigUint::from_bytes_be(h.as_slice())
148         };
149
150         let b_pub = BigUint::from_bytes_be(b_pub);
151
152         // Safeguard against malicious B
153         if &b_pub % &self.params.n == BigUint::default() {
154             return Err(SrpAuthError {
155                 description: "Malicious b_pub value",
156             });
157         }
158
159         let x = BigUint::from_bytes_be(private_key);
160         let key = self.calc_key(&b_pub, &x, &u);
161         // M1 = H(A, B, K)
162         let proof = {
163             let mut d = D::new();
164             d.update(&self.a_pub.to_bytes_be());
165             d.update(&b_pub.to_bytes_be());
166             d.update(&key);
167             d.finalize()
168         };
169
170         // M2 = H(A, M1, K)
171         let server_proof = {
172             let mut d = D::new();
173             d.update(&self.a_pub.to_bytes_be());
174             d.update(&proof);
175             d.update(&key);
176             d.finalize()
177         };
178
179         Ok(SrpClientVerifier {
180             proof,
181             server_proof,
182             key,
183         })
184     }
185
186     /// Process server reply to the handshake with username and salt.
187     pub fn process_reply_with_username_and_salt(
188         self,
189         username: &[u8],
190         salt: &[u8],
191         private_key: &[u8],
192         b_pub: &[u8],
193     ) -> Result<SrpClientVerifier<D>, SrpAuthError> {
194         let u = {
195             let mut d = D::new();
196             d.update(&self.a_pub.to_bytes_be());
197             d.update(b_pub);
198             let h = d.finalize();
199             BigUint::from_bytes_be(h.as_slice())
200         };
201
202         let b_pub = BigUint::from_bytes_be(b_pub);
203
204         // Safeguard against malicious B
205         if &b_pub % &self.params.n == BigUint::default() {
206             return Err(SrpAuthError {
207                 description: "Malicious b_pub value",
208             });
209         }
210
211         let x = BigUint::from_bytes_be(private_key);
212         let key = self.calc_key(&b_pub, &x, &u);
213         // M1 = H(H(N)^H(g), H(I), salt, A, B, K)
214         let proof = {
215             let mut d = D::new();
216             d.update(username);
217             let h = d.finalize_reset();
218             let I: &[u8] = h.as_slice();
219
220             d.update(self.params.compute_hash_n_xor_hash_g::<D>());
221             d.update(I);
222             d.update(salt);
223             d.update(&self.a_pub.to_bytes_be());
224             d.update(&b_pub.to_bytes_be());
225             d.update(&key.to_vec());
226             d.finalize()
227         };
228         let x = proof.to_vec().as_slice();
229
230         // M2 = H(A, M1, K)
231         let server_proof = {
232             let mut d = D::new();
233             d.update(&self.a_pub.to_bytes_be());
234             d.update(&proof);
235             d.update(&key);
236             d.finalize()
237         };
238
239         Ok(SrpClientVerifier {
240             proof,
241             server_proof,
242             key,
243         })
244     }
245
246     /// Get public ephemeral value for handshaking with the server.
247     pub fn get_a_pub(&self) -> Vec<u8> {
248         self.a_pub.to_bytes_be()
249     }
250 }
251
252 impl<D: Digest> SrpClientVerifier<D> {
253     /// Get shared secret key without authenticating server, e.g. for using with
254     /// authenticated encryption modes. DO NOT USE this method without
255     /// some kind of secure authentication
256     pub fn get_key(self) -> Output<D> {
257         self.key
258     }
259
260     /// Verification data for sending to the server.
261     pub fn get_proof(&self) -> Output<D> {
262         self.proof.clone()
263     }
264
265     /// Verify server reply to verification data. It will return shared secret
266     /// key in case of success.
267     pub fn verify_server(self, reply: &[u8]) -> Result<Output<D>, SrpAuthError> {
268         if self.server_proof.as_slice() != reply {
269             Err(SrpAuthError {
270                 description: "Incorrect server proof",
271             })
272         } else {
273             Ok(self.key)
274         }
275     }
276 }