Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bump digest, sha-1, and sha2 dependencies to v0.9 #37

Merged
merged 17 commits into from
Oct 7, 2020
6 changes: 3 additions & 3 deletions srp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ categories = ["cryptography", "authentication"]
[dependencies]
num-bigint = "0.2"
generic-array = "0.12"
digest = "0.8"
digest = "0.9"
lazy_static = "1.2"

[dev-dependencies]
rand = "0.6"
sha2 = "0.8"
sha-1 = "0.8"
sha2 = "0.9"
sha-1 = "0.9"

[badges]
travis-ci = { repository = "RustCrypto/PAKEs" }
128 changes: 88 additions & 40 deletions srp/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@
//! ```
use std::marker::PhantomData;

use digest::Digest;
use generic_array::GenericArray;
use digest::{Digest, Output};
use num_bigint::BigUint;

use crate::tools::powm;
Expand All @@ -77,29 +76,25 @@ pub struct SrpClient<'a, D: Digest> {

/// SRP client state after handshake with the server.
pub struct SrpClientVerifier<D: Digest> {
proof: GenericArray<u8, D::OutputSize>,
server_proof: GenericArray<u8, D::OutputSize>,
key: GenericArray<u8, D::OutputSize>,
proof: Output<D>,
server_proof: Output<D>,
key: Output<D>,
}

/// 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]) -> Output<D> {
let p = {
let mut d = D::new();
d.input(username);
d.input(b":");
d.input(password);
d.result()
d.update(username);
d.update(b":");
d.update(password);
d.finalize()
};
let mut d = D::new();
d.input(salt);
d.input(&p);
d.result()
d.update(salt);
d.update(p.as_slice());
d.finalize()
}

