]> git.lizzy.rs Git - PAKEs.git/commitdiff
improvements for tests, which of course fail
authorBrian Warner <warner@lothar.com>
Thu, 25 May 2017 01:05:27 +0000 (18:05 -0700)
committerBrian Warner <warner@lothar.com>
Thu, 25 May 2017 01:05:27 +0000 (18:05 -0700)
Cargo.toml
src/lib.rs
src/spake2.rs

index bdf1e870753b39648ac209dec8dde4c04c8afa62..bf860d33100b8327e0849ab18918becf70d3d529 100644 (file)
@@ -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"
index 98758dae01fbdf7cbfd16a8f7431a6a33611208a..4342a7464430801dc4d17be0b84c45aac238fcf7 100644 (file)
@@ -2,11 +2,15 @@
 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};
index fe0fe4fd718829d8439cced5eead17b61f9fa5da..e3efa2abeae61233b995b021e52bfe0a77dfaeeb 100644 (file)
@@ -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");
+    }
+
+
 }