]> git.lizzy.rs Git - PAKEs.git/blob - srp/src/client.rs
db4c5767d772a0fb06b02de014776925e00b0e25
[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;
62 use generic_array::GenericArray;
63 use num_bigint::BigUint;
64 use num_traits::Zero;
65
66 use crate::tools::powm;
67 use crate::types::{SrpAuthError, SrpGroup};
68
69 /// SRP client state before handshake with the server.
70 pub struct SrpClient<'a, D: Digest> {
71     params: &'a SrpGroup,
72
73     a: BigUint,
74     a_pub: BigUint,
75
76     d: PhantomData<D>,
77 }
78
79 /// SRP client state after handshake with the server.
80 pub struct SrpClientVerifier<D: Digest> {
81     proof: GenericArray<u8, D::OutputSize>,
82     server_proof: GenericArray<u8, D::OutputSize>,
83     key: GenericArray<u8, D::OutputSize>,
84 }
85
86 /// Compute user private key as described in the RFC 5054. Consider using proper
87 /// password hashing algorithm instead.
88 pub fn srp_private_key<D: Digest>(
89     username: &[u8],
90     password: &[u8],
91     salt: &[u8],
92 ) -> GenericArray<u8, D::OutputSize> {
93     let p = {
94         let mut d = D::new();
95         d.input(username);
96         d.input(b":");
97         d.input(password);
98         d.result()
99     };
100     let mut d = D::new();
101     d.input(salt);
102     d.input(&p);
103     d.result()
104 }
105
106 impl<'a, D: Digest> SrpClient<'a, D> {
107     /// Create new SRP client instance.
108     pub fn new(a: &[u8], params: &'a SrpGroup) -> Self {
109         let a = BigUint::from_bytes_be(a);
110         let a_pub = params.powm(&a);
111
112         Self {
113             params,
114             a,
115             a_pub,
116             d: Default::default(),
117         }
118     }
119
120     /// Get password verfier for user registration on the server
121     pub fn get_password_verifier(&self, private_key: &[u8]) -> Vec<u8> {
122         let x = BigUint::from_bytes_be(private_key);
123         let v = self.params.powm(&x);
124         v.to_bytes_be()
125     }
126
127     fn calc_key(
128         &self,
129         b_pub: &BigUint,
130         x: &BigUint,
131         u: &BigUint,
132     ) -> GenericArray<u8, D::OutputSize> {
133         let n = &self.params.n;
134         let k = self.params.compute_k::<D>();
135         let interm = (k * self.params.powm(x)) % n;
136         // Because we do operation in modulo N we can get: (kv + g^b) < kv
137         let v = if *b_pub > interm {
138             (b_pub - &interm) % n
139         } else {
140             (n + b_pub - &interm) % n
141         };
142         // S = |B - kg^x| ^ (a + ux)
143         let s = powm(&v, &(&self.a + (u * x) % n), n);
144         D::digest(&s.to_bytes_be())
145     }
146
147     /// Process server reply to the handshake.
148     pub fn process_reply(
149         self,
150         private_key: &[u8],
151         b_pub: &[u8],
152     ) -> Result<SrpClientVerifier<D>, SrpAuthError> {
153         let u = {
154             let mut d = D::new();
155             d.input(&self.a_pub.to_bytes_be());
156             d.input(b_pub);
157             BigUint::from_bytes_be(&d.result())
158         };
159
160         let b_pub = BigUint::from_bytes_be(b_pub);
161
162         // Safeguard against malicious B
163         if &b_pub % &self.params.n == BigUint::zero() {
164             return Err(SrpAuthError {
165                 description: "Malicious b_pub value",
166             });
167         }
168
169         let x = BigUint::from_bytes_be(private_key);
170         let key = self.calc_key(&b_pub, &x, &u);
171         // M1 = H(A, B, K)
172         let proof = {
173             let mut d = D::new();
174             d.input(&self.a_pub.to_bytes_be());
175             d.input(&b_pub.to_bytes_be());
176             d.input(&key);
177             d.result()
178         };
179
180         // M2 = H(A, M1, K)
181         let server_proof = {
182             let mut d = D::new();
183             d.input(&self.a_pub.to_bytes_be());
184             d.input(&proof);
185             d.input(&key);
186             d.result()
187         };
188
189         Ok(SrpClientVerifier {
190             proof,
191             server_proof,
192             key,
193         })
194     }
195
196     /// Get public ephemeral value for handshaking with the server.
197     pub fn get_a_pub(&self) -> Vec<u8> {
198         self.a_pub.to_bytes_be()
199     }
200 }
201
202 impl<D: Digest> SrpClientVerifier<D> {
203     /// Get shared secret key without authenticating server, e.g. for using with
204     /// authenticated encryption modes. DO NOT USE this method without
205     /// some kind of secure authentication
206     pub fn get_key(self) -> GenericArray<u8, D::OutputSize> {
207         self.key
208     }
209
210     /// Verification data for sending to the server.
211     pub fn get_proof(&self) -> GenericArray<u8, D::OutputSize> {
212         self.proof.clone()
213     }
214
215     /// Verify server reply to verification data. It will return shared secret
216     /// key in case of success.
217     pub fn verify_server(
218         self,
219         reply: &[u8],
220     ) -> Result<GenericArray<u8, D::OutputSize>, SrpAuthError> {
221         if self.server_proof.as_slice() != reply {
222             Err(SrpAuthError {
223                 description: "Incorrect server proof",
224             })
225         } else {
226             Ok(self.key)
227         }
228     }
229 }