2 #![doc(html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo_small.png")]
3 #![doc = include_str!("../README.md")]
4 #![forbid(unsafe_code)]
5 #![warn(rust_2018_idioms, unused_qualifications)]
9 //! Alice and Bob both initialize their SPAKE2 instances with the same (weak)
10 //! password. They will exchange messages to (hopefully) derive a shared secret
11 //! key. The protocol is symmetric: for each operation that Alice does, Bob will
14 //! However, there are two roles in the SPAKE2 protocol, "A" and "B". The two
15 //! sides must agree ahead of time which one will play which role (the
16 //! messages they generate depend upon which side they play). There are two
17 //! separate constructor functions, `start_a()` and `start_b()`, and a
18 //! complete interaction will use one of each (one `start_a` on one computer,
19 //! and one `start_b` on the other computer).
21 //! Each instance of a SPAKE2 protocol uses a set of shared parameters. These
22 //! include a group, a generator, and a pair of arbitrary group elements.
23 //! This library comes a single pre-generated parameter set, but could be
24 //! extended with others.
26 //! You start by calling `start_a()` (or `_b)` with the password and identity
27 //! strings for both sides. This gives you back a state object and the first
28 //! message, which you must send to your partner. Once you receive the
29 //! corresponding inbound message, you pass it into the state object
30 //! (consuming both in the process) by calling `s.finish()`, and you get back
31 //! the shared key as a bytestring.
33 //! The password and identity strings must each be wrapped in a "newtype",
34 //! which is a simple `struct` that protects against swapping the different
35 //! types of bytestrings.
37 //! Thus a client-side program start with:
40 //! use spake2::{Ed25519Group, Identity, Password, SPAKE2};
41 //! # fn send(msg: &[u8]) {}
42 //! let (s1, outbound_msg) = SPAKE2::<Ed25519Group>::start_a(
43 //! &Password::new(b"password"),
44 //! &Identity::new(b"client id string"),
45 //! &Identity::new(b"server id string"));
46 //! send(&outbound_msg);
48 //! # fn receive() -> Vec<u8> { let (s2, i2) = SPAKE2::<Ed25519Group>::start_b(&Password::new(b"password"), &Identity::new(b"client id string"), &Identity::new(b"server id string")); i2 }
49 //! let inbound_msg = receive();
50 //! let key1 = s1.finish(&inbound_msg).unwrap();
53 //! while the server-side might do:
56 //! # fn send(msg: &[u8]) {}
57 //! use spake2::{Ed25519Group, Identity, Password, SPAKE2};
58 //! let (s1, outbound_msg) = SPAKE2::<Ed25519Group>::start_b(
59 //! &Password::new(b"password"),
60 //! &Identity::new(b"client id string"),
61 //! &Identity::new(b"server id string"));
62 //! send(&outbound_msg);
64 //! # fn receive() -> Vec<u8> { let (s2, i2) = SPAKE2::<Ed25519Group>::start_a(&Password::new(b"password"), &Identity::new(b"client id string"), &Identity::new(b"server id string")); i2 }
65 //! let inbound_msg = receive();
66 //! let key2 = s1.finish(&inbound_msg).unwrap();
69 //! If both sides used the same password, and there is no man-in-the-middle,
70 //! then `key1` and `key2` will be identical. If not, the two sides will get
71 //! different keys. When one side encrypts with `key1`, and the other side
72 //! attempts to decrypt with `key2`, they'll get nothing but garbled noise.
74 //! The shared key can be used as an HMAC key to provide data integrity on
75 //! subsequent messages, or as an authenticated-encryption key (e.g.
76 //! nacl.secretbox). It can also be fed into [HKDF][1] to derive other
77 //! session keys as necessary.
79 //! The `SPAKE2` instances, and the messages they create, are single-use. Create
80 //! a new one for each new session. `finish` consumes the instance.
84 //! A single SPAKE2 instance must be used asymmetrically: the two sides must
85 //! somehow decide (ahead of time) which role they will each play. The
86 //! implementation includes the side identifier in the exchanged message to
87 //! guard against a `start_a` talking to another `start_a`. Typically a
88 //! "client" will take on the `A` role, and the "server" will be `B`.
90 //! This is a nuisance for more egalitarian protocols, where there's no clear
91 //! way to assign these roles ahead of time. In this case, use
92 //! `start_symmetric()` on both sides. This uses a different set of
93 //! parameters (so it is not interoperable with `start_A` or `start_b`), but
94 //! should otherwise behave the same way. The symmetric mode uses only one
95 //! identity string, not two.
100 //! # fn send(msg: &[u8]) {}
101 //! use spake2::{Ed25519Group, Identity, Password, SPAKE2};
102 //! let (s1, outbound_msg) = SPAKE2::<Ed25519Group>::start_symmetric(
103 //! &Password::new(b"password"),
104 //! &Identity::new(b"shared id string"));
105 //! send(&outbound_msg);
107 //! # fn receive() -> Vec<u8> { let (s2, i2) = SPAKE2::<Ed25519Group>::start_symmetric(&Password::new(b"password"), &Identity::new(b"shared id string")); i2 }
108 //! let inbound_msg = receive();
109 //! let key1 = s1.finish(&inbound_msg).unwrap();
112 //! Dave does exactly the same:
115 //! # fn send(msg: &[u8]) {}
116 //! use spake2::{Ed25519Group, Identity, Password, SPAKE2};
117 //! let (s1, outbound_msg) = SPAKE2::<Ed25519Group>::start_symmetric(
118 //! &Password::new(b"password"),
119 //! &Identity::new(b"shared id string"));
120 //! send(&outbound_msg);
122 //! # fn receive() -> Vec<u8> { let (s2, i2) = SPAKE2::<Ed25519Group>::start_symmetric(&Password::new(b"password"), &Identity::new(b"shared id string")); i2 }
123 //! let inbound_msg = receive();
124 //! let key1 = s1.finish(&inbound_msg).unwrap();
127 //! # Identifier Strings
129 //! The SPAKE2 protocol includes a pair of "identity strings" `idA` and `idB`
130 //! that are included in the final key-derivation hash. This binds the key to a
131 //! single pair of parties, or for some specific purpose.
133 //! For example, when user "alice" logs into "example.com", both sides should set
134 //! `idA = b"alice"` and `idB = b"example.com"`. This prevents an attacker from
135 //! substituting messages from unrelated login sessions (other users on the same
136 //! server, or other servers for the same user).
138 //! This also makes sure the session is established with the correct service. If
139 //! Alice has one password for "example.com" but uses it for both login and
140 //! file-transfer services, `idB` should be different for the two services.
141 //! Otherwise if Alice is simultaneously connecting to both services, and
142 //! attacker could rearrange the messages and cause her login client to connect
143 //! to the file-transfer server, and vice versa.
145 //! `idA` and `idB` must be bytestrings (slices of `<u8>`).
147 //! `start_symmetric` uses a single `idSymmetric=` string, instead of `idA`
148 //! and `idB`. Both sides must provide the same `idSymmetric=`, or leave it
153 //! Sometimes, you can't hold the SPAKE2 instance in memory for the whole
154 //! negotiation: perhaps all your program state is stored in a database, and
155 //! nothing lives in RAM for more than a few moments.
157 //! Unfortunately the Rust implementation does not yet provide serialization
158 //! of the state object. A future version should correct this.
162 //! This library is probably not constant-time, and does not protect against
163 //! timing attacks. Do not allow attackers to measure how long it takes you
164 //! to create or respond to a message. This matters somewhat less for pairing
165 //! protocols, because their passwords are single-use randomly-generated
166 //! keys, so an attacker has much less to work with.
168 //! This library depends upon a strong source of random numbers. Do not use it on
169 //! a system where os.urandom() is weak.
173 //! To run the built-in speed tests, just run `cargo bench`.
175 //! SPAKE2 consists of two phases, separated by a single message exchange.
176 //! The time these phases take is split roughly 50/50. On my 2.8GHz Core-i7
177 //! (i7-7600U) cpu, the built-in Ed25519Group parameters take about 112
178 //! microseconds for each phase, and the message exchanged is 33 bytes long.
182 //! Run `cargo test` to run the built-in test suite.
186 //! The protocol was described as "PAKE2" in ["cryptobook"] [2] from Dan Boneh
187 //! and Victor Shoup. This is a form of "SPAKE2", defined by Abdalla and
188 //! Pointcheval at [RSA 2005] [3]. Additional recommendations for groups and
189 //! distinguished elements were published in [Ladd's IETF draft] [4].
191 //! The Ed25519 implementation uses code adapted from Daniel Bernstein (djb),
192 //! Matthew Dempsky, Daniel Holth, Ron Garret, with further optimizations by
193 //! Brian Warner[5]. The "arbitrary element" computation, which must be the same
194 //! for both participants, is from python-pure25519 version 0.5.
196 //! The Boneh/Shoup chapter that defines PAKE2 also defines an augmented variant
197 //! named "PAKE2+", which changes one side (typically a server) to record a
198 //! derivative of the password instead of the actual password. In PAKE2+, a
199 //! server compromise does not immediately give access to the passwords: instead,
200 //! the attacker must perform an offline dictionary attack against the stolen
201 //! data before they can learn the passwords. PAKE2+ support is planned, but not
204 //! The security of the symmetric case was proved by Kobara/Imai[6] in 2003, and
205 //! uses different (slightly weaker?) reductions than that of the asymmetric
206 //! form. See also Mike Hamburg's analysis[7] from 2015.
208 //! Brian Warner first wrote the Python version in July 2010. He wrote this
209 //! Rust version in in May 2017.
213 //! [1]: https://tools.ietf.org/html/rfc5869 "HKDF"
214 //! [2]: http://crypto.stanford.edu/~dabo/cryptobook/ "cryptobook"
215 //! [3]: http://www.di.ens.fr/~pointche/Documents/Papers/2005_rsa.pdf "RSA 2005"
216 //! [4]: https://tools.ietf.org/html/draft-ladd-spake2-01 "Ladd's IETF draft"
217 //! [5]: https://github.com/warner/python-pure25519
218 //! [6]: http://eprint.iacr.org/2003/038.pdf "Pretty-Simple Password-Authenticated Key-Exchange Under Standard Assumptions"
219 //! [7]: https://moderncrypto.org/mail-archive/curves/2015/000419.html "PAKE questions"
221 #[allow(unused_imports)]
225 #[cfg(feature = "std")]
226 #[cfg_attr(test, macro_use)]
230 use core::{fmt, ops::Deref, str};
231 use curve25519_dalek::{
232 constants::ED25519_BASEPOINT_POINT,
233 edwards::{CompressedEdwardsY, EdwardsPoint as c2_Element},
234 scalar::Scalar as c2_Scalar,
237 use rand_core::{CryptoRng, OsRng, RngCore};
238 use sha2::{Digest, Sha256};
240 /* "newtype pattern": it's a Vec<u8>, but only used for a specific argument
241 * type, to distinguish between ones that are meant as passwords, and ones
242 * that are meant as identity strings */
244 #[derive(PartialEq, Eq, Clone)]
245 pub struct Password(Vec<u8>);
247 pub fn new(p: &[u8]) -> Password {
251 impl Deref for Password {
252 type Target = Vec<u8>;
253 fn deref(&self) -> &Vec<u8> {
258 #[derive(PartialEq, Eq, Clone)]
259 pub struct Identity(Vec<u8>);
260 impl Deref for Identity {
261 type Target = Vec<u8>;
262 fn deref(&self) -> &Vec<u8> {
267 pub fn new(p: &[u8]) -> Identity {
272 #[derive(Debug, PartialEq, Eq)]
279 #[derive(Debug, PartialEq, Eq)]
280 pub struct SPAKEErr {
287 //type Element: Add<Output=Self::Element>
288 // + Mul<Self::Scalar, Output=Self::Element>;
289 // const element_length: usize; // in unstable, or u8
290 //type ElementBytes : Index<usize, Output=u8>+IndexMut<usize>; // later
292 fn name() -> &'static str;
293 fn const_m() -> Self::Element;
294 fn const_n() -> Self::Element;
295 fn const_s() -> Self::Element;
296 fn hash_to_scalar(s: &[u8]) -> Self::Scalar;
297 fn random_scalar<T>(cspring: &mut T) -> Self::Scalar
299 T: RngCore + CryptoRng;
300 fn scalar_neg(s: &Self::Scalar) -> Self::Scalar;
301 fn element_to_bytes(e: &Self::Element) -> Vec<u8>;
302 fn bytes_to_element(b: &[u8]) -> Option<Self::Element>;
303 fn element_length() -> usize;
304 fn basepoint_mult(s: &Self::Scalar) -> Self::Element;
305 fn scalarmult(e: &Self::Element, s: &Self::Scalar) -> Self::Element;
306 fn add(a: &Self::Element, b: &Self::Element) -> Self::Element;
309 #[derive(Debug, PartialEq, Eq)]
310 pub struct Ed25519Group;
312 impl Group for Ed25519Group {
313 type Scalar = c2_Scalar;
314 type Element = c2_Element;
315 //type ElementBytes = Vec<u8>;
316 //type ElementBytes = [u8; 32];
318 type TranscriptHash = Sha256;
320 fn name() -> &'static str {
324 fn const_m() -> c2_Element {
325 // 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)]))"
326 // 15cfd18e385952982b6a8f8c7854963b58e34388c8e6dae891db756481a02312
328 0x15, 0xcf, 0xd1, 0x8e, 0x38, 0x59, 0x52, 0x98, 0x2b, 0x6a, 0x8f, 0x8c, 0x78, 0x54,
329 0x96, 0x3b, 0x58, 0xe3, 0x43, 0x88, 0xc8, 0xe6, 0xda, 0xe8, 0x91, 0xdb, 0x75, 0x64,
330 0x81, 0xa0, 0x23, 0x12,
336 fn const_n() -> c2_Element {
337 // 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)]))"
338 // f04f2e7eb734b2a8f8b472eaf9c3c632576ac64aea650b496a8a20ff00e583c3
340 0xf0, 0x4f, 0x2e, 0x7e, 0xb7, 0x34, 0xb2, 0xa8, 0xf8, 0xb4, 0x72, 0xea, 0xf9, 0xc3,
341 0xc6, 0x32, 0x57, 0x6a, 0xc6, 0x4a, 0xea, 0x65, 0x0b, 0x49, 0x6a, 0x8a, 0x20, 0xff,
342 0x00, 0xe5, 0x83, 0xc3,
348 fn const_s() -> c2_Element {
349 // 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)]))"
350 // 6f00dae87c1be1a73b5922ef431cd8f57879569c222d22b1cd71e8546ab8e6f1
352 0x6f, 0x00, 0xda, 0xe8, 0x7c, 0x1b, 0xe1, 0xa7, 0x3b, 0x59, 0x22, 0xef, 0x43, 0x1c,
353 0xd8, 0xf5, 0x78, 0x79, 0x56, 0x9c, 0x22, 0x2d, 0x22, 0xb1, 0xcd, 0x71, 0xe8, 0x54,
354 0x6a, 0xb8, 0xe6, 0xf1,
360 fn hash_to_scalar(s: &[u8]) -> c2_Scalar {
361 ed25519_hash_to_scalar(s)
363 fn random_scalar<T>(cspring: &mut T) -> c2_Scalar
365 T: RngCore + CryptoRng,
367 c2_Scalar::random(cspring)
369 fn scalar_neg(s: &c2_Scalar) -> c2_Scalar {
372 fn element_to_bytes(s: &c2_Element) -> Vec<u8> {
373 s.compress().as_bytes().to_vec()
375 fn element_length() -> usize {
378 fn bytes_to_element(b: &[u8]) -> Option<c2_Element> {
382 //let mut bytes: [u8; 32] =
383 let mut bytes = [0u8; 32];
384 bytes.copy_from_slice(b);
385 let cey = CompressedEdwardsY(bytes);
386 // CompressedEdwardsY::new(b)
390 fn basepoint_mult(s: &c2_Scalar) -> c2_Element {
391 //c2_Element::basepoint_mult(s)
392 ED25519_BASEPOINT_POINT * s
394 fn scalarmult(e: &c2_Element, s: &c2_Scalar) -> c2_Element {
398 fn add(a: &c2_Element, b: &c2_Element) -> c2_Element {
404 fn ed25519_hash_to_scalar(s: &[u8]) -> c2_Scalar {
405 //c2_Scalar::hash_from_bytes::<Sha512>(&s)
407 // h = HKDF(salt=b"", ikm=s, hash=SHA256, info=b"SPAKE2 pw", len=32+16)
411 let mut okm = [0u8; 32 + 16];
412 Hkdf::<Sha256>::new(Some(b""), s)
413 .expand(b"SPAKE2 pw", &mut okm)
415 //println!("expanded: {}{}", "................................", okm.iter().to_hex()); // ok
417 let mut reducible = [0u8; 64]; // little-endian
418 for (i, x) in okm.iter().enumerate().take(32 + 16) {
419 reducible[32 + 16 - 1 - i] = *x;
421 //println!("reducible: {}", reducible.iter().to_hex());
422 c2_Scalar::from_bytes_mod_order_wide(&reducible)
423 //let reduced = c2_Scalar::reduce(&reducible);
424 //println!("reduced: {}", reduced.as_bytes().to_hex());
437 assert_eq!(first_msg.len(), 32);
438 assert_eq!(second_msg.len(), 32);
439 // the transcript is fixed-length, made up of 6 32-byte values:
440 // byte 0-31 : sha256(pw)
441 // byte 32-63 : sha256(idA)
442 // byte 64-95 : sha256(idB)
443 // byte 96-127 : X_msg
444 // byte 128-159: Y_msg
445 // byte 160-191: K_bytes
446 let mut transcript = [0u8; 6 * 32];
448 let mut pw_hash = Sha256::new();
449 pw_hash.update(password_vec);
450 transcript[0..32].copy_from_slice(&pw_hash.finalize());
452 let mut ida_hash = Sha256::new();
453 ida_hash.update(id_a);
454 transcript[32..64].copy_from_slice(&ida_hash.finalize());
456 let mut idb_hash = Sha256::new();
457 idb_hash.update(id_b);
458 transcript[64..96].copy_from_slice(&idb_hash.finalize());
460 transcript[96..128].copy_from_slice(first_msg);
461 transcript[128..160].copy_from_slice(second_msg);
462 transcript[160..192].copy_from_slice(key_bytes);
464 //println!("transcript: {:?}", transcript.iter().to_hex());
466 //let mut hash = G::TranscriptHash::default();
467 let mut hash = Sha256::new();
468 hash.update(transcript.to_vec());
469 hash.finalize().to_vec()
472 fn ed25519_hash_symmetric(
479 assert_eq!(msg_u.len(), 32);
480 assert_eq!(msg_v.len(), 32);
481 // # since we don't know which side is which, we must sort the messages
482 // first_msg, second_msg = sorted([msg1, msg2])
483 // transcript = b"".join([sha256(pw).digest(),
484 // sha256(idSymmetric).digest(),
485 // first_msg, second_msg, K_bytes])
487 // the transcript is fixed-length, made up of 5 32-byte values:
488 // byte 0-31 : sha256(pw)
489 // byte 32-63 : sha256(idSymmetric)
490 // byte 64-95 : X_msg
491 // byte 96-127 : Y_msg
492 // byte 128-159: K_bytes
493 let mut transcript = [0u8; 5 * 32];
495 let mut pw_hash = Sha256::new();
496 pw_hash.update(password_vec);
497 transcript[0..32].copy_from_slice(&pw_hash.finalize());
499 let mut ids_hash = Sha256::new();
500 ids_hash.update(id_s);
501 transcript[32..64].copy_from_slice(&ids_hash.finalize());
504 transcript[64..96].copy_from_slice(msg_u);
505 transcript[96..128].copy_from_slice(msg_v);
507 transcript[64..96].copy_from_slice(msg_v);
508 transcript[96..128].copy_from_slice(msg_u);
510 transcript[128..160].copy_from_slice(key_bytes);
512 let mut hash = Sha256::new();
513 hash.update(transcript.to_vec());
514 hash.finalize().to_vec()
517 /* "session type pattern" */
519 #[derive(Debug, PartialEq, Eq)]
526 // we implement a custom Debug below, to avoid revealing secrets in a dump
527 #[derive(PartialEq, Eq)]
528 pub struct SPAKE2<G: Group> {
529 //where &G::Scalar: Neg {
531 xy_scalar: G::Scalar,
532 password_vec: Vec<u8>,
537 password_scalar: G::Scalar,
540 impl<G: Group> SPAKE2<G> {
547 xy_scalar: G::Scalar,
548 ) -> (SPAKE2<G>, Vec<u8>) {
549 //let password_scalar: G::Scalar = hash_to_scalar::<G::Scalar>(password);
550 let password_scalar: G::Scalar = G::hash_to_scalar(password);
554 // sym: X = B*x * S*pw
555 let blinding = match side {
556 Side::A => G::const_m(),
557 Side::B => G::const_n(),
558 Side::Symmetric => G::const_s(),
560 let m1: G::Element = G::add(
561 &G::basepoint_mult(&xy_scalar),
562 &G::scalarmult(&blinding, &password_scalar),
564 //let m1: G::Element = &G::basepoint_mult(&x) + &(blinding * &password_scalar);
565 let msg1: Vec<u8> = G::element_to_bytes(&m1);
566 let mut password_vec = Vec::new();
567 password_vec.extend_from_slice(password);
568 let mut id_a_copy = Vec::new();
569 id_a_copy.extend_from_slice(id_a);
570 let mut id_b_copy = Vec::new();
571 id_b_copy.extend_from_slice(id_b);
572 let mut id_s_copy = Vec::new();
573 id_s_copy.extend_from_slice(id_s);
575 let mut msg_and_side = vec![match side {
576 Side::A => 0x41, // 'A'
577 Side::B => 0x42, // 'B'
578 Side::Symmetric => 0x53, // 'S'
580 msg_and_side.extend_from_slice(&msg1);
586 password_vec, // string
591 password_scalar, // scalar
601 xy_scalar: G::Scalar,
602 ) -> (SPAKE2<G>, Vec<u8>) {
603 Self::start_internal(
617 xy_scalar: G::Scalar,
618 ) -> (SPAKE2<G>, Vec<u8>) {
619 Self::start_internal(
629 fn start_symmetric_internal(
632 xy_scalar: G::Scalar,
633 ) -> (SPAKE2<G>, Vec<u8>) {
634 Self::start_internal(
644 pub fn start_a(password: &Password, id_a: &Identity, id_b: &Identity) -> (SPAKE2<G>, Vec<u8>) {
645 let mut cspring = OsRng;
646 let xy_scalar: G::Scalar = G::random_scalar(&mut cspring);
647 Self::start_a_internal(password, id_a, id_b, xy_scalar)
650 pub fn start_b(password: &Password, id_a: &Identity, id_b: &Identity) -> (SPAKE2<G>, Vec<u8>) {
651 let mut cspring = OsRng;
652 let xy_scalar: G::Scalar = G::random_scalar(&mut cspring);
653 Self::start_b_internal(password, id_a, id_b, xy_scalar)
656 pub fn start_symmetric(password: &Password, id_s: &Identity) -> (SPAKE2<G>, Vec<u8>) {
657 let mut cspring = OsRng;
658 let xy_scalar: G::Scalar = G::random_scalar(&mut cspring);
659 Self::start_symmetric_internal(password, id_s, xy_scalar)
662 pub fn finish(self, msg2: &[u8]) -> Result<Vec<u8>, SPAKEErr> {
663 if msg2.len() != 1 + G::element_length() {
664 return Err(SPAKEErr {
665 kind: ErrorType::WrongLength,
668 let msg_side = msg2[0];
671 Side::A => match msg_side {
674 return Err(SPAKEErr {
675 kind: ErrorType::BadSide,
679 Side::B => match msg_side {
682 return Err(SPAKEErr {
683 kind: ErrorType::BadSide,
687 Side::Symmetric => match msg_side {
690 return Err(SPAKEErr {
691 kind: ErrorType::BadSide,
697 let msg2_element = match G::bytes_to_element(&msg2[1..]) {
700 return Err(SPAKEErr {
701 kind: ErrorType::CorruptMessage,
706 // a: K = (Y+N*(-pw))*x
707 // b: K = (X+M*(-pw))*y
708 let unblinding = match self.side {
709 Side::A => G::const_n(),
710 Side::B => G::const_m(),
711 Side::Symmetric => G::const_s(),
713 let tmp1 = G::scalarmult(&unblinding, &G::scalar_neg(&self.password_scalar));
714 let tmp2 = G::add(&msg2_element, &tmp1);
715 let key_element = G::scalarmult(&tmp2, &self.xy_scalar);
716 let key_bytes = G::element_to_bytes(&key_element);
718 // key = H(H(pw) + H(idA) + H(idB) + X + Y + K)
719 //transcript = b"".join([sha256(pw).digest(),
720 // sha256(idA).digest(), sha256(idB).digest(),
721 // X_msg, Y_msg, K_bytes])
722 //key = sha256(transcript).digest()
723 // note that both sides must use the same order
726 Side::A => ed25519_hash_ab(
730 self.msg1.as_slice(),
734 Side::B => ed25519_hash_ab(
739 self.msg1.as_slice(),
742 Side::Symmetric => ed25519_hash_symmetric(
753 impl<G: Group> fmt::Debug for SPAKE2<G> {
754 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
755 fmt.debug_struct("SPAKE2")
756 .field("group", &G::name())
757 .field("side", &self.side)
758 .field("idA", &MaybeUtf8(&self.id_a))
759 .field("idB", &MaybeUtf8(&self.id_b))
760 .field("idS", &MaybeUtf8(&self.id_s))
765 struct MaybeUtf8<'a>(&'a [u8]);
767 impl fmt::Debug for MaybeUtf8<'_> {
768 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
769 if let Ok(s) = str::from_utf8(self.0) {
770 write!(fmt, "(s={})", s)
772 write!(fmt, "(hex=")?;
775 write!(fmt, "{:x}", byte)?;