3 use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
4 use curve25519_dalek::edwards::CompressedEdwardsY;
5 use curve25519_dalek::edwards::EdwardsPoint as c2_Element;
6 use curve25519_dalek::scalar::Scalar as c2_Scalar;
9 use num_bigint::BigUint;
10 use rand::{CryptoRng, OsRng, Rng};
11 use sha2::{Digest, Sha256};
16 #[derive(Debug, PartialEq, Eq)]
23 #[derive(Debug, PartialEq, Eq)]
31 //type Element: Add<Output=Self::Element>
32 // + Mul<Self::Scalar, Output=Self::Element>;
33 // const element_length: usize; // in unstable, or u8
34 //type ElementBytes : Index<usize, Output=u8>+IndexMut<usize>; // later
36 fn const_m() -> Self::Element;
37 fn const_n() -> Self::Element;
38 fn const_s() -> Self::Element;
39 fn hash_to_scalar(s: &[u8]) -> Self::Scalar;
40 fn random_scalar<T>(cspring: &mut T) -> Self::Scalar
43 fn scalar_neg(s: &Self::Scalar) -> Self::Scalar;
44 fn element_to_bytes(e: &Self::Element) -> Vec<u8>;
45 fn bytes_to_element(b: &[u8]) -> Option<Self::Element>;
46 fn element_length() -> usize;
47 fn basepoint_mult(s: &Self::Scalar) -> Self::Element;
48 fn scalarmult(e: &Self::Element, s: &Self::Scalar) -> Self::Element;
49 fn add(a: &Self::Element, b: &Self::Element) -> Self::Element;
52 #[derive(Debug, PartialEq, Eq)]
53 pub struct Ed25519Group;
55 impl Group for Ed25519Group {
56 type Scalar = c2_Scalar;
57 type Element = c2_Element;
58 //type ElementBytes = Vec<u8>;
59 //type ElementBytes = [u8; 32];
61 type TranscriptHash = Sha256;
63 fn const_m() -> c2_Element {
64 // python -c "import binascii, spake2; b=binascii.hexlify(spake2.ParamsEd25519.M.to_bytes()); print(', '.join(['0x'+b[i:i+2] for i in range(0,len(b),2)]))"
65 // 15cfd18e385952982b6a8f8c7854963b58e34388c8e6dae891db756481a02312
67 0x15, 0xcf, 0xd1, 0x8e, 0x38, 0x59, 0x52, 0x98, 0x2b, 0x6a, 0x8f, 0x8c, 0x78, 0x54,
68 0x96, 0x3b, 0x58, 0xe3, 0x43, 0x88, 0xc8, 0xe6, 0xda, 0xe8, 0x91, 0xdb, 0x75, 0x64,
69 0x81, 0xa0, 0x23, 0x12,
74 fn const_n() -> c2_Element {
75 // python -c "import binascii, spake2; b=binascii.hexlify(spake2.ParamsEd25519.N.to_bytes()); print(', '.join(['0x'+b[i:i+2] for i in range(0,len(b),2)]))"
76 // f04f2e7eb734b2a8f8b472eaf9c3c632576ac64aea650b496a8a20ff00e583c3
78 0xf0, 0x4f, 0x2e, 0x7e, 0xb7, 0x34, 0xb2, 0xa8, 0xf8, 0xb4, 0x72, 0xea, 0xf9, 0xc3,
79 0xc6, 0x32, 0x57, 0x6a, 0xc6, 0x4a, 0xea, 0x65, 0x0b, 0x49, 0x6a, 0x8a, 0x20, 0xff,
80 0x00, 0xe5, 0x83, 0xc3,
85 fn const_s() -> c2_Element {
86 // python -c "import binascii, spake2; b=binascii.hexlify(spake2.ParamsEd25519.S.to_bytes()); print(', '.join(['0x'+b[i:i+2] for i in range(0,len(b),2)]))"
87 // 6f00dae87c1be1a73b5922ef431cd8f57879569c222d22b1cd71e8546ab8e6f1
89 0x6f, 0x00, 0xda, 0xe8, 0x7c, 0x1b, 0xe1, 0xa7, 0x3b, 0x59, 0x22, 0xef, 0x43, 0x1c,
90 0xd8, 0xf5, 0x78, 0x79, 0x56, 0x9c, 0x22, 0x2d, 0x22, 0xb1, 0xcd, 0x71, 0xe8, 0x54,
91 0x6a, 0xb8, 0xe6, 0xf1,
96 fn hash_to_scalar(s: &[u8]) -> c2_Scalar {
97 ed25519_hash_to_scalar(s)
99 fn random_scalar<T>(cspring: &mut T) -> c2_Scalar
103 c2_Scalar::random(cspring)
105 fn scalar_neg(s: &c2_Scalar) -> c2_Scalar {
108 fn element_to_bytes(s: &c2_Element) -> Vec<u8> {
109 s.compress().as_bytes().to_vec()
111 fn element_length() -> usize {
114 fn bytes_to_element(b: &[u8]) -> Option<c2_Element> {
118 //let mut bytes: [u8; 32] =
119 let mut bytes = [0u8; 32];
120 bytes.copy_from_slice(b);
121 let cey = CompressedEdwardsY(bytes);
122 // CompressedEdwardsY::new(b)
126 fn basepoint_mult(s: &c2_Scalar) -> c2_Element {
127 //c2_Element::basepoint_mult(s)
128 ED25519_BASEPOINT_POINT * s
130 fn scalarmult(e: &c2_Element, s: &c2_Scalar) -> c2_Element {
134 fn add(a: &c2_Element, b: &c2_Element) -> c2_Element {
140 fn decimal_to_scalar(d: &[u8]) -> c2_Scalar {
141 let bytes = BigUint::parse_bytes(d, 10).unwrap().to_bytes_le();
142 assert_eq!(bytes.len(), 32);
143 let mut b2 = [0u8; 32];
144 b2.copy_from_slice(&bytes);
145 c2_Scalar::from_bytes_mod_order(b2)
148 fn ed25519_hash_to_scalar(s: &[u8]) -> c2_Scalar {
149 //c2_Scalar::hash_from_bytes::<Sha512>(&s)
151 // h = HKDF(salt=b"", ikm=s, hash=SHA256, info=b"SPAKE2 pw", len=32+16)
155 let okm = Hkdf::<Sha256>::extract(Some(b""), s).expand(b"SPAKE2 pw", 32 + 16);
156 //println!("expanded: {}{}", "................................", okm.iter().to_hex()); // ok
158 let mut reducible = [0u8; 64]; // little-endian
159 for (i, x) in okm.iter().enumerate().take(32 + 16) {
160 reducible[32 + 16 - 1 - i] = *x;
162 //println!("reducible: {}", reducible.iter().to_hex());
163 c2_Scalar::from_bytes_mod_order_wide(&reducible)
164 //let reduced = c2_Scalar::reduce(&reducible);
165 //println!("reduced: {}", reduced.as_bytes().to_hex());
178 assert_eq!(first_msg.len(), 32);
179 assert_eq!(second_msg.len(), 32);
180 // the transcript is fixed-length, made up of 6 32-byte values:
181 // byte 0-31 : sha256(pw)
182 // byte 32-63 : sha256(idA)
183 // byte 64-95 : sha256(idB)
184 // byte 96-127 : X_msg
185 // byte 128-159: Y_msg
186 // byte 160-191: K_bytes
187 let mut transcript = [0u8; 6 * 32];
189 let mut pw_hash = Sha256::new();
190 pw_hash.input(password_vec);
191 transcript[0..32].copy_from_slice(&pw_hash.result());
193 let mut ida_hash = Sha256::new();
194 ida_hash.input(id_a);
195 transcript[32..64].copy_from_slice(&ida_hash.result());
197 let mut idb_hash = Sha256::new();
198 idb_hash.input(id_b);
199 transcript[64..96].copy_from_slice(&idb_hash.result());
201 transcript[96..128].copy_from_slice(first_msg);
202 transcript[128..160].copy_from_slice(second_msg);
203 transcript[160..192].copy_from_slice(key_bytes);
205 //println!("transcript: {:?}", transcript.iter().to_hex());
207 //let mut hash = G::TranscriptHash::default();
208 let mut hash = Sha256::new();
209 hash.input(&transcript);
210 hash.result().to_vec()
213 fn ed25519_hash_symmetric(
220 assert_eq!(msg_u.len(), 32);
221 assert_eq!(msg_v.len(), 32);
222 // # since we don't know which side is which, we must sort the messages
223 // first_msg, second_msg = sorted([msg1, msg2])
224 // transcript = b"".join([sha256(pw).digest(),
225 // sha256(idSymmetric).digest(),
226 // first_msg, second_msg, K_bytes])
228 // the transcript is fixed-length, made up of 5 32-byte values:
229 // byte 0-31 : sha256(pw)
230 // byte 32-63 : sha256(idSymmetric)
231 // byte 64-95 : X_msg
232 // byte 96-127 : Y_msg
233 // byte 128-159: K_bytes
234 let mut transcript = [0u8; 5 * 32];
236 let mut pw_hash = Sha256::new();
237 pw_hash.input(password_vec);
238 transcript[0..32].copy_from_slice(&pw_hash.result());
240 let mut ids_hash = Sha256::new();
241 ids_hash.input(id_s);
242 transcript[32..64].copy_from_slice(&ids_hash.result());
245 transcript[64..96].copy_from_slice(msg_u);
246 transcript[96..128].copy_from_slice(msg_v);
248 transcript[64..96].copy_from_slice(msg_v);
249 transcript[96..128].copy_from_slice(msg_u);
251 transcript[128..160].copy_from_slice(key_bytes);
253 let mut hash = Sha256::new();
254 hash.input(&transcript);
255 hash.result().to_vec()
258 /* "session type pattern" */
260 #[derive(Debug, PartialEq, Eq)]
267 // we implement a custom Debug below, to avoid revealing secrets in a dump
268 #[derive(PartialEq, Eq)]
269 pub struct SPAKE2<G: Group> {
270 //where &G::Scalar: Neg {
272 xy_scalar: G::Scalar,
273 password_vec: Vec<u8>,
278 password_scalar: G::Scalar,
281 impl<G: Group> SPAKE2<G> {
288 xy_scalar: G::Scalar,
289 ) -> (SPAKE2<G>, Vec<u8>) {
290 //let password_scalar: G::Scalar = hash_to_scalar::<G::Scalar>(password);
291 let password_scalar: G::Scalar = G::hash_to_scalar(password);
295 // sym: X = B*x * S*pw
296 let blinding = match side {
297 Side::A => G::const_m(),
298 Side::B => G::const_n(),
299 Side::Symmetric => G::const_s(),
301 let m1: G::Element = G::add(
302 &G::basepoint_mult(&xy_scalar),
303 &G::scalarmult(&blinding, &password_scalar),
305 //let m1: G::Element = &G::basepoint_mult(&x) + &(blinding * &password_scalar);
306 let msg1: Vec<u8> = G::element_to_bytes(&m1);
307 let mut password_vec = Vec::new();
308 password_vec.extend_from_slice(password);
309 let mut id_a_copy = Vec::new();
310 id_a_copy.extend_from_slice(id_a);
311 let mut id_b_copy = Vec::new();
312 id_b_copy.extend_from_slice(id_b);
313 let mut id_s_copy = Vec::new();
314 id_s_copy.extend_from_slice(id_s);
316 let mut msg_and_side = Vec::new();
317 msg_and_side.push(match side {
318 Side::A => 0x41, // 'A'
319 Side::B => 0x42, // 'B'
320 Side::Symmetric => 0x53, // 'S'
322 msg_and_side.extend_from_slice(&msg1);
328 password_vec, // string
333 password_scalar, // scalar
343 xy_scalar: G::Scalar,
344 ) -> (SPAKE2<G>, Vec<u8>) {
345 Self::start_internal(Side::A, password, id_a, id_b, b"", xy_scalar)
352 xy_scalar: G::Scalar,
353 ) -> (SPAKE2<G>, Vec<u8>) {
354 Self::start_internal(Side::B, password, id_a, id_b, b"", xy_scalar)
357 fn start_symmetric_internal(
360 xy_scalar: G::Scalar,
361 ) -> (SPAKE2<G>, Vec<u8>) {
362 Self::start_internal(Side::Symmetric, password, b"", b"", id_s, xy_scalar)
365 pub fn start_a(password: &[u8], id_a: &[u8], id_b: &[u8]) -> (SPAKE2<G>, Vec<u8>) {
366 let mut cspring: OsRng = OsRng::new().unwrap();
367 let xy_scalar: G::Scalar = G::random_scalar(&mut cspring);
368 Self::start_a_internal(password, id_a, id_b, xy_scalar)
371 pub fn start_b(password: &[u8], id_a: &[u8], id_b: &[u8]) -> (SPAKE2<G>, Vec<u8>) {
372 let mut cspring: OsRng = OsRng::new().unwrap();
373 let xy_scalar: G::Scalar = G::random_scalar(&mut cspring);
374 Self::start_b_internal(password, id_a, id_b, xy_scalar)
377 pub fn start_symmetric(password: &[u8], id_s: &[u8]) -> (SPAKE2<G>, Vec<u8>) {
378 let mut cspring: OsRng = OsRng::new().unwrap();
379 let xy_scalar: G::Scalar = G::random_scalar(&mut cspring);
380 Self::start_symmetric_internal(password, id_s, xy_scalar)
383 pub fn finish(self, msg2: &[u8]) -> Result<Vec<u8>, SPAKEErr> {
384 if msg2.len() != 1 + G::element_length() {
385 return Err(SPAKEErr {
386 kind: ErrorType::WrongLength,
389 let msg_side = msg2[0];
392 Side::A => match msg_side {
395 return Err(SPAKEErr {
396 kind: ErrorType::BadSide,
400 Side::B => match msg_side {
403 return Err(SPAKEErr {
404 kind: ErrorType::BadSide,
408 Side::Symmetric => match msg_side {
411 return Err(SPAKEErr {
412 kind: ErrorType::BadSide,
418 let msg2_element = match G::bytes_to_element(&msg2[1..]) {
421 return Err(SPAKEErr {
422 kind: ErrorType::CorruptMessage,
427 // a: K = (Y+N*(-pw))*x
428 // b: K = (X+M*(-pw))*y
429 let unblinding = match self.side {
430 Side::A => G::const_n(),
431 Side::B => G::const_m(),
432 Side::Symmetric => G::const_s(),
434 let tmp1 = G::scalarmult(&unblinding, &G::scalar_neg(&self.password_scalar));
435 let tmp2 = G::add(&msg2_element, &tmp1);
436 let key_element = G::scalarmult(&tmp2, &self.xy_scalar);
437 let key_bytes = G::element_to_bytes(&key_element);
439 // key = H(H(pw) + H(idA) + H(idB) + X + Y + K)
440 //transcript = b"".join([sha256(pw).digest(),
441 // sha256(idA).digest(), sha256(idB).digest(),
442 // X_msg, Y_msg, K_bytes])
443 //key = sha256(transcript).digest()
444 // note that both sides must use the same order
447 Side::A => ed25519_hash_ab(
451 self.msg1.as_slice(),
455 Side::B => ed25519_hash_ab(
460 self.msg1.as_slice(),
463 Side::Symmetric => ed25519_hash_symmetric(
474 fn maybe_utf8(s: &[u8]) -> String {
475 match String::from_utf8(s.to_vec()) {
476 Ok(m) => format!("(s={})", m),
477 Err(_) => format!("(hex={})", hex::encode(s)),
481 impl<G: Group> fmt::Debug for SPAKE2<G> {
482 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
485 "SPAKE2(G=?, side={:?}, idA={}, idB={}, idS={})",
487 maybe_utf8(&self.id_a),
488 maybe_utf8(&self.id_b),
489 maybe_utf8(&self.id_s)
496 /* This compares results against the python compatibility tests:
497 spake2.test.test_compat.SPAKE2.test_asymmetric . The python test passes a
498 deterministic RNG (used only for tests, of course) into the per-Group
499 "random_scalar()" function, which results in some particular scalar.
502 use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
504 use spake2::{Ed25519Group, SPAKE2};
506 // the python tests show the long-integer form of scalars. the rust code
507 // wants an array of bytes (little-endian). Make sure the way we convert
508 // things works correctly.
513 b"2238329342913194256032495932344128051776374960164957527413114840482143558222";
514 let t1_scalar = decimal_to_scalar(t1_decimal);
515 let t1_bytes = t1_scalar.to_bytes();
517 0x4e, 0x5a, 0xb4, 0x34, 0x5d, 0x47, 0x08, 0x84, 0x59, 0x13, 0xb4, 0x64, 0x1b, 0xc2,
518 0x7d, 0x52, 0x52, 0xa5, 0x85, 0x10, 0x1b, 0xcc, 0x42, 0x44, 0xd4, 0x49, 0xf4, 0xa8,
519 0x79, 0xd9, 0xf2, 0x04,
521 assert_eq!(t1_bytes, expected);
522 //println!("t1_scalar is {:?}", t1_scalar);
526 fn test_serialize_basepoint() {
527 // make sure elements are serialized same as the python library
528 let exp = "5866666666666666666666666666666666666666666666666666666666666666";
529 let base_vec = ED25519_BASEPOINT_POINT.compress().as_bytes().to_vec();
530 let base_hex = hex::encode(base_vec);
531 println!("exp: {:?}", exp);
532 println!("got: {:?}", base_hex);
533 assert_eq!(exp, base_hex);
537 fn test_password_to_scalar() {
538 let password = b"password";
539 let expected_pw_scalar = decimal_to_scalar(
540 b"3515301705789368674385125653994241092664323519848410154015274772661223168839",
542 let pw_scalar = Ed25519Group::hash_to_scalar(password);
543 println!("exp: {:?}", hex::encode(expected_pw_scalar.as_bytes()));
544 println!("got: {:?}", hex::encode(pw_scalar.as_bytes()));
545 assert_eq!(&pw_scalar, &expected_pw_scalar);
550 let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(b"password", b"idA", b"idB");
551 assert_eq!(msg1.len(), 1 + 32);
552 let (s2, msg2) = SPAKE2::<Ed25519Group>::start_b(b"password", b"idA", b"idB");
553 assert_eq!(msg2.len(), 1 + 32);
554 let key1 = s1.finish(&msg2).unwrap();
555 let key2 = s2.finish(&msg1).unwrap();
556 assert_eq!(key1.len(), 32);
557 assert_eq!(key2.len(), 32);
559 let (s1, msg1) = SPAKE2::<Ed25519Group>::start_symmetric(b"password", b"idS");
560 assert_eq!(msg1.len(), 1 + 32);
561 let (s2, msg2) = SPAKE2::<Ed25519Group>::start_symmetric(b"password", b"idS");
562 assert_eq!(msg2.len(), 1 + 32);
563 let key1 = s1.finish(&msg2).unwrap();
564 let key2 = s2.finish(&msg1).unwrap();
565 assert_eq!(key1.len(), 32);
566 assert_eq!(key2.len(), 32);
571 let key = ed25519_hash_ab(
575 b"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", // len=32
576 b"YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY",
577 b"KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK",
579 let expected_key = "d59d9ba920f7092565cec747b08d5b2e981d553ac32fde0f25e5b4a4cfca3efd";
580 assert_eq!(hex::encode(key), expected_key);
584 fn test_hash_symmetric() {
585 let key = ed25519_hash_symmetric(
588 b"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
589 b"YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY",
590 b"KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK",
592 let expected_key = "b0b31e4401aae37d91a9a8bf6fbb1298cafc005ff9142e3ffc5b9799fb11128b";
593 assert_eq!(hex::encode(key), expected_key);
597 fn test_asymmetric() {
598 let scalar_a = decimal_to_scalar(
599 b"2611694063369306139794446498317402240796898290761098242657700742213257926693",
601 let scalar_b = decimal_to_scalar(
602 b"7002393159576182977806091886122272758628412261510164356026361256515836884383",
604 let expected_pw_scalar = decimal_to_scalar(
605 b"3515301705789368674385125653994241092664323519848410154015274772661223168839",
608 println!("scalar_a is {}", hex::encode(scalar_a.as_bytes()));
611 SPAKE2::<Ed25519Group>::start_a_internal(b"password", b"idA", b"idB", scalar_a);
612 let expected_msg1 = "416fc960df73c9cf8ed7198b0c9534e2e96a5984bfc5edc023fd24dacf371f2af9";
615 println!("xys1: {:?}", hex::encode(s1.xy_scalar.as_bytes()));
617 println!("pws1: {:?}", hex::encode(s1.password_scalar.as_bytes()));
618 println!("exp : {:?}", hex::encode(expected_pw_scalar.as_bytes()));
620 println!("msg1: {:?}", hex::encode(&msg1));
621 println!("exp : {:?}", expected_msg1);
625 hex::encode(expected_pw_scalar.as_bytes()),
626 hex::encode(s1.password_scalar.as_bytes())
628 assert_eq!(hex::encode(&msg1), expected_msg1);
631 SPAKE2::<Ed25519Group>::start_b_internal(b"password", b"idA", b"idB", scalar_b);
632 assert_eq!(expected_pw_scalar, s2.password_scalar);
635 "42354e97b88406922b1df4bea1d7870f17aed3dba7c720b313edae315b00959309"
638 let key1 = s1.finish(&msg2).unwrap();
639 let key2 = s2.finish(&msg1).unwrap();
640 assert_eq!(key1, key2);
643 "712295de7219c675ddd31942184aa26e0a957cf216bc230d165b215047b520c1"
649 let (s1, _msg1) = SPAKE2::<Ed25519Group>::start_a(b"password", b"idA", b"idB");
650 println!("s1: {:?}", s1);
651 let (s2, _msg1) = SPAKE2::<Ed25519Group>::start_symmetric(b"password", b"idS");
652 println!("s2: {:?}", s2);