#[cfg(test)]
mod tests {
- use spake2::{Ed25519Group, ErrorType, Identity, Password, SPAKE2, SPAKEErr};
+ use spake2::{Ed25519Group, ErrorType, Identity, Password, SPAKEErr, SPAKE2};
#[test]
fn test_basic() {
0x96, 0x3b, 0x58, 0xe3, 0x43, 0x88, 0xc8, 0xe6, 0xda, 0xe8, 0x91, 0xdb, 0x75, 0x64,
0x81, 0xa0, 0x23, 0x12,
]).decompress()
- .unwrap()
+ .unwrap()
}
fn const_n() -> c2_Element {
0xc6, 0x32, 0x57, 0x6a, 0xc6, 0x4a, 0xea, 0x65, 0x0b, 0x49, 0x6a, 0x8a, 0x20, 0xff,
0x00, 0xe5, 0x83, 0xc3,
]).decompress()
- .unwrap()
+ .unwrap()
}
fn const_s() -> c2_Element {
0xd8, 0xf5, 0x78, 0x79, 0x56, 0x9c, 0x22, 0x2d, 0x22, 0xb1, 0xcd, 0x71, 0xe8, 0x54,
0x6a, 0xb8, 0xe6, 0xf1,
]).decompress()
- .unwrap()
+ .unwrap()
}
fn hash_to_scalar(s: &[u8]) -> c2_Scalar {
//! SRP client implementation.
-//!
+//!
//! # Usage
//! First create SRP client struct by passing to it SRP parameters (shared
//! between client and server) and randomly generated `a`:
-//!
+//!
//! ```ignore
//! 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, &G_2048);
//! ```
-//!
+//!
//! Next send handshake data (username and `a_pub`) to the server and receive
//! `salt` and `b_pub`:
-//!
+//!
//! ```ignore
//! let a_pub = client.get_a_pub();
//! let (salt, b_pub) = conn.send_handshake(username, a_pub);
//! ```
-//!
+//!
//! Compute private key using `salt` with any password hashing function.
//! You can use method from SRP-6a, but it's recommended to use specialized
//! password hashing algorithm instead (e.g. PBKDF2, argon2 or scrypt).
//! Next create verifier instance, note that `get_verifier` consumes client and
//! can return error in case of malicious `b_pub`.
-//!
+//!
//! ```ignore
//! let private_key = srp_private_key::<Sha256>(username, password, salt);
//! let verifier = client.get_verifier(&private_key, &b_pub)?;
//! Finally verify the server: first generate user proof,
//! send it to the server and verify server proof in the reply. Note that
//! `verify_server` method will return error in case of incorrect server reply.
-//!
+//!
//! ```ignore
//! let user_proof = verifier.get_proof();
//! let server_proof = conn.send_proof(user_proof);
//! let key = verifier.verify_server(server_proof)?;
//! ```
-//!
+//!
//! `key` contains shared secret key between user and the server. Alternatively
//! you can directly extract shared secret key using `get_key()` method and
//! handle authentification through different (secure!) means (e.g. by using
//! authentificated cipher mode).
-//!
+//!
//! For user registration on the server first generate salt (e.g. 32 bytes long)
//! and get password verifier which depends on private key. Send useranme, salt
//! and password verifier over protected channel to protect against
//! Man-in-the-middle (MITM) attack for registration.
-//!
+//!
//! ```ignore
//! let pwd_verifier = client.get_password_verifier(&private_key);
//! conn.send_registration_data(username, salt, pwd_verifier);
//let buf = rng.gen_iter::<u8>().take(l).collect::<Vec<u8>>();
use std::marker::PhantomData;
-use num::{BigUint, Zero};
use digest::Digest;
use generic_array::GenericArray;
+use num::{BigUint, Zero};
use tools::powm;
use types::{SrpAuthError, SrpGroup};
a: BigUint,
a_pub: BigUint,
- d: PhantomData<D>
+ d: PhantomData<D>,
}
/// SRP client state after handshake with the server.
/// Compute user private key as described in the RFC 5054. Consider using proper
/// password hashing algorithm instead.
-pub fn srp_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();
d.input(username);
let a = BigUint::from_bytes_be(a);
let a_pub = params.powm(&a);
- Self { params, a, a_pub, d: Default::default() }
+ Self {
+ params,
+ a,
+ a_pub,
+ d: Default::default(),
+ }
}
/// Get password verfier for user registration on the server
v.to_bytes_be()
}
- fn calc_key(&self, b_pub: &BigUint, x: &BigUint, u: &BigUint)
- -> GenericArray<u8, D::OutputSize>
- {
+ fn calc_key(
+ &self,
+ b_pub: &BigUint,
+ x: &BigUint,
+ u: &BigUint,
+ ) -> GenericArray<u8, D::OutputSize> {
let n = &self.params.n;
let k = self.params.compute_k::<D>();
let interm = (k * self.params.powm(x)) % n;
(n + b_pub - &interm) % n
};
// S = |B - kg^x| ^ (a + ux)
- let s = powm(&v, &(&self.a + (u*x) % n ), n);
+ let s = powm(&v, &(&self.a + (u * x) % n), n);
D::digest(&s.to_bytes_be())
}
/// Process server reply to the handshake.
- pub fn process_reply(self, private_key: &[u8], b_pub: &[u8])
- -> Result<SrpClientVerifier<D>, SrpAuthError>
- {
+ pub fn process_reply(
+ self,
+ private_key: &[u8],
+ b_pub: &[u8],
+ ) -> Result<SrpClientVerifier<D>, SrpAuthError> {
let u = {
let mut d = D::new();
d.input(&self.a_pub.to_bytes_be());
// Safeguard against malicious B
if &b_pub % &self.params.n == BigUint::zero() {
- return Err(SrpAuthError{ description: "Malicious b_pub value" })
+ return Err(SrpAuthError {
+ description: "Malicious b_pub value",
+ });
}
let x = BigUint::from_bytes_be(private_key);
/// Verify server reply to verification data. It will return shared secret
/// key in case of success.
- pub fn verify_server(self, reply: &[u8])
- -> Result<GenericArray<u8, D::OutputSize>, SrpAuthError>
- {
+ pub fn verify_server(
+ self,
+ reply: &[u8],
+ ) -> Result<GenericArray<u8, D::OutputSize>, SrpAuthError> {
if self.server_proof.as_slice() != reply {
- Err(SrpAuthError{ description: "Incorrect server proof" })
+ Err(SrpAuthError {
+ description: "Incorrect server proof",
+ })
} else {
Ok(self.key)
}
//! 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 compatibility with the legacy software.
-use types::SrpGroup;
use num::BigUint;
+use types::SrpGroup;
lazy_static! {
pub static ref G_1024: SrpGroup = SrpGroup {
//!
//! [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 num;
#[macro_use]
extern crate lazy_static;
#[cfg(test)]
extern crate sha_1;
-mod tools;
pub mod client;
+pub mod groups;
pub mod server;
+mod tools;
pub mod types;
-pub mod groups;
//! First receive user's username and public value `a_pub`, retrieve from a
//! database `UserRecord` for a given username, generate `b` (e.g. 512 bits
//! long) and initialize SRP server instance:
-//!
+//!
//! ```ignore
//! use srp::groups::G_2048;
//!
//! 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:
-//!
+//!
//! ```ignore
//! let b_pub = server.get_b_pub();
//! conn.reply_to_handshake(&user.salt, b_pub);
//! ```
-//!
+//!
//! And finally recieve user proof, verify it and send server proof in the
//! reply:
-//!
+//!
//! ```ignore
//! let user_proof = conn.receive_proof();
//! let server_proof = server.verify(user_proof)?;
//! conn.send_proof(server_proof);
//! ```
-//!
+//!
//! To get the shared secret use `get_key` method. As alternative to using
//! `verify` method it's also possible to use this key for authentificated
//! encryption.
use std::marker::PhantomData;
-use num::{BigUint, Zero};
use digest::Digest;
use generic_array::GenericArray;
+use num::{BigUint, Zero};
use tools::powm;
use types::{SrpAuthError, SrpGroup};
key: GenericArray<u8, D::OutputSize>,
- d: PhantomData<D>
+ d: PhantomData<D>,
}
-impl< D: Digest> SrpServer< D> {
+impl<D: Digest> SrpServer<D> {
/// Create new server state.
- pub fn new(user: &UserRecord, a_pub: &[u8], b: &[u8], params: &SrpGroup)
- -> Result<Self, SrpAuthError>
- {
+ pub fn new(
+ user: &UserRecord,
+ a_pub: &[u8],
+ b: &[u8],
+ params: &SrpGroup,
+ ) -> Result<Self, SrpAuthError> {
let a_pub = BigUint::from_bytes_be(a_pub);
// Safeguard against malicious A
if &a_pub % ¶ms.n == BigUint::zero() {
- return Err(SrpAuthError { description: "Malicious a_pub value" })
+ return Err(SrpAuthError {
+ description: "Malicious a_pub value",
+ });
}
let v = BigUint::from_bytes_be(user.verifier);
- let b = BigUint::from_bytes_be(b) % ¶ms.n;
+ let b = BigUint::from_bytes_be(b) % ¶ms.n;
let k = params.compute_k::<D>();
// kv + g^b
let interm = (k * &v) % ¶ms.n;
let d = Default::default();
//(Av^u) ^ b
let key = {
- let u = BigUint::from_bytes_be(&u);
+ let u = BigUint::from_bytes_be(&u);
let t = (&a_pub * powm(&v, &u, ¶ms.n)) % ¶ms.n;
let s = powm(&t, &b, ¶ms.n);
D::digest(&s.to_bytes_be())
};
- Ok(Self { b, a_pub, b_pub, key, d})
+ Ok(Self {
+ b,
+ a_pub,
+ b_pub,
+ key,
+ d,
+ })
}
/// Get private `b` value. (see `new_with_b` documentation)
/// Process user proof of having the same shared secret and compute
/// server proof for sending to the user.
- pub fn verify(&self, user_proof: &[u8])
- -> Result<GenericArray<u8, D::OutputSize>, SrpAuthError>
- {
+ pub fn verify(
+ &self,
+ user_proof: &[u8],
+ ) -> Result<GenericArray<u8, D::OutputSize>, SrpAuthError> {
// M = H(A, B, K)
let mut d = D::new();
d.input(&self.a_pub.to_bytes_be());
d.input(&self.key);
Ok(d.result())
} else {
- Err(SrpAuthError { description: "Incorrect user proof" })
+ Err(SrpAuthError {
+ description: "Incorrect user proof",
+ })
}
}
}
//! Additional SRP types.
-use std::{fmt, error};
+use digest::Digest;
use num::BigUint;
+use std::{error, fmt};
use tools::powm;
-use digest::Digest;
/// SRP authentification error.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct SrpAuthError {
- pub(crate) description: &'static str
+ pub(crate) description: &'static str,
}
impl fmt::Display for SrpAuthError {
#[cfg(test)]
mod tests {
- use ::groups::G_1024;
+ use groups::G_1024;
use sha_1::Sha1;
#[test]
extern crate num;
-extern crate sha2;
extern crate rand;
+extern crate sha2;
extern crate srp;
-use sha2::Sha256;
use rand::Rng;
+use sha2::Sha256;
+use srp::client::{srp_private_key, SrpClient};
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 a_pub = client.get_a_pub();
// Server retrieve user record from db and processes handshake
- let user = UserRecord { username, salt: &salt, verifier: &verif };
+ let user = UserRecord {
+ username,
+ salt: &salt,
+ verifier: &verif,
+ };
let b = rng.gen_iter::<u8>().take(64).collect::<Vec<u8>>();
- let server = SrpServer::<Sha256>::new(&user, &a_pub, &b, &G_2048)
- .unwrap();
+ 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