]> git.lizzy.rs Git - PAKEs.git/blob - srp/src/client.rs
travis: cargo fmt now wants --check
[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 a = rng.gen_iter::<u8>().take(64).collect::<Vec<u8>>();
12 //! let client = SrpClient::<Sha256>::new(&a, &G_2048);
13 //! ```
14 //! 
15 //! Next send handshake data (username and `a_pub`) to the server and receive
16 //! `salt` and `b_pub`:
17 //! 
18 //! ```ignore
19 //! let a_pub = client.get_a_pub();
20 //! let (salt, b_pub) = conn.send_handshake(username, a_pub);
21 //! ```
22 //! 
23 //! Compute private key using `salt` with any password hashing function.
24 //! You can use method from SRP-6a, but it's recommended to use specialized
25 //! password hashing algorithm instead (e.g. PBKDF2, argon2 or scrypt).
26 //! Next create verifier instance, note that `get_verifier` consumes client and
27 //! can return error in case of malicious `b_pub`.
28 //! 
29 //! ```ignore
30 //! let private_key = srp_private_key::<Sha256>(username, password, salt);
31 //! let verifier = client.get_verifier(&private_key, &b_pub)?;
32 //! ```
33 //!
34 //! Finally verify the server: first generate user proof,
35 //! send it to the server and verify server proof in the reply. Note that
36 //! `verify_server` method will return error in case of incorrect server reply.
37 //! 
38 //! ```ignore
39 //! let user_proof = verifier.get_proof();
40 //! let server_proof = conn.send_proof(user_proof);
41 //! let key = verifier.verify_server(server_proof)?;
42 //! ```
43 //! 
44 //! `key` contains shared secret key between user and the server. Alternatively
45 //! you can directly extract shared secret key using `get_key()` method and
46 //! handle authentification through different (secure!) means (e.g. by using
47 //! authentificated cipher mode).
48 //! 
49 //! For user registration on the server first generate salt (e.g. 32 bytes long)
50 //! and get password verifier which depends on private key. Send useranme, salt
51 //! and password verifier over protected channel to protect against
52 //! Man-in-the-middle (MITM) attack for registration.
53 //! 
54 //! ```ignore
55 //! let pwd_verifier = client.get_password_verifier(&private_key);
56 //! conn.send_registration_data(username, salt, pwd_verifier);
57 //! ```
58
59 //let buf = rng.gen_iter::<u8>().take(l).collect::<Vec<u8>>();
60 use std::marker::PhantomData;
61
62 use num::{BigUint, Zero};
63 use digest::Digest;
64 use generic_array::GenericArray;
65
66 use tools::powm;
67 use 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>(username: &[u8], password: &[u8], salt: &[u8])
89     -> GenericArray<u8, D::OutputSize>
90 {
91     let p = {
92         let mut d = D::new();
93         d.input(username);
94         d.input(b":");
95         d.input(password);
96         d.result()
97     };
98     let mut d = D::new();
99     d.input(salt);
100     d.input(&p);
101     d.result()
102 }
103
104 impl<'a, D: Digest> SrpClient<'a, D> {
105     /// Create new SRP client instance.
106     pub fn new(a: &[u8], params: &'a SrpGroup) -> Self {
107         let a = BigUint::from_bytes_be(a);
108         let a_pub = params.powm(&a);
109
110         Self { params, a, a_pub, d: Default::default() }
111     }
112
113     /// Get password verfier for user registration on the server
114     pub fn get_password_verifier(&self, private_key: &[u8]) -> Vec<u8> {
115         let x = BigUint::from_bytes_be(private_key);
116         let v = self.params.powm(&x);
117         v.to_bytes_be()
118     }
119
120     fn calc_key(&self, b_pub: &BigUint, x: &BigUint, u: &BigUint)
121         -> GenericArray<u8, D::OutputSize>
122     {
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
129         } else {
130             (n + b_pub - &interm) % n
131         };
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())
135     }
136
137     /// Process server reply to the handshake.
138     pub fn process_reply(self, private_key: &[u8], b_pub: &[u8])
139         -> Result<SrpClientVerifier<D>, SrpAuthError>
140     {
141         let u = {
142             let mut d = D::new();
143             d.input(&self.a_pub.to_bytes_be());
144             d.input(b_pub);
145             BigUint::from_bytes_be(&d.result())
146         };
147
148         let b_pub = BigUint::from_bytes_be(b_pub);
149
150         // Safeguard against malicious B
151         if &b_pub % &self.params.n == BigUint::zero() {
152             return Err(SrpAuthError{ description: "Malicious b_pub value" })
153         }
154
155         let x = BigUint::from_bytes_be(private_key);
156         let key = self.calc_key(&b_pub, &x, &u);
157         // M1 = H(A, B, K)
158         let proof = {
159             let mut d = D::new();
160             d.input(&self.a_pub.to_bytes_be());
161             d.input(&b_pub.to_bytes_be());
162             d.input(&key);
163             d.result()
164         };
165
166         // M2 = H(A, M1, K)
167         let server_proof = {
168             let mut d = D::new();
169             d.input(&self.a_pub.to_bytes_be());
170             d.input(&proof);
171             d.input(&key);
172             d.result()
173         };
174
175         Ok(SrpClientVerifier {
176             proof: proof,
177             server_proof: server_proof,
178             key: key,
179         })
180     }
181
182     /// Get public ephemeral value for handshaking with the server.
183     pub fn get_a_pub(&self) -> Vec<u8> {
184         self.a_pub.to_bytes_be()
185     }
186 }
187
188 impl<D: Digest> SrpClientVerifier<D> {
189     /// Get shared secret key without authenticating server, e.g. for using with
190     /// authenticated encryption modes. DO NOT USE this method without
191     /// some kind of secure authentification.
192     pub fn get_key(self) -> GenericArray<u8, D::OutputSize> {
193         self.key
194     }
195
196     /// Verification data for sending to the server.
197     pub fn get_proof(&self) -> GenericArray<u8, D::OutputSize> {
198         self.proof.clone()
199     }
200
201     /// Verify server reply to verification data. It will return shared secret
202     /// key in case of success.
203     pub fn verify_server(self, reply: &[u8])
204         -> Result<GenericArray<u8, D::OutputSize>, SrpAuthError>
205     {
206         if self.server_proof.as_slice() != reply {
207             Err(SrpAuthError{ description: "Incorrect server proof" })
208         } else {
209             Ok(self.key)
210         }
211     }
212 }