-
Notifications
You must be signed in to change notification settings - Fork 35
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
Standard implementation option #153
base: master
Are you sure you want to change the base?
Changes from 2 commits
f01e9b9
f4ffa0f
15cb641
6cd45bb
8c163b4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -82,6 +82,17 @@ | |
//! let pwd_verifier = client.compute_verifier(username, password, salt); | ||
//! send_registration_data(username, salt, &pwd_verifier); | ||
//! ``` | ||
//! | ||
//! | ||
//! Alternatively, you can instantiate the client with options to use the implementation | ||
//! from the specification of SRP (that generates M1 differently) or omit the username | ||
//! when calculating X to be able to authenticate the same users with their different IDs. | ||
//! | ||
//! ```rust | ||
//! let by_the_spec = true; | ||
//! let no_user_in_x = true; | ||
//! let client = crate::srp::client::SrpClient::<sha2::Sha256>::new_with_options(&crate::srp::groups::G_2048, by_the_spec, no_user_in_x); | ||
//! ``` | ||
|
||
use std::marker::PhantomData; | ||
|
||
|
@@ -90,11 +101,13 @@ use num_bigint::BigUint; | |
use subtle::ConstantTimeEq; | ||
|
||
use crate::types::{SrpAuthError, SrpGroup}; | ||
use crate::utils::{compute_k, compute_m1, compute_m2, compute_u}; | ||
use crate::utils::{compute_k, compute_m1, compute_m1_std, compute_m2, compute_u}; | ||
|
||
/// SRP client state before handshake with the server. | ||
pub struct SrpClient<'a, D: Digest> { | ||
params: &'a SrpGroup, | ||
by_the_spec: bool, | ||
no_user_in_x: bool, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this a separate variable? |
||
d: PhantomData<D>, | ||
} | ||
|
||
|
@@ -111,6 +124,21 @@ impl<'a, D: Digest> SrpClient<'a, D> { | |
pub const fn new(params: &'a SrpGroup) -> Self { | ||
Self { | ||
params, | ||
by_the_spec: false, | ||
no_user_in_x: false, | ||
d: PhantomData, | ||
} | ||
} | ||
|
||
pub const fn new_with_options( | ||
params: &'a SrpGroup, | ||
by_the_spec: bool, | ||
no_user_in_x: bool, | ||
) -> Self { | ||
Self { | ||
params, | ||
by_the_spec, | ||
no_user_in_x, | ||
d: PhantomData, | ||
} | ||
} | ||
|
@@ -170,6 +198,7 @@ impl<'a, D: Digest> SrpClient<'a, D> { | |
/// Get password verifier (v in RFC5054) for user registration on the server. | ||
#[must_use] | ||
pub fn compute_verifier(&self, username: &[u8], password: &[u8], salt: &[u8]) -> Vec<u8> { | ||
let username = if self.no_user_in_x { &[] } else { username }; | ||
let identity_hash = Self::compute_identity_hash(username, password); | ||
let x = Self::compute_x(identity_hash.as_slice(), salt); | ||
self.compute_v(&x).to_bytes_be() | ||
|
@@ -205,24 +234,36 @@ impl<'a, D: Digest> SrpClient<'a, D> { | |
|
||
let u = compute_u::<D>(&a_pub.to_bytes_be(), &b_pub.to_bytes_be()); | ||
let k = compute_k::<D>(self.params); | ||
let username = if self.no_user_in_x { &[] } else { username }; | ||
let identity_hash = Self::compute_identity_hash(username, password); | ||
let x = Self::compute_x(identity_hash.as_slice(), salt); | ||
|
||
let key = self.compute_premaster_secret(&b_pub, &k, &x, &a, &u); | ||
|
||
let m1 = compute_m1::<D>( | ||
&a_pub.to_bytes_be(), | ||
&b_pub.to_bytes_be(), | ||
&key.to_bytes_be(), | ||
); | ||
let key = if self.by_the_spec { | ||
let mut u = D::new(); | ||
u.update(key.to_bytes_be()); | ||
u.finalize().to_vec() | ||
} else { | ||
key.to_bytes_be() | ||
}; | ||
|
||
let m1 = if self.by_the_spec { | ||
compute_m1_std::<D>( | ||
self.params, | ||
username, | ||
salt, | ||
&a_pub.to_bytes_be(), | ||
&b_pub.to_bytes_be(), | ||
key.as_slice(), | ||
) | ||
} else { | ||
compute_m1::<D>(&a_pub.to_bytes_be(), &b_pub.to_bytes_be(), key.as_slice()) | ||
}; | ||
|
||
let m2 = compute_m2::<D>(&a_pub.to_bytes_be(), &m1, &key.to_bytes_be()); | ||
let m2 = compute_m2::<D>(&a_pub.to_bytes_be(), &m1, key.as_slice()); | ||
|
||
Ok(SrpClientVerifier { | ||
m1, | ||
m2, | ||
key: key.to_bytes_be(), | ||
}) | ||
Ok(SrpClientVerifier { m1, m2, key }) | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -27,8 +27,25 @@ pub fn compute_k<D: Digest>(params: &SrpGroup) -> BigUint { | |||||||||||||
BigUint::from_bytes_be(d.finalize().as_slice()) | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
// H(N) XOR H(PAD(g)) | ||||||||||||||
#[must_use] | ||||||||||||||
pub fn compute_hash_n_xor_hash_g<D: Digest>(params: &SrpGroup) -> Vec<u8> { | ||||||||||||||
let n = params.n.to_bytes_be(); | ||||||||||||||
let g_bytes = params.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 h_n = compute_hash::<D>(&n).to_vec(); | ||||||||||||||
let h_g = compute_hash::<D>(&buf).to_vec(); | ||||||||||||||
|
||||||||||||||
h_n.iter() | ||||||||||||||
.zip(h_g.iter()) | ||||||||||||||
.map(|(&x1, &x2)| x1 ^ x2) | ||||||||||||||
.collect() | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
// M1 = H(A, B, K) this doesn't follow the spec but apparently no one does for M1 | ||||||||||||||
// M1 should equal = H(H(N) XOR H(g) | H(U) | s | A | B | K) according to the spec | ||||||||||||||
#[must_use] | ||||||||||||||
pub fn compute_m1<D: Digest>(a_pub: &[u8], b_pub: &[u8], key: &[u8]) -> Output<D> { | ||||||||||||||
let mut d = D::new(); | ||||||||||||||
|
@@ -38,6 +55,33 @@ pub fn compute_m1<D: Digest>(a_pub: &[u8], b_pub: &[u8], key: &[u8]) -> Output<D | |||||||||||||
d.finalize() | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
#[must_use] | ||||||||||||||
pub fn compute_hash<D: Digest>(data: &[u8]) -> Output<D> { | ||||||||||||||
let mut d = D::new(); | ||||||||||||||
d.update(data); | ||||||||||||||
d.finalize() | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
// M1 = H(H(N) XOR H(g) | H(U) | s | A | B | K) this follows the spec | ||||||||||||||
#[must_use] | ||||||||||||||
pub fn compute_m1_std<D: Digest>( | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Spec" doesn't say which spec, and Perhaps use RFC5054 instead?
Suggested change
|
||||||||||||||
params: &SrpGroup, | ||||||||||||||
username: &[u8], | ||||||||||||||
salt: &[u8], | ||||||||||||||
a_pub: &[u8], | ||||||||||||||
b_pub: &[u8], | ||||||||||||||
key: &[u8], | ||||||||||||||
) -> Output<D> { | ||||||||||||||
let mut d = D::new(); | ||||||||||||||
d.update(compute_hash_n_xor_hash_g::<D>(params)); | ||||||||||||||
d.update(compute_hash::<D>(username)); | ||||||||||||||
d.update(salt); | ||||||||||||||
d.update(a_pub); | ||||||||||||||
d.update(b_pub); | ||||||||||||||
d.update(key); | ||||||||||||||
d.finalize() | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
// M2 = H(A, M1, K) | ||||||||||||||
#[must_use] | ||||||||||||||
pub fn compute_m2<D: Digest>(a_pub: &[u8], m1: &Output<D>, key: &[u8]) -> Output<D> { | ||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
use hex_literal::hex; | ||
use num_bigint::BigUint; | ||
use sha2::Sha256; | ||
use srp::client::SrpClient; | ||
use srp::groups::G_2048; | ||
use srp::server::SrpServer; | ||
use srp::utils::{compute_hash, compute_k, compute_u}; | ||
|
||
#[test] | ||
#[allow(clippy::many_single_char_names)] | ||
fn rfc5054_standard() { | ||
let i = b""; | ||
let p = hex!("CCAF1BCA 820E8E6B 392C0EBA 7014CC9C DF49A650 84B39A53 834CC090 92BCDA20"); | ||
let s = hex!("BC01D972 31E5A4BC 79171C6D 83783FF2"); | ||
let group = &G_2048; | ||
|
||
let k = compute_k::<Sha256>(group); | ||
|
||
assert_eq!( | ||
k.to_bytes_be(), | ||
hex!("05B9E8EF 059C6B32 EA59FC1D 322D37F0 4AA30BAE 5AA9003B 8321E21D DB04E300"), | ||
"bad k value" | ||
); | ||
|
||
let identity_hash = SrpClient::<Sha256>::compute_identity_hash(i, &p); | ||
let x = SrpClient::<Sha256>::compute_x(identity_hash.as_slice(), &s); | ||
|
||
assert_eq!( | ||
x.to_bytes_be(), | ||
hex!("FD1709C7 30244792 F33348CB FBBD4AB4 39AC8090 FCDDC474 46244073 0D85ADDB"), | ||
"bad x value" | ||
); | ||
|
||
let client = SrpClient::<Sha256>::new_with_options(group, true, true); | ||
let v = client.compute_v(&x); | ||
|
||
assert_eq!( | ||
v.to_bytes_be(), | ||
hex!( | ||
" | ||
A2E59E34 4EC9AB6D 611BFC12 4A2E5DC7 46174702 9AC44A6F 6A8DB9E2 | ||
7326A5CB E370C469 A20D59CD 63FA13E4 1F0F1968 61A0AA3A 778AB5F5 | ||
2A0D57E9 BC3E9494 7ACDA1BD 3E62785D DB51FCE1 D2A34C87 E95CAD5A | ||
30731035 269E72AF 235E4537 62F94011 C965E1D3 F940A196 43B56810 | ||
D7CD38AE 4DBB7CFF 80E529FE E33CBB88 C7877096 62342D98 314687BF | ||
0A5B0AE2 E6595B9A DC61B1BE 691E3176 62A01A24 BE963C70 8565694F | ||
575DCEA2 791364C7 465B1BD4 6D8BC9F7 53F3F6E5 C55491F2 080D00D5 | ||
40F6E247 53AFB477 C33BC117 A0D6551A 16026D96 22F3AD50 6C379EFD | ||
85A075E7 C6D0DA46 442D6084 7095D43A 3E3C5EC2 6F523479 B3F2902B | ||
641A7B92 | ||
" | ||
), | ||
"bad v value" | ||
); | ||
|
||
let a = BigUint::from_bytes_be(&hex!( | ||
" | ||
81010101 01010101 01010101 01010101 01010101 01010101 01010101 | ||
01010101 | ||
" | ||
)); | ||
|
||
let b = BigUint::from_bytes_be(&hex!( | ||
" | ||
E487CB59 D31AC550 471E81F0 0F6928E0 1DDA08E9 74A004F4 9E61F5D1 | ||
05284D20 | ||
" | ||
)); | ||
|
||
let a_pub = client.compute_a_pub(&a); | ||
|
||
assert_eq!( | ||
a_pub.to_bytes_be(), | ||
hex!( | ||
" | ||
01F9D75A 9DF8AC07 FB3684C4 DDF9ABD9 4CC03C10 1A381976 F16C92B5 | ||
8083BB98 4137AD44 7D815819 529E0313 FCC4EDD4 5F31D033 CBC0FB4B | ||
F0CBB75A B0A2A10C 4C0C1C23 A62BA798 AB308818 C94F017C 2015BD3A | ||
4B2334B0 E2125F57 E12A2D31 936856B2 7BE1A615 8D32FC65 48A6B4BB | ||
62E63A13 8EF89664 CC1F43E7 457DE565 E1551F34 29A4B73A 7FAB0D9D | ||
821EF749 7A8A1D84 D637FA8E 443F57CE AF12D0B0 54A67726 3D7C15A4 | ||
C88D87B9 136684BA 4AAB3466 524D9A47 30FBE924 1194B3E4 E61EB9A7 | ||
67401AA3 E2D66AD0 B07CFF63 B41CD665 E0EC8BC2 75D16A49 E6ECAC4F | ||
2BB3AF76 BC2CBA64 83B665A6 CA804DB8 5093B091 77114E70 9DD8DFB1 | ||
7A8B98CD | ||
" | ||
), | ||
"bad a_pub value" | ||
); | ||
|
||
let server = SrpServer::<Sha256>::new(group); | ||
let b_pub = server.compute_b_pub(&b, &k, &v); | ||
|
||
assert_eq!( | ||
b_pub.to_bytes_be(), | ||
hex!( | ||
" | ||
7F75618C 8C3EC5D7 CDD11D6A C2C24157 0D3254FA 39CFF9C0 DBDD39BC | ||
B6161B2D 12FEE512 0814D17C 6CD56E37 EC9AFC86 8213C60F 672CA6D7 | ||
436AEE09 11F59AC6 30DAE4F0 70B15E84 86200B1B 0163900D 2EBB612E | ||
963F1AC8 E083F70F E5484F83 559C11A2 C1936C79 361FCCA7 E9B21192 | ||
14416D3F 487C3874 A76D3B23 29A0690B DA774225 104D06B9 418D6207 | ||
75D64CE8 54004E07 50F64299 D13B5AA2 3AEBF69B 56814E17 EBC1784B | ||
1881E6BE 651DEABE 2C6E78DE 0A84C032 0DE266B4 1444DC2F 397F0436 | ||
8B62BDDE F573D274 EA304F40 B7CDB74A 345AD036 C5ED746F B3F7D627 | ||
597FC9C6 A453BF05 D11185E0 EBB74797 D4490903 2D9749F7 8AB8ED63 | ||
FCBFCA58 | ||
" | ||
), | ||
"bad b_pub value" | ||
); | ||
|
||
let u = compute_u::<Sha256>(&a_pub.to_bytes_be(), &b_pub.to_bytes_be()); | ||
|
||
assert_eq!( | ||
u.to_bytes_be(), | ||
hex!("6B527E30 667D330D 84874755 1E17E271 BA465393 CA48264C D37E59DF 18267B37"), | ||
"bad u value" | ||
); | ||
|
||
assert_eq!( | ||
compute_hash::<Sha256>( | ||
&client | ||
.compute_premaster_secret(&b_pub, &k, &x, &a, &u) | ||
.to_bytes_be() | ||
) | ||
.as_slice(), | ||
hex!( | ||
" | ||
CD315DD4 2652B85B FFDD273E EF98FEDE 3C77E0AE 07898ABE A60FEEA6 | ||
EE706231 | ||
" | ||
), | ||
"bad client premaster" | ||
); | ||
|
||
assert_eq!( | ||
compute_hash::<Sha256>( | ||
&server | ||
.compute_premaster_secret(&a_pub, &v, &u, &b) | ||
.to_bytes_be() | ||
) | ||
.as_slice(), | ||
hex!( | ||
" | ||
CD315DD4 2652B85B FFDD273E EF98FEDE 3C77E0AE 07898ABE A60FEEA6 | ||
EE706231 | ||
" | ||
), | ||
"bad server premaster" | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An enum might be nice here for clarity