-#![allow(dead_code)]
use curve25519_dalek::scalar::Scalar as c2_Scalar;
use curve25519_dalek::curve::ExtendedPoint as c2_Element;
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;
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] =
}
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
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);
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(),
#[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");
+ }
+
+
}