impl<'a, D: Digest> SrpClient<'a, D> {
Expand All @@ -123,12 +118,7 @@ impl<'a, D: Digest> SrpClient<'a, D> {
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) -> Output<D> {
let n = &self.params.n;
let k = self.params.compute_k::<D>();
let interm = (k * self.params.powm(x)) % n;
Expand All @@ -151,9 +141,10 @@ impl<'a, D: Digest> SrpClient<'a, D> {
) -> Result<SrpClientVerifier<D>, SrpAuthError> {
let u = {
let mut d = D::new();
d.input(&self.a_pub.to_bytes_be());
d.input(b_pub);
BigUint::from_bytes_be(&d.result())
d.update(&self.a_pub.to_bytes_be());
d.update(b_pub);
let h = d.finalize();
BigUint::from_bytes_be(h.as_slice())
};

let b_pub = BigUint::from_bytes_be(b_pub);
Expand All @@ -170,19 +161,79 @@ impl<'a, D: Digest> SrpClient<'a, D> {
// M1 = H(A, B, K)
let proof = {
let mut d = D::new();
d.input(&self.a_pub.to_bytes_be());
d.input(&b_pub.to_bytes_be());
d.input(&key);
d.result()
d.update(&self.a_pub.to_bytes_be());
d.update(&b_pub.to_bytes_be());
d.update(&key);
d.finalize()
};

// M2 = H(A, M1, K)
let server_proof = {
let mut d = D::new();
d.input(&self.a_pub.to_bytes_be());
d.input(&proof);
d.input(&key);
d.result()
d.update(&self.a_pub.to_bytes_be());
d.update(&proof);
d.update(&key);
d.finalize()
};

Ok(SrpClientVerifier {
proof,
server_proof,
key,
})
}

/// Process server reply to the handshake with username and salt.
pub fn process_reply_with_username_and_salt(
self,
username: &[u8],
salt: &[u8],
private_key: &[u8],
b_pub: &[u8],
) -> Result<SrpClientVerifier<D>, SrpAuthError> {
let u = {
let mut d = D::new();
d.update(&self.a_pub.to_bytes_be());
d.update(b_pub);
let h = d.finalize();
BigUint::from_bytes_be(h.as_slice())
};

let b_pub = BigUint::from_bytes_be(b_pub);

// Safeguard against malicious B
if &b_pub % &self.params.n == BigUint::default() {
return Err(SrpAuthError {
description: "Malicious b_pub value",
});
}

let x = BigUint::from_bytes_be(private_key);
let key = self.calc_key(&b_pub, &x, &u);
// M1 = H(H(N)^H(g), H(I), salt, A, B, K)
let proof = {
let mut d = D::new();
d.update(username);
let h = d.finalize_reset();
let I: &[u8] = h.as_slice();

d.update(self.params.compute_hash_n_xor_hash_g::<D>());
d.update(I);
d.update(salt);
d.update(&self.a_pub.to_bytes_be());
d.update(&b_pub.to_bytes_be());
d.update(&key.to_vec());
d.finalize()
};
let x = proof.to_vec().as_slice();

// M2 = H(A, M1, K)
let server_proof = {
let mut d = D::new();
d.update(&self.a_pub.to_bytes_be());
d.update(&proof);
d.update(&key);
d.finalize()
};

Ok(SrpClientVerifier {
Expand All @@ -202,21 +253,18 @@ impl<D: Digest> SrpClientVerifier<D> {
/// Get shared secret key without authenticating server, e.g. for using with
/// authenticated encryption modes. DO NOT USE this method without
/// some kind of secure authentication
pub fn get_key(self) -> GenericArray<u8, D::OutputSize> {
pub fn get_key(self) -> Output<D> {
self.key
}

/// Verification data for sending to the server.
pub fn get_proof(&self) -> GenericArray<u8, D::OutputSize> {
pub fn get_proof(&self) -> Output<D> {
self.proof.clone()
}

/// 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<Output<D>, SrpAuthError> {
if self.server_proof.as_slice() != reply {
Err(SrpAuthError {
description: "Incorrect server proof",
Expand Down
36 changes: 16 additions & 20 deletions srp/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@
//! encryption.
use std::marker::PhantomData;

use digest::Digest;
use generic_array::GenericArray;
use digest::{Digest, Output};
use num_bigint::BigUint;

use crate::tools::powm;
Expand All @@ -57,7 +56,7 @@ pub struct SrpServer<D: Digest> {
a_pub: BigUint,
b_pub: BigUint,

key: GenericArray<u8, D::OutputSize>,
key: Output<D>,

d: PhantomData<D>,
}
Expand Down Expand Up @@ -86,14 +85,14 @@ impl<D: Digest> SrpServer<D> {
// H(A || B)
let u = {
let mut d = D::new();
d.input(&a_pub.to_bytes_be());
d.input(&b_pub.to_bytes_be());
d.result()
d.update(&a_pub.to_bytes_be());
d.update(&b_pub.to_bytes_be());
d.finalize()
};
let d = Default::default();
//(Av^u) ^ b
let key = {
let u = BigUint::from_bytes_be(&u);
let u = BigUint::from_bytes_be(u.as_slice());
let t = (&a_pub * powm(&v, &u, &params.n)) % &params.n;
let s = powm(&t, &b, &params.n);
D::digest(&s.to_bytes_be())
Expand All @@ -119,29 +118,26 @@ impl<D: Digest> SrpServer<D> {

/// Get shared secret between user and the server. (do not forget to verify
/// that keys are the same!)
pub fn get_key(&self) -> GenericArray<u8, D::OutputSize> {
pub fn get_key(&self) -> Output<D> {
self.key.clone()
}

/// 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<Output<D>, SrpAuthError> {
// M = H(A, B, K)
let mut d = D::new();
d.input(&self.a_pub.to_bytes_be());
d.input(&self.b_pub.to_bytes_be());
d.input(&self.key);
d.update(&self.a_pub.to_bytes_be());
d.update(&self.b_pub.to_bytes_be());
d.update(&self.key);

if user_proof == d.result().as_slice() {
if user_proof == d.finalize().as_slice() {
// H(A, M, K)
let mut d = D::new();
d.input(&self.a_pub.to_bytes_be());
d.input(user_proof);
d.input(&self.key);
Ok(d.result())
d.update(&self.a_pub.to_bytes_be());
d.update(user_proof);
d.update(&self.key);
Ok(d.finalize())
} else {
Err(SrpAuthError {
description: "Incorrect user proof",
Expand Down
28 changes: 25 additions & 3 deletions srp/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,31 @@ impl SrpGroup {
buf[l..].copy_from_slice(&g_bytes);

let mut d = D::new();
d.input(&n);
d.input(&buf);
BigUint::from_bytes_be(&d.result())
d.update(&n);
d.update(&buf);
BigUint::from_bytes_be(&d.finalize().as_slice())
}

/// Compute `Hash(N) xor Hash(g)` with given hash function and return SRP parameters
pub(crate) fn compute_hash_n_xor_hash_g<D: Digest>(&self) -> Vec<u8> {
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.update(&n);
let h = d.finalize_reset();
let h_n: &[u8] = h.as_slice();
d.update(&buf);
let h = d.finalize_reset();
let h_g: &[u8] = h.as_slice();

h_n.iter()
.zip(h_g.iter())
.map(|(&x1, &x2)| x1 ^ x2)
.collect()
}
}

Expand Down