]> git.lizzy.rs Git - PAKEs.git/blob - srp/src/server.rs
rfc compliant client proof
[PAKEs.git] / srp / src / server.rs
1 //! SRP server implementation
2 //!
3 //! # Usage
4 //! First receive user's username and public value `a_pub`, retrieve from a
5 //! database the salt and verifier for a given username. Generate `b` and public value `b_pub`.
6 //!
7 //!
8 //! ```rust
9 //! use crate::srp::groups::G_2048;
10 //! use sha2::Sha256; // Note: You should probably use a proper password KDF
11 //! # use crate::srp::server::SrpServer;
12 //! # fn get_client_request()-> (Vec<u8>, Vec<u8>) { (vec![], vec![])}
13 //! # fn get_user(_: &[u8])-> (Vec<u8>, Vec<u8>) { (vec![], vec![])}
14 //!
15 //! let server = SrpServer::<Sha256>::new(&G_2048);
16 //! let (username, a_pub) = get_client_request();
17 //! let (salt, v) = get_user(&username);
18 //! let mut b = [0u8; 64];
19 //! // rng.fill_bytes(&mut b);
20 //! let b_pub = server.compute_public_ephemeral(&b, &v);
21 //! ```
22 //!
23 //! Next send to user `b_pub` and `salt` from user record
24 //!
25 //! Next process the user response:
26 //!
27 //! ```rust
28 //! # let server = crate::srp::server::SrpServer::<sha2::Sha256>::new(&crate::srp::groups::G_2048);
29 //! # fn get_client_response() -> Vec<u8> { vec![1] }
30 //! # let b = [0u8; 64];
31 //! # let v = b"";
32 //!
33 //! let a_pub = get_client_response();
34 //! let verifier = server.process_reply(&b, v, &a_pub).unwrap();
35 //! ```
36 //!
37 //!
38 //! And finally receive user proof, verify it and send server proof in the
39 //! reply:
40 //!
41 //! ```rust
42 //! # let server = crate::srp::server::SrpServer::<sha2::Sha256>::new(&crate::srp::groups::G_2048);
43 //! # let verifier = server.process_reply(b"", b"", b"1").unwrap();
44 //! # fn get_client_proof()-> Vec<u8> { vec![26, 80, 8, 243, 111, 162, 238, 171, 208, 237, 207, 46, 46, 137, 44, 213, 105, 208, 84, 224, 244, 216, 103, 145, 14, 103, 182, 56, 242, 4, 179, 57] };
45 //! # fn send_proof(_: &[u8]) { };
46 //!
47 //! let client_proof = get_client_proof();
48 //! verifier.verify_client(&client_proof).unwrap();
49 //! send_proof(verifier.proof());
50 //! ```
51 //!
52 //!
53 //! `key` contains shared secret key between user and the server. You can extract shared secret
54 //! key using `key()` method.
55 //! ```rust
56 //! # let server = crate::srp::server::SrpServer::<sha2::Sha256>::new(&crate::srp::groups::G_2048);
57 //! # let verifier = server.process_reply(b"", b"", b"1").unwrap();
58 //!
59 //! verifier.key();
60 //!```
61 //!
62 use std::marker::PhantomData;
63
64 use digest::{Digest, Output};
65 use num_bigint::BigUint;
66 use subtle::ConstantTimeEq;
67
68 use crate::types::{SrpAuthError, SrpGroup};
69 use crate::utils::{compute_k, compute_m1, compute_m2, compute_u};
70
71 /// SRP server state
72 pub struct SrpServer<'a, D: Digest> {
73     params: &'a SrpGroup,
74     d: PhantomData<D>,
75 }
76
77 /// SRP server state after handshake with the client.
78 pub struct SrpServerVerifier<D: Digest> {
79     m1: Output<D>,
80     m2: Output<D>,
81     key: Vec<u8>,
82 }
83
84 impl<'a, D: Digest> SrpServer<'a, D> {
85     /// Create new server state.
86     pub fn new(params: &'a SrpGroup) -> Self {
87         Self {
88             params,
89             d: Default::default(),
90         }
91     }
92
93     //  k*v + g^b % N
94     pub fn compute_b_pub(&self, b: &BigUint, k: &BigUint, v: &BigUint) -> BigUint {
95         let inter = (k * v) % &self.params.n;
96         (inter + self.params.g.modpow(b, &self.params.n)) % &self.params.n
97     }
98
99     // <premaster secret> = (A * v^u) ^ b % N
100     pub fn compute_premaster_secret(
101         &self,
102         a_pub: &BigUint,
103         v: &BigUint,
104         u: &BigUint,
105         b: &BigUint,
106     ) -> BigUint {
107         // (A * v^u)
108         let base = (a_pub * v.modpow(u, &self.params.n)) % &self.params.n;
109         base.modpow(b, &self.params.n)
110     }
111
112     /// Get public ephemeral value for sending to the client.
113     pub fn compute_public_ephemeral(&self, b: &[u8], v: &[u8]) -> Vec<u8> {
114         self.compute_b_pub(
115             &BigUint::from_bytes_be(b),
116             &compute_k::<D>(self.params),
117             &BigUint::from_bytes_be(v),
118         )
119         .to_bytes_be()
120     }
121
122     /// Process client reply to the handshake.
123     /// b is a random value,
124     /// v is the provided during initial user registration
125     pub fn process_reply(
126         &self,
127         b: &[u8],
128         v: &[u8],
129         a_pub: &[u8],
130     ) -> Result<SrpServerVerifier<D>, SrpAuthError> {
131         let b = BigUint::from_bytes_be(b);
132         let v = BigUint::from_bytes_be(v);
133         let a_pub = BigUint::from_bytes_be(a_pub);
134
135         let k = compute_k::<D>(self.params);
136         let b_pub = self.compute_b_pub(&b, &k, &v);
137
138         // Safeguard against malicious A
139         if &a_pub % &self.params.n == BigUint::default() {
140             return Err(SrpAuthError::IllegalParameter("a_pub".to_owned()));
141         }
142
143         let u = compute_u::<D>(&a_pub.to_bytes_be(), &b_pub.to_bytes_be());
144
145         let key = self.compute_premaster_secret(&a_pub, &v, &u, &b);
146
147         let m1 = compute_m1::<D>(
148             self.params,
149             &a_pub.to_bytes_be(),
150             &b_pub.to_bytes_be(),
151             &key.to_bytes_be(),
152         );
153
154         let m2 = compute_m2::<D>(&a_pub.to_bytes_be(), &m1, &key.to_bytes_be());
155
156         Ok(SrpServerVerifier {
157             m1,
158             m2,
159             key: key.to_bytes_be(),
160         })
161     }
162 }
163
164 impl<D: Digest> SrpServerVerifier<D> {
165     /// Get shared secret between user and the server. (do not forget to verify
166     /// that keys are the same!)
167     pub fn key(&self) -> &[u8] {
168         &self.key
169     }
170
171     /// Verification data for sending to the client.
172     pub fn proof(&self) -> &[u8] {
173         // TODO not Output
174         self.m2.as_slice()
175     }
176
177     /// Process user proof of having the same shared secret.
178     pub fn verify_client(&self, reply: &[u8]) -> Result<(), SrpAuthError> {
179         if self.m1.ct_eq(reply).unwrap_u8() != 1 {
180             // aka == 0
181             Err(SrpAuthError::BadRecordMac("client".to_owned()))
182         } else {
183             Ok(())
184         }
185     }
186 }