]> git.lizzy.rs Git - PAKEs.git/commitdiff
v0.2.0
authorАртём Павлов [Artyom Pavlov] <newpavlov@gmail.com>
Mon, 14 Aug 2017 02:25:36 +0000 (05:25 +0300)
committerАртём Павлов [Artyom Pavlov] <newpavlov@gmail.com>
Mon, 14 Aug 2017 02:25:36 +0000 (05:25 +0300)
15 files changed:
Cargo.toml
src/client.rs
src/groups.rs [new file with mode: 0644]
src/groups/1024.bin [new file with mode: 0644]
src/groups/1536.bin [new file with mode: 0644]
src/groups/2048.bin [new file with mode: 0644]
src/groups/3072.bin [new file with mode: 0644]
src/groups/4096.bin [new file with mode: 0644]
src/groups/6144.bin [new file with mode: 0644]
src/groups/8192.bin [new file with mode: 0644]
src/k_sha1_1024.bin [new file with mode: 0644]
src/lib.rs
src/server.rs
src/types.rs
tests/mod.rs

index 19e315531cab03a0c2ff6586e1aee174b14d0e9c..37dcc6a81f7616e0f95ab3300e2bbfc4ab334a4b 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "srp"
-version = "0.1.1"
+version = "0.2.0"
 authors = ["RustCrypto Developers"]
 license = "MIT/Apache-2.0"
 description = "Secure Remote Password (SRP) protocol implementation"
@@ -12,11 +12,13 @@ categories = ["cryptography", "authentication"]
 [dependencies]
 num = "0.1"
 generic-array = "0.8"
-rand = "0.3"
 digest = "0.6"
+lazy_static = "0.2"
 
 [dev-dependencies]
+rand = "0.3"
 sha2 = "0.6"
+sha-1 = "0.4"
 
 [badges]
 travis-ci = { repository = "RustCrypto/SRP" }
index ee19e88439199cd336bb2dd0b20ab4bfe149ed41..87990a6f1e58f430697ba2d708333c6c5dcef673 100644 (file)
@@ -2,12 +2,14 @@
 //! 
 //! # Usage
 //! First create SRP client struct by passing to it SRP parameters (shared
-//! between client and server) and RNG instance (OS RNG is recommended):
+//! between client and server) and randomly generated `a`:
 //! 
 //! ```ignore
-//! let srp_params = SrpParams{n, g, k};
-//! let mut rng = rand::os::OsRng::new().unwrap();
-//! let client = SrpClient::<Sha256>::new(&srp_params, &mut rng);
+//! use srp::groups::G_2048;
+//! use sha2::Sha256;
+//!
+//! let a = rng.gen_iter::<u8>().take(64).collect::<Vec<u8>>();
+//! let client = SrpClient::<Sha256>::new(&a, &srp_params);
 //! ```
 //! 
 //! Next send handshake data (username and `a_pub`) to the server and receive
@@ -25,7 +27,7 @@
 //! can return error in case of malicious `b_pub`.
 //! 
 //! ```ignore
-//! let private_key = srp6a_private_key::<Sha256>(username, password, salt);
+//! let private_key = srp_private_key::<Sha256>(username, password, salt);
 //! let verifier = client.get_verifier(&private_key, &b_pub)?;
 //! ```
 //!
 //! let pwd_verifier = SrpClient::<Sha256>::register(&private_key, &srp_params);
 //! conn.send_registration_data(username, salt, pwd_verifier);
 //! ```
+
+//let buf = rng.gen_iter::<u8>().take(l).collect::<Vec<u8>>();
 use std::marker::PhantomData;
 
-use rand::Rng;
 use num::{BigUint, Zero};
 use digest::Digest;
 use generic_array::GenericArray;
 
 use tools::powm;
-use types::{SrpAuthError, SrpParams};
+use types::{SrpAuthError, SrpGroup};
 
 /// SRP client state before handshake with the server.
 pub struct SrpClient<'a, D: Digest> {
-    params: &'a SrpParams,
+    params: &'a SrpGroup,
 
     a: BigUint,
     a_pub: BigUint,
@@ -80,11 +83,10 @@ pub struct SrpClientVerifier<D: Digest> {
     key: GenericArray<u8, D::OutputSize>,
 }
 
