rust_srp/
lib.rs

1#[allow(dead_code)]
2mod hash_helper;
3pub mod bigint_helper;
4
5use std::borrow::Borrow;
6use std::ops::{Add, Mul};
7
8use num::{BigUint, Zero};
9use sha2::Sha256;
10use bigint_helper::{convert_to_bigint};
11
12
13use std::io::{Error, ErrorKind};
14use crate::hash_helper::hash;
15
16#[derive(Debug)]
17pub struct SrpConfig {
18    n: BigUint,
19    g: BigUint,
20}
21
22impl SrpConfig {
23    fn new(n: BigUint, g: BigUint) -> Self {
24        SrpConfig {
25            n,
26            g
27        }
28    }
29}
30
31/// Computes the SRP-6 multiplier k = H(N | g)
32///
33/// Specification: RFC 5054.
34/// # Arguments
35///
36/// * `SrpConfig` - srp configuration {n, g}
37///
38/// The resulting multiplier 'k'.
39fn compute_k(config: &SrpConfig) -> BigUint {
40    let k = hash::<Sha256>(&[config.n.to_bytes_be().as_slice(), config.g.to_bytes_be().as_slice()]);
41    bigint_helper::convert_to_bigint(k.as_slice(), 16).unwrap()
42}
43
44/// Computes x = H(s | P)
45///
46/// Note that this method differs from the RFC 5054 recommendation
47/// which includes the user identity 'I', i.e. x = H(s | H(I | ":" | P))
48///
49/// # Arguments
50///
51/// * `salt` - small random unsigned salt
52/// * `password` - the client password
53///
54/// The resulting 'x'.
55pub fn compute_x(salt: &BigUint, password: &str) -> BigUint {
56    let x = hash::<Sha256>(&[salt.to_bytes_be().as_slice(), password.as_bytes()]);
57    bigint_helper::convert_to_bigint(x.as_slice(), 16).unwrap()
58}
59
60/// Computes the random scrambling parameter u = H(A|B)
61///
62/// <p>Specification: RFC 5054.
63///
64/// # Arguments
65///
66/// * `public_a` - The public client value 'A'
67/// * `public_b` - The public server value 'B'
68///
69/// The resulting 'u' value.
70fn compute_u(public_a: &BigUint, public_b: &BigUint) -> BigUint {
71    let hash = hash::<Sha256>(&[public_a.to_bytes_be().as_slice(), public_b.to_bytes_be().as_slice()]);
72    bigint_helper::convert_to_bigint(hash.as_slice(), 16).unwrap()
73}
74
75/// Computes a verifier v = g^x (mod N)
76///
77/// <p>Specification: RFC 5054.
78///
79/// # Arguments
80///
81/// * `SrpConfig` - srp configuration {n, g}
82/// * `x` - private password key
83///
84///  The resulting verifier 'v'.
85pub fn compute_v(srp_config: &SrpConfig, x: &BigUint) -> BigUint {
86    srp_config.g.modpow(x, &srp_config.n)
87}
88
89#[derive(Debug)]
90pub struct SrpServer {
91    srp_config: SrpConfig,
92    srp_state: SrpState,
93    username: Option<String>,
94    salt: Option<BigUint>,
95    verifier: Option<BigUint>,
96    public_b: Option<BigUint>,
97    u: Option<BigUint>,
98    public_a: BigUint,
99    ks: Option<BigUint>
100}
101
102impl SrpServer {
103    pub fn new(public_a: BigUint, n: BigUint, g: BigUint) -> Self {
104        let config = SrpConfig::new(n,g);
105        if (public_a.clone() % &config.n).is_zero() {
106            panic!("bad auth!");
107        }
108        SrpServer {
109            srp_config: config,
110            srp_state: SrpState::Init,
111            username: None,
112            salt: None,
113            verifier: None,
114            public_b: None,
115            u: None,
116            public_a,
117            ks: None
118        }
119    }
120
121    pub fn step_1(&mut self, username: String, salt: BigUint, verifier: BigUint) -> Result<BigUint, Error> {
122        if !self.srp_state.eq(&SrpState::Init) {
123            return Err(Error::new(ErrorKind::InvalidData, "wrong state!"))
124        }
125        let k = compute_k(&self.srp_config);
126        let private_b = bigint_helper::generate_random_256bit_bigint();
127        let public_b = (k.clone() * verifier.clone()) + self.srp_config.g.modpow(&private_b, &self.srp_config.n);
128        self.username = Some(username);
129        self.salt = Some(salt);
130        self.verifier = Some(verifier.clone());
131        self.u = Some(compute_u(&self.public_a, public_b.borrow()));
132        self.public_b = Some(public_b);
133        // Steve: SSteve = (Av^u)^b = (g^av^u)^b = [g^a(g^x)^u]^b = (g^(a + ux))^b = (g^b)^(a + ux)
134        let ks = self.public_a.clone().mul(&verifier.modpow(&self.u.clone().unwrap(), &self.srp_config.n)).modpow(&private_b, &self.srp_config.n);
135        let ks = bigint_helper::convert_to_bigint(hash::<Sha256>(&[ks.clone().to_bytes_be().as_slice()]).as_slice(), 16);
136        match ks {
137            Ok(ks) => {
138                self.ks = Some(ks);
139            }
140            Err(err) => {
141                self.ks = Some(bigint_helper::generate_random_256bit_bigint());
142            }
143        }
144        self.srp_state = SrpState::Step1;
145        Ok(self.public_b.clone().unwrap())
146    }
147
148    pub fn step_2(mut self, m1: BigUint) -> Result<BigUint, Error> {
149        if !self.srp_state.eq(&SrpState::Step1) {
150            return Err(Error::new(ErrorKind::InvalidData, "wrong state!"));
151        }
152        let m1_computed = self.compute_m1()?;
153        if !m1.eq(&m1_computed) {
154            return Err(Error::new(ErrorKind::InvalidData, "bad client credentials!"));
155        }
156        self.srp_state = SrpState::Step2;
157        Ok(self.compute_m2(m1)?)
158    }
159
160    fn compute_m1(&mut self) -> Result<BigUint, Error> {
161        let m1= hash::<Sha256>(&[
162            self.public_a.clone().to_bytes_be().as_slice(),
163            self.public_b.clone().unwrap().to_bytes_be().as_slice(),
164            self.ks.clone().unwrap().to_bytes_be().as_slice()
165        ]);
166        convert_to_bigint(m1.as_slice(), 16)
167    }
168
169    fn compute_m2(&mut self, m1: BigUint) -> Result<BigUint, Error> {
170        let m_1 = hash::<Sha256>(&[
171            self.public_a.borrow().to_bytes_be().as_slice(),
172            m1.to_bytes_be().as_slice(),
173            self.ks.clone().unwrap().to_bytes_be().as_slice()
174        ]);
175        convert_to_bigint(m_1.as_slice(), 16)
176    }
177
178}
179
180#[derive(Debug)]
181pub struct SrpClient {
182    srp_config: SrpConfig,
183    srp_state: SrpState,
184    username: Option<String>,
185    password: Option<String>,
186    salt: Option<BigUint>,
187    private_a: Option<BigUint>,
188    public_a: Option<BigUint>,
189    u: Option<BigUint>,
190    public_b: Option<BigUint>,
191    kc: Option<BigUint>,
192    m1: Option<BigUint>
193}
194
195impl SrpClient {
196    pub fn new(n: BigUint, g:BigUint) -> Self {
197        SrpClient {
198            srp_config: SrpConfig::new(n,g),
199            srp_state: SrpState::Init,
200            username: None,
201            password: None,
202            salt: None,
203            private_a: None,
204            public_a: None,
205            u: None,
206            public_b: None,
207            kc: None,
208            m1: None
209
210        }
211    }
212
213    pub fn step_1(&mut self, username: String, password: String) -> Result<BigUint, Error> {
214        if !self.srp_state.eq(&SrpState::Init) {
215            return Err(Error::new(ErrorKind::InvalidData, "wrong state!"));
216        }
217        self.username = Some(username);
218        self.password = Some(password);
219        // compute A
220        let private_a = bigint_helper::generate_random_256bit_bigint();
221        let a = self.srp_config.g.modpow(&private_a, &self.srp_config.n);
222        self.private_a = Some(private_a);
223        self.public_a = Some(a.clone());
224        self.srp_state = SrpState::Step1;
225        Ok(a)
226    }
227
228    pub fn step_2(&mut self, salt: BigUint, public_b: BigUint) -> Result<BigUint, Error> {
229        if !self.srp_state.eq(&SrpState::Step1) {
230            panic!("bad srp state!")
231        }
232        let u = compute_u(&self.public_a.as_mut().unwrap(), &public_b);
233        if public_b.clone().is_zero() || u.clone().is_zero() {
234            panic!("bad client auth!");
235        }
236        self.u = Some(u.clone());
237        self.salt = Some(salt.clone());
238        self.public_b = Some(public_b.clone());
239        let x = compute_x(&salt, self.password.clone().unwrap().as_str());
240        let k = compute_k(&self.srp_config);
241        // Carol: SCarol = (B − kg^x)^(a + ux) = (kv + gb − kg^x)^(a + ux) = (kg^x − kg^x + g^b)^(a + ux) = (g^b)^(a + ux)
242        let tmp = (self.srp_config.g.clone().modpow(&x, &self.srp_config.n)) * k;
243        if public_b < tmp {
244            panic!("bad client auth!");
245        }
246        let sc = (public_b - tmp)
247            .modpow(&(self.private_a.clone().unwrap().add(&u.mul(&x))), &self.srp_config.n);
248        let kc = bigint_helper::convert_to_bigint(hash::<Sha256>(&[sc.borrow().to_bytes_be().as_slice()]).as_slice(), 16).unwrap();
249        self.kc = Some(kc);
250        let m_1 = self.compute_m1()?;
251        self.m1 = Some(m_1.clone());
252        self.srp_state = SrpState::Step2;
253        Ok(m_1)
254    }
255
256    pub fn step_3(mut self, m2: BigUint) -> Result<(), Error> {
257        if !self.srp_state.eq(&SrpState::Step2) {
258            return Err(Error::new(ErrorKind::InvalidData, "wrong state!"));
259        }
260        let computed_m2 = self.compute_m2(self.m1.clone().unwrap())?;
261        if !m2.eq(&computed_m2) {
262            return Err(Error::new(ErrorKind::InvalidData, "bad credentials!"));
263        }
264        Ok(())
265    }
266
267
268    fn compute_m1(&mut self) -> Result<BigUint, Error> {
269        let m_1 = hash::<Sha256>(&[
270            self.public_a.clone().unwrap().clone().to_bytes_be().as_slice(),
271            self.public_b.clone().unwrap().clone().to_bytes_be().as_slice(),
272            self.kc.clone().unwrap().clone().to_bytes_be().as_slice()]);
273        convert_to_bigint(m_1.as_slice(), 16)
274    }
275
276    fn compute_m2(&mut self, m1: BigUint) -> Result<BigUint, Error> {
277        let m_1 = hash::<Sha256>(&[
278            self.public_a.clone().unwrap().to_bytes_be().as_slice(),
279            m1.to_bytes_be().as_slice(),
280            self.kc.clone().unwrap().to_bytes_be().as_slice()
281        ]);
282        convert_to_bigint(m_1.as_slice(), 16)
283    }
284}
285
286#[derive(Debug, PartialEq)]
287enum SrpState {
288    Init, Step1, Step2
289}
290
291#[cfg(test)]
292mod tests {
293    use core::iter;
294
295    use rand::{Rng, thread_rng};
296    use rand::distributions::Alphanumeric;
297
298    use super::*;
299    use std::time::SystemTime;
300
301    #[test]
302    fn test_srp_config() {
303        let n = BigUint::parse_bytes(b"C41C06B5D64E368D2581D791A0233577795106D623130526BFE23528BBE95F1E5F80A6555CD1E5A29FB2FA49547072C7B6D9147C4F8B2209424FBB95DBF70BBB", 16).unwrap();
304        let g = BigUint::parse_bytes(b"2", 10).unwrap();
305        let config = SrpConfig::new(n,g);
306        println!("{:?}", config);
307    }
308
309    #[test]
310    fn test_srp_generate_verifier() {
311        let salt = bigint_helper::generate_random_256bit_bigint();
312        let x = compute_x(&salt,"pass123");
313        println!("private key = {:?}", x);
314        let n = BigUint::parse_bytes(b"C41C06B5D64E368D2581D791A0233577795106D623130526BFE23528BBE95F1E5F80A6555CD1E5A29FB2FA49547072C7B6D9147C4F8B2209424FBB95DBF70BBB", 16).unwrap();
315        let g = BigUint::parse_bytes(b"2", 10).unwrap();
316        let verifier = compute_v(&SrpConfig::new(n,g),&x);
317        println!("verifier = {:?}", verifier)
318    }
319
320    #[test]
321    fn test_srp_client_server() {
322        const SIZE: usize = 100;
323        let mut users = vec![];
324        let mut rng = thread_rng();
325        for index in 0..SIZE {
326            let salt = bigint_helper::generate_random_256bit_bigint();
327            let identity: String = iter::repeat(())
328                .map(|()| rng.sample(Alphanumeric))
329                .map(char::from)
330                .take(7)
331                .collect();
332            let password: String = iter::repeat(())
333                .map(|()| rng.sample(Alphanumeric))
334                .map(char::from)
335                .take(7)
336                .collect();
337
338            let x = compute_x(&salt, password.as_str());
339            let n = BigUint::parse_bytes(b"C41C06B5D64E368D2581D791A0233577795106D623130526BFE23528BBE95F1E5F80A6555CD1E5A29FB2FA49547072C7B6D9147C4F8B2209424FBB95DBF70BBB", 16).unwrap();
340            let g = BigUint::parse_bytes(b"2", 10).unwrap();
341            let verifier = compute_v(&SrpConfig::new(n,g), &x);
342            users.push(User {
343                salt,
344                verifier,
345                username: identity,
346                password
347            });
348        }
349
350        let now = SystemTime::now();
351        for (index, user) in users.iter().enumerate() {
352            let n = BigUint::parse_bytes(b"C41C06B5D64E368D2581D791A0233577795106D623130526BFE23528BBE95F1E5F80A6555CD1E5A29FB2FA49547072C7B6D9147C4F8B2209424FBB95DBF70BBB", 16).unwrap();
353            let g = BigUint::parse_bytes(b"2", 10).unwrap();
354            let mut client = SrpClient::new(n.clone(),g.clone());
355            // do client step 1
356            let a = client.step_1(user.username.clone(), user.password.clone());
357            match a {
358                Ok(a) => {
359                    // send (A, I) to server
360                    let mut server = SrpServer::new(a, n.clone(), g.clone());
361                    // do server step 1
362                    let b = server.step_1(user.username.clone(), user.salt.clone(), user.verifier.clone());
363                    match b {
364                        Ok(b) => {
365                            // server send salt and b
366                            // client step 2
367                            let m_1 = client.step_2(user.salt.clone(), b.clone());
368                            match m_1 {
369                                Ok(m_1) => {
370                                    // server step 2
371                                    let m_2 = server.step_2(m_1);
372                                    match m_2 {
373                                        Ok(m_2) => {
374                                            match client.step_3(m_2) {
375                                                Ok(_) => {}
376                                                Err(err) => {
377                                                    panic!("{}", err);
378                                                }
379                                            };
380                                        }
381                                        Err(err) => {
382                                            panic!("{}", err);
383                                        }
384                                    }
385                                }
386                                Err(err) => {
387                                    panic!("{}", err);
388                                }
389                            }
390
391                        }
392                        Err(err) => {
393                            panic!("{}", err);
394                        }
395                    }
396                }
397                Err(err) => {
398                    panic!("{}", err);
399                }
400            }
401        }
402    }
403
404
405    struct User {
406        username: String,
407        password: String,
408        salt: BigUint,
409        verifier: BigUint
410    }
411}
412
413