]> git.lizzy.rs Git - PAKEs.git/blob - srp/src/client.rs
include username in client proof
[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).
6 //!
7 //! You can use SHA1 from SRP-6a, but it's highly recommended to use specialized
8 //! password hashing algorithm instead (e.g. PBKDF2, argon2 or scrypt).
9 //!
10 //! ```rust
11 //! use crate::srp::groups::G_2048;
12 //! use sha2::Sha256; // Note: You should probably use a proper password KDF
13 //! # use crate::srp::client::SrpClient;
14 //!
15 //! let client = SrpClient::<Sha256>::new(&G_2048);
16 //! ```
17 //!
18 //! Next send handshake data (username and `a_pub`) to the server and receive
19 //! `salt` and `b_pub`:
20 //!
21 //! ```rust
22 //! # let client = crate::srp::client::SrpClient::<sha2::Sha256>::new(&crate::srp::groups::G_2048);
23 //! # fn server_response()-> (Vec<u8>, Vec<u8>) { (vec![], vec![]) }
24 //!
25 //! let mut a = [0u8; 64];
26 //! // rng.fill_bytes(&mut a);
27 //! let a_pub = client.compute_public_ephemeral(&a);
28 //! let (salt, b_pub) = server_response();
29 //! ```
30 //!
31 //! Process the server response and create verifier instance.
32 //! process_reply can return error in case of malicious `b_pub`.
33 //!
34 //! ```rust
35 //! # let client = crate::srp::client::SrpClient::<sha2::Sha256>::new(&crate::srp::groups::G_2048);
36 //! # let a = [0u8; 64];
37 //! # let username = b"username";
38 //! # let password = b"password";
39 //! # let salt = b"salt";
40 //! # let b_pub = b"b_pub";
41 //!
42 //! let private_key = (username, password, salt);
43 //! let verifier = client.process_reply(&a, username, password, salt, b_pub);
44 //! ```
45 //!
46 //! Finally verify the server: first generate user proof,
47 //! send it to the server and verify server proof in the reply. Note that
48 //! `verify_server` method will return error in case of incorrect server reply.
49 //!
50 //! ```rust
51 //! # let client = crate::srp::client::SrpClient::<sha2::Sha256>::new(&crate::srp::groups::G_2048);
52 //! # let verifier = client.process_reply(b"", b"", b"", b"", b"1").unwrap();
53 //! # 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]   }
54 //!
55 //! let client_proof = verifier.proof();
56 //! let server_proof = send_proof(client_proof);
57 //! verifier.verify_server(&server_proof).unwrap();
58 //! ```
59 //!
60 //! `key` contains shared secret key between user and the server. You can extract shared secret
61 //! key using `key()` method.
62 //! ```rust
63 //! # let client = crate::srp::client::SrpClient::<sha2::Sha256>::new(&crate::srp::groups::G_2048);
64 //! # let verifier = client.process_reply(b"", b"", b"", b"", b"1").unwrap();
65 //!
66 //! verifier.key();
67 //!```
68 //!
69 //!
70 //! For user registration on the server first generate salt (e.g. 32 bytes long)
71 //! and get password verifier which depends on private key. Send username, salt
72 //! and password verifier over protected channel to protect against
73 //! Man-in-the-middle (MITM) attack for registration.
74 //!
75 //! ```rust
76 //! # let client = crate::srp::client::SrpClient::<sha2::Sha256>::new(&crate::srp::groups::G_2048);
77 //! # let username = b"username";
78 //! # let password = b"password";
79 //! # let salt = b"salt";
80 //! # fn send_registration_data(_: &[u8], _: &[u8], _: &[u8]) {}
81 //!
82 //! let pwd_verifier = client.compute_verifier(username, password, salt);
83 //! send_registration_data(username, salt, &pwd_verifier);
84 //! ```
85
86 use std::marker::PhantomData;
87
88 use digest::{Digest, Output};
89 use num_bigint::BigUint;
90 use subtle::ConstantTimeEq;
91
92 use crate::types::{SrpAuthError, SrpGroup};
93 use crate::utils::{compute_k, compute_m1, compute_m2, compute_u};
94
95 /// SRP client state before handshake with the server.
96 pub struct SrpClient<'a, D: Digest> {
97     params: &'a SrpGroup,
98     d: PhantomData<D>,
99 }
100
101 /// SRP client state after handshake with the server.
102 pub struct SrpClientVerifier<D: Digest> {
103     m1: Output<D>,
104     m2: Output<D>,
105     key: Vec<u8>,
106 }
107
108 impl<'a, D: Digest> SrpClient<'a, D> {
109     /// Create new SRP client instance.
110     pub fn new(params: &'a SrpGroup) -> Self {
111         Self {
112             params,
113             d: Default::default(),
114         }
115     }
116
117     pub fn compute_a_pub(&self, a: &BigUint) -> BigUint {
118         self.params.g.modpow(a, &self.params.n)
119     }
120
121     //  H(<username> | ":" | <raw password>)
122     pub fn compute_identity_hash(username: &[u8], password: &[u8]) -> Output<D> {
123         let mut d = D::new();
124         d.update(username);
125         d.update(b":");
126         d.update(password);
127         d.finalize()
128     }
129
130     // x = H(<salt> | H(<username> | ":" | <raw password>))
131     pub fn compute_x(identity_hash: &[u8], salt: &[u8]) -> BigUint {
132         let mut x = D::new();
133         x.update(salt);
134         x.update(identity_hash);
135         BigUint::from_bytes_be(&x.finalize())
136     }
137
138     // (B - (k * g^x)) ^ (a + (u * x)) % N
139     pub fn compute_premaster_secret(
140         &self,
141         b_pub: &BigUint,
142         k: &BigUint,
143         x: &BigUint,
144         a: &BigUint,
145         u: &BigUint,
146     ) -> BigUint {
147         // (k * g^x)
148         let base = (k * (self.params.g.modpow(x, &self.params.n))) % &self.params.n;
149         // 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.
150         // B - kg^x
151         let base = ((&self.params.n + b_pub) - &base) % &self.params.n;
152         let exp = (u * x) + a;
153         // S = (B - kg^x) ^ (a + ux)
154         // or
155         // S = base ^ exp
156         base.modpow(&exp, &self.params.n)
157     }
158
159     // v = g^x % N
160     pub fn compute_v(&self, x: &BigUint) -> BigUint {
161         self.params.g.modpow(x, &self.params.n)
162     }
163
164     /// Get password verifier (v in RFC5054) for user registration on the server.
165     pub fn compute_verifier(&self, username: &[u8], password: &[u8], salt: &[u8]) -> Vec<u8> {
166         let identity_hash = Self::compute_identity_hash(username, password);
167         let x = Self::compute_x(identity_hash.as_slice(), salt);
168         self.compute_v(&x).to_bytes_be()
169     }
170
171     /// Get public ephemeral value for handshaking with the server.
172     /// g^a % N
173     pub fn compute_public_ephemeral(&self, a: &[u8]) -> Vec<u8> {
174         self.compute_a_pub(&BigUint::from_bytes_be(a)).to_bytes_be()
175     }
176
177     /// Process server reply to the handshake.
178     /// a is a random value,
179     /// username, password is supplied by the user
180     /// salt and b_pub come from the server
181     pub fn process_reply(
182         &self,
183         a: &[u8],
184         username: &[u8],
185         password: &[u8],
186         salt: &[u8],
187         b_pub: &[u8],
188     ) -> Result<SrpClientVerifier<D>, SrpAuthError> {
189         let a = BigUint::from_bytes_be(a);
190         let a_pub = self.compute_a_pub(&a);
191         let b_pub = BigUint::from_bytes_be(b_pub);
192
193         // Safeguard against malicious B
194         if &b_pub % &self.params.n == BigUint::default() {
195             return Err(SrpAuthError::IllegalParameter("b_pub".to_owned()));
196         }
197
198         let u = compute_u::<D>(&a_pub.to_bytes_be(), &b_pub.to_bytes_be());
199         let k = compute_k::<D>(self.params);
200         let identity_hash = Self::compute_identity_hash(username, password);
201         let x = Self::compute_x(identity_hash.as_slice(), salt);
202
203         let key = self.compute_premaster_secret(&b_pub, &k, &x, &a, &u);
204
205         let m1 = compute_m1::<D>(
206             self.params,
207             identity_hash.as_slice(),
208             &a_pub.to_bytes_be(),
209             &b_pub.to_bytes_be(),
210             &key.to_bytes_be(),
211         );
212
213         let m2 = compute_m2::<D>(&a_pub.to_bytes_be(), &m1, &key.to_bytes_be());
214
215         Ok(SrpClientVerifier {
216             m1,
217             m2,
218             key: key.to_bytes_be(),
219         })
220     }
221 }
222
223 impl<D: Digest> SrpClientVerifier<D> {
224     /// Get shared secret key without authenticating server, e.g. for using with
225     /// authenticated encryption modes. DO NOT USE this method without
226     /// some kind of secure authentication
227     pub fn key(&self) -> &[u8] {
228         &self.key
229     }
230
231     /// Verification data for sending to the server.
232     pub fn proof(&self) -> &[u8] {
233         self.m1.as_slice()
234     }
235
236     /// Verify server reply to verification data.
237     pub fn verify_server(&self, reply: &[u8]) -> Result<(), SrpAuthError> {
238         if self.m2.ct_eq(reply).unwrap_u8() != 1 {
239             // aka == 0
240             Err(SrpAuthError::BadRecordMac("server".to_owned()))
241         } else {
242             Ok(())
243         }
244     }
245 }