Skip to content

Commit

Permalink
Bump digest, sha-1, and sha2 dependencies to v0.9 (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
masihyeganeh authored Oct 7, 2020
1 parent 9f42267 commit 00d7d43
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 66 deletions.
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

0 comments on commit 00d7d43

Please sign in to comment.