-/// Compute user private key as described in the SRP6a. Consider using proper
+/// Compute user private key as described in the RFC 5054. Consider using proper
 /// password hashing algorithm instead.
-pub fn srp6a_private_key<D: Digest>(username: &[u8], password: &[u8],
-        salt: &[u8]
-    ) -> GenericArray<u8, D::OutputSize>
+pub fn srp_private_key<D: Digest>(username: &[u8], password: &[u8], salt: &[u8])
+    -> GenericArray<u8, D::OutputSize>
 {
     let p = {
         let mut d = D::new();
@@ -101,10 +103,8 @@ pub fn srp6a_private_key<D: Digest>(username: &[u8], password: &[u8],
 
 impl<'a, D: Digest> SrpClient<'a, D> {
     /// Create new SRP client instance.
-    pub fn new<R: Rng>(params: &'a SrpParams, rng: &mut R) -> Self {
-        let l = params.n.bits()/8; 
-        let buf = rng.gen_iter::<u8>().take(l).collect::<Vec<u8>>();
-        let a = BigUint::from_bytes_le(&buf);
+    pub fn new(a: &[u8], params: &'a SrpGroup) -> Self {
+        let a = BigUint::from_bytes_be(a);
         let a_pub = params.powm(&a);
 
         Self { params, a, a_pub, d: Default::default() }
@@ -112,16 +112,17 @@ impl<'a, D: Digest> SrpClient<'a, D> {
 
     /// Get password verfier for user registration on the server
     pub fn get_password_verifier(&self, private_key: &[u8]) -> Vec<u8> {
-        let x = BigUint::from_bytes_le(&private_key);
+        let x = BigUint::from_bytes_be(&private_key);
         let v = self.params.powm(&x);
-        v.to_bytes_le()
+        v.to_bytes_be()
     }
 
     fn calc_key(&self, b_pub: &BigUint, x: &BigUint, u: &BigUint)
         -> GenericArray<u8, D::OutputSize>
     {
         let n = &self.params.n;
-        let interm = (&self.params.k * self.params.powm(x)) % n;
+        let k = self.params.compute_k::<D>();
+        let interm = (k * self.params.powm(x)) % n;
         // Because we do operation in modulo N we can get: (kv + g^b) < kv
         let v = if b_pub > &interm {
             (b_pub - &interm) % n
@@ -130,7 +131,7 @@ impl<'a, D: Digest> SrpClient<'a, D> {
         };
         // S = |B - kg^x| ^ (a + ux)
         let s = powm(&v, &(&self.a + (u*x) % n ), n);
-        D::digest(&s.to_bytes_le())
+        D::digest(&s.to_bytes_be())
     }
 
     /// Process server reply to the handshake.
@@ -139,25 +140,25 @@ impl<'a, D: Digest> SrpClient<'a, D> {
     {
         let u = {
             let mut d = D::new();
-            d.input(&self.a_pub.to_bytes_le());
+            d.input(&self.a_pub.to_bytes_be());
             d.input(b_pub);
-            BigUint::from_bytes_le(&d.result())
+            BigUint::from_bytes_be(&d.result())
         };
 
-        let b_pub = BigUint::from_bytes_le(b_pub);
+        let b_pub = BigUint::from_bytes_be(b_pub);
 
         // Safeguard against malicious B
         if &b_pub % &self.params.n == BigUint::zero() {
             return Err(SrpAuthError{ description: "Malicious b_pub value" })
         }
 
-        let x = BigUint::from_bytes_le(&private_key);
+        let x = BigUint::from_bytes_be(&private_key);
         let key = self.calc_key(&b_pub, &x, &u);
         // M1 = H(A, B, K)
         let proof = {
             let mut d = D::new();
-            d.input(&self.a_pub.to_bytes_le());
-            d.input(&b_pub.to_bytes_le());
+            d.input(&self.a_pub.to_bytes_be());
+            d.input(&b_pub.to_bytes_be());
             d.input(&key);
             d.result()
         };
@@ -165,7 +166,7 @@ impl<'a, D: Digest> SrpClient<'a, D> {
         // M2 = H(A, M1, K)
         let server_proof = {
             let mut d = D::new();
-            d.input(&self.a_pub.to_bytes_le());
+            d.input(&self.a_pub.to_bytes_be());
             d.input(&proof);
             d.input(&key);
             d.result()
@@ -180,7 +181,7 @@ impl<'a, D: Digest> SrpClient<'a, D> {
 
     /// Get public ephemeral value for handshaking with the server.
     pub fn get_a_pub(&self) -> Vec<u8> {
-        self.a_pub.to_bytes_le()
+        self.a_pub.to_bytes_be()
     }
 }
 
diff --git a/src/groups.rs b/src/groups.rs
new file mode 100644 (file)
index 0000000..246134e
--- /dev/null
@@ -0,0 +1,56 @@
+//! Groups from [RFC 5054](https://tools.ietf.org/html/rfc5054)
+//! 
+//! It is strongly recommended to use them instead of custom generated
+//! groups. Additionally it is not recommended to use `G_1024` and `G_1536`,
+//! they are provided only for compatability with the legacy software.
+use types::SrpGroup;
+use num::BigUint;
+
+lazy_static! {
+    pub static ref G_1024: SrpGroup = SrpGroup {
+        n: BigUint::from_bytes_be(include_bytes!("groups/1024.bin")),
+        g: BigUint::from_bytes_be(&[2]),
+    };
+}
+
+lazy_static! {
+    pub static ref G_1536: SrpGroup = SrpGroup {
+        n: BigUint::from_bytes_be(include_bytes!("groups/1536.bin")),
+        g: BigUint::from_bytes_be(&[2]),
+    };
+}
+
+lazy_static! {
+    pub static ref G_2048: SrpGroup = SrpGroup {
+        n: BigUint::from_bytes_be(include_bytes!("groups/2048.bin")),
+        g: BigUint::from_bytes_be(&[2]),
+    };
+}
+
+lazy_static! {
+    pub static ref G_3072: SrpGroup = SrpGroup {
+        n: BigUint::from_bytes_be(include_bytes!("groups/3072.bin")),
+        g: BigUint::from_bytes_be(&[5]),
+    };
+}
+
+lazy_static! {
+    pub static ref G_4096: SrpGroup = SrpGroup {
+        n: BigUint::from_bytes_be(include_bytes!("groups/4096.bin")),
+        g: BigUint::from_bytes_be(&[5]),
+    };
+}
+
+lazy_static! {
+    pub static ref G_6144: SrpGroup = SrpGroup {
+        n: BigUint::from_bytes_be(include_bytes!("groups/6144.bin")),
+        g: BigUint::from_bytes_be(&[5]),
+    };
+}
+
+lazy_static! {
+    pub static ref G_8192: SrpGroup = SrpGroup {
+        n: BigUint::from_bytes_be(include_bytes!("groups/8192.bin")),
+        g: BigUint::from_bytes_be(&[19]),
+    };
+}
diff --git a/src/groups/1024.bin b/src/groups/1024.bin
new file mode 100644 (file)
index 0000000..7ce0aa3
--- /dev/null
@@ -0,0 +1,3 @@
+î¯
+¹­³\8dÖ\9c
\8fÅè`ra\87uÿ<\v\9e¢1L\9c%evÖtßt\96ê\81Ó8;H\13Ö\92ÆààÕØâP¹\8bä\8eI\\1d`\89ÚÑ]Ç×´aTÖ¶Î\8eô­i±]I\82U\9b){Ï\18\85Å)õff\ eWìhí¼<\ 5rlÀ/ÔËô\97\9aýQ8þ\83vC[\9fÆ\1d/Àë\ 6ã
\ No newline at end of file
diff --git a/src/groups/1536.bin b/src/groups/1536.bin
new file mode 100644 (file)
index 0000000..c3a5972
--- /dev/null
@@ -0,0 +1 @@
+\9dï<¯¹9'z±ñ*\86\17¤{»Û¥\1dô\99¬L\80¾î©aK\19ÌM_O_Un'ËÞQÆ©Kä`z)\15X\90; ÐøC\80¶U»\9a"èÜß\ 2\8a|ìgðÐ\814±È¹y\89\14\9b`\9e\v㺶=GT\83\81ÛűüvN?KSÝ\9d¡\15\8bý>+\9c\8cõnß\ 1\9594\96'Û/Õ=$·Ä\86ew.C}l\7f\8cäBsJ÷Ì·®\83|&J㩾¸\7f\8a/鸵).Z\ 2\1fÿ^\91G\9e\8cç¢\8c$BÆó\15\18\ f\93I\9a#MÏvãþÑ5ù»
\ No newline at end of file
diff --git a/src/groups/2048.bin b/src/groups/2048.bin
new file mode 100644 (file)
index 0000000..23207c6
--- /dev/null
@@ -0,0 +1,2 @@
+¬kÛA2J\9a\9bñfÞ^\13\89X/¯r¶e\19\87î\aü1\92\94=µ`P£s)Ë´ \99í\81\93àuwg¡=Õ#\12«K\ 31\rÍ\7fH©Ú\ 4ýPè\b9ií·g°Ï`\95\17\9a\16:³f\1a\ 5ûÕúªè)\18©\96/\v\93¸Uùy\93ì\97^ê¨\rt
+ÛôÿtsYÐAÕÃ>§\1d(\1eDk\14w;Ê\97´:#û\80\16v½ zCld\81ñÒ¹\a\87\17F\1a[\9d\88øwHTE#µ$°Õ}^§z'uÒìú\ 3,ûÛõ/³xa`'\90\ 4åzæ¯\87Ns\ 3ÎS)\9cÌ\ 4\1c\bØ*V\98ó¨ÐÃ\82q®5øéÛû¶\94µÈ\ 3Ø\9fzä5Þ#mR_Tu\9beãrüÖ\8eò\ f§\11\1f\9eJÿs
\ No newline at end of file
diff --git a/src/groups/3072.bin b/src/groups/3072.bin
new file mode 100644 (file)
index 0000000..7e1a84d
Binary files /dev/null and b/src/groups/3072.bin differ
diff --git a/src/groups/4096.bin b/src/groups/4096.bin
new file mode 100644 (file)
index 0000000..82463c0
Binary files /dev/null and b/src/groups/4096.bin differ
diff --git a/src/groups/6144.bin b/src/groups/6144.bin
new file mode 100644 (file)
index 0000000..83a559a
Binary files /dev/null and b/src/groups/6144.bin differ
diff --git a/src/groups/8192.bin b/src/groups/8192.bin
new file mode 100644 (file)
index 0000000..b1f32ac
Binary files /dev/null and b/src/groups/8192.bin differ
diff --git a/src/k_sha1_1024.bin b/src/k_sha1_1024.bin
new file mode 100644 (file)
index 0000000..4408438
--- /dev/null
@@ -0,0 +1 @@
+uVª\ 4Zï,Ý\a«¯\ ff\>\81\89\13\18o
\ No newline at end of file
index 20c0432235ca5acd3121259107723aad13fcf0d8..df11ae9c5e9619f437036252afbd44b502ecdbbb 100644 (file)
@@ -1,12 +1,13 @@
 //! [Secure Remote Password][1] (SRP) protocol implementation.
 //! 
-//! This implementation uses little-endian serialization of big integers and is
-//! generic over hash functions using `Digest` trait, so you will need to choose
-//! a hash function, e.g. `Sha256` from `sha2` crate. Additionally this crate
-//! allows to use a specialized password hashing algorithms for private key
-//! computation instead of method described in the SRP literature.
+//! This implementation is generic over hash functions using
+//! [`Digest`](https://docs.rs/digest) trait, so you will need to choose a hash
+//! function, e.g. `Sha256` from [`sha2`](https://crates.io/crates/sha2) crate.
+//! Additionally this crate allows to use a specialized password hashing
+//! algorithm for private key computation instead of method described in the
+//! SRP literature.
 //! 
-//! Currently compatability with over implementations was not tested.
+//! Compatability with over implementations was not yet tested.
 //! 
 //! # Usage
 //! Add `srp` dependecy to your `Cargo.toml`:
 //! # Algorithm description
 //! Here we briefly describe implemented algroithm. For additionall information
 //! refer to SRP literature. All arithmetic is done modulo `N`, where `N` is a 
-//! large safe prime (`N = 2q+1`, where `q` is prime).
+//! large safe prime (`N = 2q+1`, where `q` is prime). Additionally `g` MUST be
+//! a generator modulo `N`. It's STRONGLY recommended to use SRP parameters
+//! provided by this crate in the [`groups`](groups/index.html) module.
 //! 
-//! Client |       | Server
-//! -------|-------|--------
+//!        Client           |               |      Server
+//! ------------------------|---------------|------------------------
 //!                         |    — `I` —>   | (lookup `s`, `v`)
 //! `x = PH(P, s)`          | <— `s`, `v` — | 
 //! `a_pub = g^a`           |  — `a_pub` —> | `b_pub = k*v + g^b`
 //! - `I` — user identity (username)
 //! - `P` — user password
 //! - `H` — one-way hash function
-//! - `HP` — password hashing algroithm, in the SRP described as
-//! `H(s || I || P)` or `H(s || P)`
+//! - `PH` — password hashing algroithm, in the RFC 5054 described as
+//! `H(s || H(I || ":" || P))`
 //! - `^` — (modular) exponentiation
 //! - `x` — user private key
 //! - `s` — salt generated by user and stored on the server
 //! - `v` — password verifier equal to `g^x` and stored on the server
-//! - `a`, `b` — secret ephemeral values
+//! - `a`, `b` — secret ephemeral values (at least 256 bits in length)
 //! - `A`, `B` — Public ephemeral values
 //! - `u` — scrambling parameter
-//! - `g` — a generator modulo `N`
 //! - `k` — multiplier parameter (`k = H(N || g)` in SRP-6a)
 //!
 //! [1]: https://en.wikipedia.org/wiki/Secure_Remote_Password_protocol
+//! [2]: https://tools.ietf.org/html/rfc5054
 extern crate num;
 extern crate digest;
 extern crate generic_array;
-extern crate rand;
+#[macro_use]
+extern crate lazy_static;
+
+#[cfg(test)]
+extern crate sha_1;
 
 mod tools;
 pub mod client;
 pub mod server;
 pub mod types;
-
-/// 1024 bit prime number which can be used as `n` in the `SrpParams`.
-///
-/// For conversion to `BigUint` use `BigUint::from_bytes_le(SRP_PRIME)`.
-pub const PRIME: &'static [u8] = include_bytes!("prime.bin");
+pub mod groups;
index e82ebf68b614b626836e1f7d7bb2417bf60f3bf6..137d6b3324d35bdcc1bfe49fa39c3dc6bcc5bf6c 100644 (file)
@@ -2,13 +2,16 @@
 //!
 //! # Usage
 //! First receive user's username and public value `a_pub`, retrieve from a
-//! database `UserRecord` for a given username and initialize SRP server state:
+//! database `UserRecord` for a given username, generate `b` (e.g. 512 bits
+//! long) and initialize SRP server instance:
 //! 
 //! ```ignore
-//! let mut rng = rand::os::OsRng::new().unwrap();
+//! use srp::groups::G_2048;
+//!
 //! let (username, a_pub) = conn.receive_handshake();
 //! let user = db.retrieve_user_record(username);
-//! let server = SrpServer::<Sha256>::new(&user, &a_pub, &srp_params, &mut rng)?;
+//! let b = rng.gen_iter::<u8>().take(64).collect::<Vec<u8>>();
+//! let server = SrpServer::<Sha256>::new(&user, &a_pub, &b, &G_2048)?;
 //! ```
 //! 
 //! Next send to user `b_pub` and `salt` from user record:
@@ -18,7 +21,8 @@
 //! conn.reply_to_handshake(&user.salt, b_pub);
 //! ```
 //! 
-//! And finally recieve user proof, verify it and send server proof as reply:
+//! And finally recieve user proof, verify it and send server proof in the
+//! reply:
 //! 
 //! ```ignore
 //! let user_proof = conn.receive_proof();
 //! encryption.
 use std::marker::PhantomData;
 
-use rand::Rng;
 use num::{BigUint, Zero};
 use digest::Digest;
 use generic_array::GenericArray;
 
 use tools::powm;
-use types::{SrpAuthError, SrpParams};
+use types::{SrpAuthError, SrpGroup};
 
 /// Data provided by users upon registration, usually stored in the database.
 pub struct UserRecord<'a> {
@@ -59,61 +62,47 @@ pub struct SrpServer<D: Digest> {
 }
 
 impl< D: Digest> SrpServer< D> {
-    /// Create new server state with randomly generated `b`.
-    pub fn new<R: Rng>(user: &UserRecord, a_pub: &[u8], params: &SrpParams,
-            rng: &mut R)
-        -> Result<Self, SrpAuthError>
-    {
-        let l = params.n.bits()/8; 
-        let b = rng.gen_iter::<u8>().take(l).collect::<Vec<u8>>();
-        Self::new_with_b(user, a_pub, &b, params)
-    }
-
-    /// Create new server state with given `b`.
-    ///
-    /// Usefull if it's not convenient to keep `SrpServer` state between
-    /// handshake and verification steps. (e.g. when working over HTTP and
-    /// storing `b` in a database)
-    pub fn new_with_b(user: &UserRecord, a_pub: &[u8], b: &[u8],
-                params: &SrpParams)
+    /// Create new server state.
+    pub fn new(user: &UserRecord, a_pub: &[u8], b: &[u8], params: &SrpGroup)
         -> Result<Self, SrpAuthError>
     {
-        let a_pub = BigUint::from_bytes_le(a_pub);
+        let a_pub = BigUint::from_bytes_be(a_pub);
         // Safeguard against malicious A
         if &a_pub % &params.n == BigUint::zero() {
             return Err(SrpAuthError { description: "Malicious a_pub value" })
         }
-        let v = BigUint::from_bytes_le(user.verifier);
-        let b = BigUint::from_bytes_le(b)  % &params.n;
+        let v = BigUint::from_bytes_be(user.verifier);
+        let b = BigUint::from_bytes_be(b)  % &params.n;
+        let k = params.compute_k::<D>();
         // kv + g^b
-        let interm = (&params.k * &v) % &params.n;
+        let interm = (k * &v) % &params.n;
         let b_pub = (interm + &params.powm(&b)) % &params.n;
         // H(A || B)
         let u = {
             let mut d = D::new();
-            d.input(&a_pub.to_bytes_le());
-            d.input(&b_pub.to_bytes_le());
+            d.input(&a_pub.to_bytes_be());
+            d.input(&b_pub.to_bytes_be());
             d.result()
         };
         let d = Default::default();
         //(Av^u) ^ b
         let key = {
-            let u =  BigUint::from_bytes_le(&u);
+            let u =  BigUint::from_bytes_be(&u);
             let t = (&a_pub * powm(&v, &u, &params.n)) % &params.n;
             let s = powm(&t, &b, &params.n);
-            D::digest(&s.to_bytes_le())
+            D::digest(&s.to_bytes_be())
         };
         Ok(Self { b, a_pub, b_pub, key, d})
     }
 
     /// Get private `b` value. (see `new_with_b` documentation)
     pub fn get_b(&self) -> Vec<u8> {
-        self.b.to_bytes_le()
+        self.b.to_bytes_be()
     }
 
     /// Get public `b_pub` value for sending to the user.
     pub fn get_b_pub(&self) -> Vec<u8> {
-        self.b_pub.to_bytes_le()
+        self.b_pub.to_bytes_be()
     }
 
     /// Get shared secret between user and the server. (do not forget to verify
@@ -129,14 +118,14 @@ impl< D: Digest> SrpServer< D> {
     {
         // M = H(A, B, K)
         let mut d = D::new();
-        d.input(&self.a_pub.to_bytes_le());
-        d.input(&self.b_pub.to_bytes_le());
+        d.input(&self.a_pub.to_bytes_be());
+        d.input(&self.b_pub.to_bytes_be());
         d.input(&self.key);
 
         if user_proof == d.result().as_slice() {
             // H(A, M, K)
             let mut d = D::new();
-            d.input(&self.a_pub.to_bytes_le());
+            d.input(&self.a_pub.to_bytes_be());
             d.input(user_proof);
             d.input(&self.key);
             Ok(d.result())
index aa8a0dc1d9b5a3f242e98cfa9ac743dfe04577b2..810132cce656e3ed522177f4b65b29ef3d032cfe 100644 (file)
@@ -2,6 +2,7 @@
 use std::{fmt, error};
 use num::BigUint;
 use tools::powm;
+use digest::Digest;
 
 /// SRP authentification error.
 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
@@ -21,19 +22,43 @@ impl error::Error for SrpAuthError {
     }
 }
 
-/// Parameters of SRP shared between client and server.
+/// Group used for SRP computations
 #[derive(Debug, Clone, Eq, PartialEq)]
-pub struct SrpParams {
+pub struct SrpGroup {
     /// A large safe prime (N = 2q+1, where q is prime)
     pub n: BigUint,
-    /// A generator modulo N (e.g. 2)
+    /// A generator modulo N
     pub g: BigUint,
-    /// Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6)
-    pub k: BigUint,
 }
 
-impl SrpParams {
+impl SrpGroup {
     pub(crate) fn powm(&self, v: &BigUint) -> BigUint {
         powm(&self.g, v, &self.n)
     }
+
+    /// Compute `k` with given hash function and return SRP parameters
+    pub(crate) fn compute_k<D: Digest>(&self) -> BigUint {
+        let n = self.n.to_bytes_be();
+        let g_bytes = self.g.to_bytes_be();
+        let mut buf = vec![0u8; n.len()];
+        let l = n.len() - g_bytes.len();
+        buf[l..].copy_from_slice(&g_bytes);
+
+        let mut d = D::new();
+        d.input(&n);
+        d.input(&buf);
+        BigUint::from_bytes_be(&d.result())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use ::groups::G_1024;
+    use sha_1::Sha1;
+
+    #[test]
+    fn test_k_1024_sha1() {
+        let k = G_1024.compute_k::<Sha1>().to_bytes_be();
+        assert_eq!(&k, include_bytes!("k_sha1_1024.bin"));
+    }
 }
index f7810e8505bb15a0689b04db0a88b03fd072d6c4..24d911d095a989d80c5472f22a5b286537266192 100644 (file)
@@ -3,29 +3,24 @@ extern crate sha2;
 extern crate rand;
 extern crate srp;
 
-use num::BigUint;
 use sha2::Sha256;
 use rand::Rng;
 
-use srp::types::SrpParams;
-use srp::client::{ SrpClient, srp6a_private_key };
+use srp::groups::G_2048;
+use srp::client::{SrpClient, srp_private_key };
 use srp::server::{SrpServer, UserRecord};
 
 fn auth_test(reg_pwd: &[u8], auth_pwd: &[u8]) {
     let mut rng = rand::os::OsRng::new().unwrap();
-    let username = "john".as_bytes();
-    let srp_params = SrpParams{
-        n: BigUint::from_bytes_le(srp::PRIME),
-        k: BigUint::from_bytes_be(&[1, 2, 3]),
-        g: BigUint::new(vec![2]),
-    };
+    let username = "alice".as_bytes();
 
     // Client instance creation
-    let client = SrpClient::<Sha256>::new(&srp_params, &mut rng);
+    let a = rng.gen_iter::<u8>().take(64).collect::<Vec<u8>>();
+    let client = SrpClient::<Sha256>::new(&a, &G_2048);
 
     // Registration
     let salt: [u8; 16] = rng.gen();
-    let reg_priv_key = srp6a_private_key::<Sha256>(username, reg_pwd, &salt);
+    let reg_priv_key = srp_private_key::<Sha256>(username, reg_pwd, &salt);
     let verif = client.get_password_verifier(&reg_priv_key);
 
     // User sends handshake
@@ -33,12 +28,13 @@ fn auth_test(reg_pwd: &[u8], auth_pwd: &[u8]) {
 
     // Server retrieve user record from db and processes handshake
     let user = UserRecord { username, salt: &salt, verifier: &verif };
-    let server = SrpServer::<Sha256>::new(&user, &a_pub, &srp_params, &mut rng)
+    let b = rng.gen_iter::<u8>().take(64).collect::<Vec<u8>>();
+    let server = SrpServer::<Sha256>::new(&user, &a_pub, &b, &G_2048)
         .unwrap();
     let (salt, b_pub) = (&user.salt, server.get_b_pub());
 
     // Client processes handshake reply
-    let auth_priv_key = srp6a_private_key::<Sha256>(username, auth_pwd, &salt);
+    let auth_priv_key = srp_private_key::<Sha256>(username, auth_pwd, &salt);
     let client2 = client.process_reply(&auth_priv_key, &b_pub).unwrap();
     let proof = client2.get_proof();