diff options
author | Brian Warner <warner@lothar.com> | 2017-05-24 18:05:27 -0700 |
---|---|---|
committer | Brian Warner <warner@lothar.com> | 2017-05-24 18:05:27 -0700 |
commit | 2c0792059c573cf12870f2aa24c9df993deef983 (patch) | |
tree | 96a68adb9e81bf3af9143911d3c0eb19caa74b26 | |
parent | ae521873c62786958051c2ae8aba5eda3e4c8114 (diff) | |
download | PAKEs-2c0792059c573cf12870f2aa24c9df993deef983.tar.xz |
improvements for tests, which of course fail
-rw-r--r-- | Cargo.toml | 4 | ||||
-rw-r--r-- | src/lib.rs | 6 | ||||
-rw-r--r-- | src/spake2.rs | 173 |
3 files changed, 163 insertions, 20 deletions
@@ -9,4 +9,6 @@ curve25519-dalek = "0.8.0" rand = "0.3.0" sha2 = "0.4.0" - +[dev-dependencies] +num-bigint = "0.1" +hex = "0.2" @@ -2,12 +2,16 @@ extern crate rand; extern crate curve25519_dalek; extern crate sha2; -extern crate core; mod spake2; pub use spake2::*; #[cfg(test)] +extern crate num_bigint; +#[cfg(test)] +extern crate hex; + +#[cfg(test)] mod tests { use spake2::{SPAKE2, Ed25519Group}; diff --git a/src/spake2.rs b/src/spake2.rs index fe0fe4f..e3efa2a 100644 --- a/src/spake2.rs +++ b/src/spake2.rs @@ -1,4 +1,3 @@ -#![allow(dead_code)] use curve25519_dalek::scalar::Scalar as c2_Scalar; use curve25519_dalek::curve::ExtendedPoint as c2_Element; @@ -26,6 +25,7 @@ pub trait Group { fn scalar_neg(s: &Self::Scalar) -> Self::Scalar; fn element_to_bytes(e: &Self::Element) -> Vec<u8>; fn bytes_to_element(b: &[u8]) -> Option<Self::Element>; + fn element_length() -> usize; fn basepoint_mult(s: &Self::Scalar) -> Self::Element; fn scalarmult(e: &Self::Element, s: &Self::Scalar) -> Self::Element; fn add(a: &Self::Element, b: &Self::Element) -> Self::Element; @@ -85,6 +85,9 @@ impl Group for Ed25519Group { fn element_to_bytes(s: &c2_Element) -> Vec<u8> { s.compress_edwards().as_bytes().to_vec() } + fn element_length() -> usize { + 32 + } fn bytes_to_element(b: &[u8]) -> Option<c2_Element> { if b.len() != 32 { return None; } //let mut bytes: [u8; 32] = @@ -129,14 +132,12 @@ pub struct SPAKE2<G: Group> { //where &G::Scalar: Neg { } impl<G: Group> SPAKE2<G> { - fn start_internal<T: Rng>(side: Side, - password: &[u8], - id_a: &[u8], id_b: &[u8], id_s: &[u8], - rng: &mut T) - -> (SPAKE2<G>, Vec<u8>) { + fn start_internal(side: Side, + password: &[u8], + id_a: &[u8], id_b: &[u8], id_s: &[u8], + xy_scalar: G::Scalar) -> (SPAKE2<G>, Vec<u8>) { //let password_scalar: G::Scalar = hash_to_scalar::<G::Scalar>(password); let password_scalar: G::Scalar = G::hash_to_scalar(password); - let xy: G::Scalar = G::random_scalar(rng); // a: X = B*x + M*pw // b: Y = B*y + N*pw @@ -146,7 +147,7 @@ impl<G: Group> SPAKE2<G> { Side::B => G::const_n(), Side::Symmetric => G::const_s(), }; - let m1: G::Element = G::add(&G::basepoint_mult(&xy), + let m1: G::Element = G::add(&G::basepoint_mult(&xy_scalar), &G::scalarmult(&blinding, &password_scalar)); //let m1: G::Element = &G::basepoint_mult(&x) + &(blinding * &password_scalar); let msg1: Vec<u8> = G::element_to_bytes(&m1); @@ -159,44 +160,75 @@ impl<G: Group> SPAKE2<G> { let mut id_s_copy = Vec::new(); id_s_copy.extend_from_slice(id_s); + let mut msg_and_side = Vec::new(); + msg_and_side.push(match side { + Side::A => 0x41, // 'A' + Side::B => 0x42, // 'B' + Side::Symmetric => 0x53, // 'S' + }); + msg_and_side.extend_from_slice(&msg1); + (SPAKE2 { side: side, - xy_scalar: xy, + xy_scalar: xy_scalar, password_vec: password_vec, // string id_a: id_a_copy, id_b: id_b_copy, id_s: id_s_copy, msg1: msg1.clone(), password_scalar: password_scalar, // scalar - }, msg1) + }, msg_and_side) + } + + fn start_a_internal(password: &[u8], id_a: &[u8], id_b: &[u8], + xy_scalar: G::Scalar) -> (SPAKE2<G>, Vec<u8>) { + Self::start_internal(Side::A, + password, id_a, id_b, b"", xy_scalar) + } + + fn start_b_internal(password: &[u8], id_a: &[u8], id_b: &[u8], + xy_scalar: G::Scalar) -> (SPAKE2<G>, Vec<u8>) { + Self::start_internal(Side::B, + password, id_a, id_b, b"", xy_scalar) + } + + fn start_symmetric_internal(password: &[u8], id_s: &[u8], + xy_scalar: G::Scalar) -> (SPAKE2<G>, Vec<u8>) { + Self::start_internal(Side::Symmetric, + password, b"", b"", id_s, xy_scalar) } + pub fn start_a(password: &[u8], id_a: &[u8], id_b: &[u8]) -> (SPAKE2<G>, Vec<u8>) { let mut cspring: OsRng = OsRng::new().unwrap(); - Self::start_internal(Side::A, - password, id_a, id_b, b"", &mut cspring) + let xy_scalar: G::Scalar = G::random_scalar(&mut cspring); + Self::start_a_internal(password, id_a, id_b, xy_scalar) } pub fn start_b(password: &[u8], id_a: &[u8], id_b: &[u8]) -> (SPAKE2<G>, Vec<u8>) { let mut cspring: OsRng = OsRng::new().unwrap(); - Self::start_internal(Side::B, - password, id_a, id_b, b"", &mut cspring) + let xy_scalar: G::Scalar = G::random_scalar(&mut cspring); + Self::start_b_internal(password, id_a, id_b, xy_scalar) } - pub fn start_symmetric(password: &[u8], id_s: &[u8]) -> (SPAKE2<G>, Vec<u8>) { let mut cspring: OsRng = OsRng::new().unwrap(); - Self::start_internal(Side::Symmetric, - password, b"", b"", id_s, &mut cspring) + let xy_scalar: G::Scalar = G::random_scalar(&mut cspring); + Self::start_symmetric_internal(password, id_s, xy_scalar) } pub fn finish(self, msg2: &[u8]) -> Result<Vec<u8>, SPAKEErr> { + if msg2.len() != 1 + G::element_length() { + return Err(SPAKEErr); //("inbound message is the wrong length")); + } + let msg_side = msg2[0]; + + let msg2_element = G::bytes_to_element(&msg2[1..]).unwrap(); // a: K = (Y+N*(-pw))*x // b: K = (X+M*(-pw))*y - let msg2_element = G::bytes_to_element(msg2).unwrap(); let unblinding = match self.side { Side::A => G::const_n(), Side::B => G::const_m(), @@ -287,4 +319,109 @@ impl<G: Group> SPAKE2<G> { #[cfg(test)] mod test { + /* This compares results against the python compatibility tests: + spake2.test.test_compat.SPAKE2.test_asymmetric . The python test passes a + deterministic RNG (used only for tests, of course) into the per-Group + "random_scalar()" function, which results in some particular scalar. + */ + use num_bigint::BigUint; + use curve25519_dalek::scalar::Scalar; + use spake2::{SPAKE2, Ed25519Group}; + + // the python tests show the long-integer form of scalars. the rust code + // wants an array of bytes (little-endian). Make sure the way we convert + // things works correctly. + + fn decimal_to_scalar(d: &[u8]) -> Scalar { + let bytes = BigUint::parse_bytes(d, 10).unwrap().to_bytes_le(); + assert_eq!(bytes.len(), 32); + let mut s = Scalar([0u8; 32]); + s.0.copy_from_slice(&bytes); + s + } + + #[test] + fn test_convert() { + let t1_decimal = b"2238329342913194256032495932344128051776374960164957527413114840482143558222"; + let t1_scalar = decimal_to_scalar(t1_decimal); + let expected: Scalar = Scalar( + [0x4e, 0x5a, 0xb4, 0x34, 0x5d, 0x47, 0x08, 0x84, + 0x59, 0x13, 0xb4, 0x64, 0x1b, 0xc2, 0x7d, 0x52, + 0x52, 0xa5, 0x85, 0x10, 0x1b, 0xcc, 0x42, 0x44, + 0xd4, 0x49, 0xf4, 0xa8, 0x79, 0xd9, 0xf2, 0x04]); + assert_eq!(t1_scalar, expected); + //println!("t1_scalar is {:?}", t1_scalar); + } + + use hex::ToHex; + use curve25519_dalek::constants::ED25519_BASEPOINT; + #[test] + fn test_serialize_basepoint() { + // make sure elements are serialized same as the python library + let exp = "5866666666666666666666666666666666666666666666666666666666666666"; + let base_vec = ED25519_BASEPOINT.compress_edwards().as_bytes().to_vec(); + let base_hex = base_vec.to_hex(); + println!("exp: {:?}", exp); + println!("got: {:?}", base_hex); + assert_eq!(exp, base_hex); + } + + #[test] + fn test_sizes() { + let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(b"password", b"idA", + b"idB"); + assert_eq!(msg1.len(), 1+32); + let (s2, msg2) = SPAKE2::<Ed25519Group>::start_b(b"password", b"idA", + b"idB"); + assert_eq!(msg2.len(), 1+32); + let key1 = s1.finish(&msg2).unwrap(); + let key2 = s2.finish(&msg1).unwrap(); + assert_eq!(key1.len(), 32); + assert_eq!(key2.len(), 32); + + let (s1, msg1) = SPAKE2::<Ed25519Group>::start_symmetric(b"password", + b"idS"); + assert_eq!(msg1.len(), 1+32); + let (s2, msg2) = SPAKE2::<Ed25519Group>::start_symmetric(b"password", + b"idS"); + assert_eq!(msg2.len(), 1+32); + let key1 = s1.finish(&msg2).unwrap(); + let key2 = s2.finish(&msg1).unwrap(); + assert_eq!(key1.len(), 32); + assert_eq!(key2.len(), 32); + } + + #[test] + fn test_asymmetric() { + let scalar_a = decimal_to_scalar(b"2611694063369306139794446498317402240796898290761098242657700742213257926693"); + let scalar_b = decimal_to_scalar(b"7002393159576182977806091886122272758628412261510164356026361256515836884383"); + let expected_pw_scalar = decimal_to_scalar(b"3515301705789368674385125653994241092664323519848410154015274772661223168839"); + + println!("scalar_a is {}", scalar_a.as_bytes().to_hex()); + + let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a_internal( + b"password", b"idA", b"idB", scalar_a); + let expected_msg1 = "416fc960df73c9cf8ed7198b0c9534e2e96a5984bfc5edc023fd24dacf371f2af9"; + + println!("msg1: {:?}", msg1.to_hex()); + println!("exp : {:?}", expected_msg1); + + assert_eq!(expected_pw_scalar.as_bytes().to_hex(), + s1.xy_scalar.as_bytes().to_hex()); + assert_eq!(msg1.to_hex(), expected_msg1); + + let (s2, msg2) = SPAKE2::<Ed25519Group>::start_b_internal( + b"password", b"idA", b"idB", scalar_b); + assert_eq!(expected_pw_scalar, s2.xy_scalar); + assert_eq!(msg2.to_hex(), + "42354e97b88406922b1df4bea1d7870f17aed3dba7c720b313edae315b00959309"); + + let key1 = s1.finish(&msg2).unwrap(); + let key2 = s2.finish(&msg1).unwrap(); + assert_eq!(key1, key2); + assert_eq!(key1.to_hex(), + "a480bca13fa04464bb644f10e340125e96c9494f7399fef7c2bda67eb0fdf06d"); + } + + } |