1 #![forbid(unsafe_code)]
2 #![warn(rust_2018_idioms, unused_qualifications)]
3 #![doc(html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo_small.png")]
4 #![doc = include_str!("../README.md")]
8 //! Alice and Bob both initialize their SPAKE2 instances with the same (weak)
9 //! password. They will exchange messages to (hopefully) derive a shared secret
10 //! key. The protocol is symmetric: for each operation that Alice does, Bob will
13 //! However, there are two roles in the SPAKE2 protocol, "A" and "B". The two
14 //! sides must agree ahead of time which one will play which role (the
15 //! messages they generate depend upon which side they play). There are two
16 //! separate constructor functions, `start_a()` and `start_b()`, and a
17 //! complete interaction will use one of each (one `start_a` on one computer,
18 //! and one `start_b` on the other computer).
20 //! Each instance of a SPAKE2 protocol uses a set of shared parameters. These
21 //! include a group, a generator, and a pair of arbitrary group elements.
22 //! This library comes a single pre-generated parameter set, but could be
23 //! extended with others.
25 //! You start by calling `start_a()` (or `_b)` with the password and identity
26 //! strings for both sides. This gives you back a state object and the first
27 //! message, which you must send to your partner. Once you receive the
28 //! corresponding inbound message, you pass it into the state object
29 //! (consuming both in the process) by calling `s.finish()`, and you get back
30 //! the shared key as a bytestring.
32 //! The password and identity strings must each be wrapped in a "newtype",
33 //! which is a simple `struct` that protects against swapping the different
34 //! types of bytestrings.
36 //! Thus a client-side program start with:
39 //! use spake2::{Ed25519Group, Identity, Password, SPAKE2};
40 //! # fn send(msg: &[u8]) {}
41 //! let (s1, outbound_msg) = SPAKE2::<Ed25519Group>::start_a(
42 //! &Password::new(b"password"),
43 //! &Identity::new(b"client id string"),
44 //! &Identity::new(b"server id string"));
45 //! send(&outbound_msg);
47 //! # 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 }
48 //! let inbound_msg = receive();
49 //! let key1 = s1.finish(&inbound_msg).unwrap();
52 //! while the server-side might do:
55 //! # fn send(msg: &[u8]) {}
56 //! use spake2::{Ed25519Group, Identity, Password, SPAKE2};
57 //! let (s1, outbound_msg) = SPAKE2::<Ed25519Group>::start_b(
58 //! &Password::new(b"password"),
59 //! &Identity::new(b"client id string"),
60 //! &Identity::new(b"server id string"));
61 //! send(&outbound_msg);
63 //! # 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 }
64 //! let inbound_msg = receive();
65 //! let key2 = s1.finish(&inbound_msg).unwrap();
68 //! If both sides used the same password, and there is no man-in-the-middle,
69 //! then `key1` and `key2` will be identical. If not, the two sides will get
70 //! different keys. When one side encrypts with `key1`, and the other side
71 //! attempts to decrypt with `key2`, they'll get nothing but garbled noise.
73 //! The shared key can be used as an HMAC key to provide data integrity on
74 //! subsequent messages, or as an authenticated-encryption key (e.g.
75 //! nacl.secretbox). It can also be fed into [HKDF][1] to derive other
76 //! session keys as necessary.
78 //! The `SPAKE2` instances, and the messages they create, are single-use. Create
79 //! a new one for each new session. `finish` consumes the instance.
83 //! A single SPAKE2 instance must be used asymmetrically: the two sides must
84 //! somehow decide (ahead of time) which role they will each play. The
85 //! implementation includes the side identifier in the exchanged message to
86 //! guard against a `start_a` talking to another `start_a`. Typically a
87 //! "client" will take on the `A` role, and the "server" will be `B`.
89 //! This is a nuisance for more egalitarian protocols, where there's no clear
90 //! way to assign these roles ahead of time. In this case, use
91 //! `start_symmetric()` on both sides. This uses a different set of
92 //! parameters (so it is not interoperable with `start_A` or `start_b`), but
93 //! should otherwise behave the same way. The symmetric mode uses only one
94 //! identity string, not two.
99 //! # fn send(msg: &[u8]) {}
100 //! use spake2::{Ed25519Group, Identity, Password, SPAKE2};
101 //! let (s1, outbound_msg) = SPAKE2::<Ed25519Group>::start_symmetric(
102 //! &Password::new(b"password"),
103 //! &Identity::new(b"shared id string"));
104 //! send(&outbound_msg);
106 //! # fn receive() -> Vec<u8> { let (s2, i2) = SPAKE2::<Ed25519Group>::start_symmetric(&Password::new(b"password"), &Identity::new(b"shared id string")); i2 }
107 //! let inbound_msg = receive();
108 //! let key1 = s1.finish(&inbound_msg).unwrap();
111 //! Dave does exactly the same:
114 //! # fn send(msg: &[u8]) {}
115 //! use spake2::{Ed25519Group, Identity, Password, SPAKE2};
116 //! let (s1, outbound_msg) = SPAKE2::<Ed25519Group>::start_symmetric(
117 //! &Password::new(b"password"),
118 //! &Identity::new(b"shared id string"));
119 //! send(&outbound_msg);
121 //! # fn receive() -> Vec<u8> { let (s2, i2) = SPAKE2::<Ed25519Group>::start_symmetric(&Password::new(b"password"), &Identity::new(b"shared id string")); i2 }
122 //! let inbound_msg = receive();
123 //! let key1 = s1.finish(&inbound_msg).unwrap();
126 //! # Identifier Strings
128 //! The SPAKE2 protocol includes a pair of "identity strings" `idA` and `idB`
129 //! that are included in the final key-derivation hash. This binds the key to a
130 //! single pair of parties, or for some specific purpose.
132 //! For example, when user "alice" logs into "example.com", both sides should set
133 //! `idA = b"alice"` and `idB = b"example.com"`. This prevents an attacker from
134 //! substituting messages from unrelated login sessions (other users on the same
135 //! server, or other servers for the same user).
137 //! This also makes sure the session is established with the correct service. If
138 //! Alice has one password for "example.com" but uses it for both login and
139 //! file-transfer services, `idB` should be different for the two services.
140 //! Otherwise if Alice is simultaneously connecting to both services, and
141 //! attacker could rearrange the messages and cause her login client to connect
142 //! to the file-transfer server, and vice versa.
144 //! `idA` and `idB` must be bytestrings (slices of `<u8>`).
146 //! `start_symmetric` uses a single `idSymmetric=` string, instead of `idA`
147 //! and `idB`. Both sides must provide the same `idSymmetric=`, or leave it
152 //! Sometimes, you can't hold the SPAKE2 instance in memory for the whole
153 //! negotiation: perhaps all your program state is stored in a database, and
154 //! nothing lives in RAM for more than a few moments.
156 //! Unfortunately the Rust implementation does not yet provide serialization
157 //! of the state object. A future version should correct this.
161 //! This library is probably not constant-time, and does not protect against
162 //! timing attacks. Do not allow attackers to measure how long it takes you
163 //! to create or respond to a message. This matters somewhat less for pairing
164 //! protocols, because their passwords are single-use randomly-generated
165 //! keys, so an attacker has much less to work with.
167 //! This library depends upon a strong source of random numbers. Do not use it on
168 //! a system where os.urandom() is weak.
172 //! To run the built-in speed tests, just run `cargo bench`.
174 //! SPAKE2 consists of two phases, separated by a single message exchange.
175 //! The time these phases take is split roughly 50/50. On my 2.8GHz Core-i7
176 //! (i7-7600U) cpu, the built-in Ed25519Group parameters take about 112
177 //! microseconds for each phase, and the message exchanged is 33 bytes long.
181 //! Run `cargo test` to run the built-in test suite.
185 //! The protocol was described as "PAKE2" in ["cryptobook"] [2] from Dan Boneh
186 //! and Victor Shoup. This is a form of "SPAKE2", defined by Abdalla and
187 //! Pointcheval at [RSA 2005] [3]. Additional recommendations for groups and
188 //! distinguished elements were published in [Ladd's IETF draft] [4].
190 //! The Ed25519 implementation uses code adapted from Daniel Bernstein (djb),
191 //! Matthew Dempsky, Daniel Holth, Ron Garret, with further optimizations by
192 //! Brian Warner[5]. The "arbitrary element" computation, which must be the same
193 //! for both participants, is from python-pure25519 version 0.5.
195 //! The Boneh/Shoup chapter that defines PAKE2 also defines an augmented variant
196 //! named "PAKE2+", which changes one side (typically a server) to record a
197 //! derivative of the password instead of the actual password. In PAKE2+, a
198 //! server compromise does not immediately give access to the passwords: instead,
199 //! the attacker must perform an offline dictionary attack against the stolen
200 //! data before they can learn the passwords. PAKE2+ support is planned, but not
203 //! The security of the symmetric case was proved by Kobara/Imai[6] in 2003, and
204 //! uses different (slightly weaker?) reductions than that of the asymmetric
205 //! form. See also Mike Hamburg's analysis[7] from 2015.
207 //! Brian Warner first wrote the Python version in July 2010. He wrote this
208 //! Rust version in in May 2017.
212 //! [1]: https://tools.ietf.org/html/rfc5869 "HKDF"
213 //! [2]: http://crypto.stanford.edu/~dabo/cryptobook/ "cryptobook"
214 //! [3]: http://www.di.ens.fr/~pointche/Documents/Papers/2005_rsa.pdf "RSA 2005"
215 //! [4]: https://tools.ietf.org/html/draft-ladd-spake2-01 "Ladd's IETF draft"
216 //! [5]: https://github.com/warner/python-pure25519
217 //! [6]: http://eprint.iacr.org/2003/038.pdf "Pretty-Simple Password-Authenticated Key-Exchange Under Standard Assumptions"
218 //! [7]: https://moderncrypto.org/mail-archive/curves/2015/000419.html "PAKE questions"
220 use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
221 use curve25519_dalek::edwards::CompressedEdwardsY;
222 use curve25519_dalek::edwards::EdwardsPoint as c2_Element;
223 use curve25519_dalek::scalar::Scalar as c2_Scalar;
225 use rand::{rngs::OsRng, CryptoRng, Rng};
226 use sha2::{Digest, Sha256};
230 /* "newtype pattern": it's a Vec<u8>, but only used for a specific argument
231 * type, to distinguish between ones that are meant as passwords, and ones
232 * that are meant as identity strings */
234 #[derive(PartialEq, Eq, Clone)]
235 pub struct Password(Vec<u8>);
237 pub fn new(p: &[u8]) -> Password {
241 impl Deref for Password {
242 type Target = Vec<u8>;
243 fn deref(&self) -> &Vec<u8> {
248 #[derive(PartialEq, Eq, Clone)]
249 pub struct Identity(Vec<u8>);
250 impl Deref for Identity {
251 type Target = Vec<u8>;
252 fn deref(&self) -> &Vec<u8> {
257 pub fn new(p: &[u8]) -> Identity {
262 #[derive(Debug, PartialEq, Eq)]
269 #[derive(Debug, PartialEq, Eq)]
270 pub struct SPAKEErr {
277 //type Element: Add<Output=Self::Element>
278 // + Mul<Self::Scalar, Output=Self::Element>;
279 // const element_length: usize; // in unstable, or u8
280 //type ElementBytes : Index<usize, Output=u8>+IndexMut<usize>; // later
282 fn name() -> &'static str;
283 fn const_m() -> Self::Element;
284 fn const_n() -> Self::Element;
285 fn const_s() -> Self::Element;
286 fn hash_to_scalar(s: &[u8]) -> Self::Scalar;
287 fn random_scalar<T>(cspring: &mut T) -> Self::Scalar
290 fn scalar_neg(s: &Self::Scalar) -> Self::Scalar;
291 fn element_to_bytes(e: &Self::Element) -> Vec<u8>;
292 fn bytes_to_element(b: &[u8]) -> Option<Self::Element>;
293 fn element_length() -> usize;
294 fn basepoint_mult(s: &Self::Scalar) -> Self::Element;
295 fn scalarmult(e: &Self::Element, s: &Self::Scalar) -> Self::Element;
296 fn add(a: &Self::Element, b: &Self::Element) -> Self::Element;
299 #[derive(Debug, PartialEq, Eq)]
300 pub struct Ed25519Group;
302 impl Group for Ed25519Group {
303 type Scalar = c2_Scalar;
304 type Element = c2_Element;
305 //type ElementBytes = Vec<u8>;
306 //type ElementBytes = [u8; 32];
308 type TranscriptHash = Sha256;
310 fn name() -> &'static str {
314 fn const_m() -> c2_Element {
315 // 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)]))"
316 // 15cfd18e385952982b6a8f8c7854963b58e34388c8e6dae891db756481a02312
318 0x15, 0xcf, 0xd1, 0x8e, 0x38, 0x59, 0x52, 0x98, 0x2b, 0x6a, 0x8f, 0x8c, 0x78, 0x54,
319 0x96, 0x3b, 0x58, 0xe3, 0x43, 0x88, 0xc8, 0xe6, 0xda, 0xe8, 0x91, 0xdb, 0x75, 0x64,
320 0x81, 0xa0, 0x23, 0x12,
326 fn const_n() -> c2_Element {
327 // 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)]))"
328 // f04f2e7eb734b2a8f8b472eaf9c3c632576ac64aea650b496a8a20ff00e583c3
330 0xf0, 0x4f, 0x2e, 0x7e, 0xb7, 0x34, 0xb2, 0xa8, 0xf8, 0xb4, 0x72, 0xea, 0xf9, 0xc3,
331 0xc6, 0x32, 0x57, 0x6a, 0xc6, 0x4a, 0xea, 0x65, 0x0b, 0x49, 0x6a, 0x8a, 0x20, 0xff,
332 0x00, 0xe5, 0x83, 0xc3,
338 fn const_s() -> c2_Element {
339 // 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)]))"
340 // 6f00dae87c1be1a73b5922ef431cd8f57879569c222d22b1cd71e8546ab8e6f1
342 0x6f, 0x00, 0xda, 0xe8, 0x7c, 0x1b, 0xe1, 0xa7, 0x3b, 0x59, 0x22, 0xef, 0x43, 0x1c,
343 0xd8, 0xf5, 0x78, 0x79, 0x56, 0x9c, 0x22, 0x2d, 0x22, 0xb1, 0xcd, 0x71, 0xe8, 0x54,
344 0x6a, 0xb8, 0xe6, 0xf1,
350 fn hash_to_scalar(s: &[u8]) -> c2_Scalar {
351 ed25519_hash_to_scalar(s)
353 fn random_scalar<T>(cspring: &mut T) -> c2_Scalar
357 c2_Scalar::random(cspring)
359 fn scalar_neg(s: &c2_Scalar) -> c2_Scalar {
362 fn element_to_bytes(s: &c2_Element) -> Vec<u8> {
363 s.compress().as_bytes().to_vec()
365 fn element_length() -> usize {
368 fn bytes_to_element(b: &[u8]) -> Option<c2_Element> {
372 //let mut bytes: [u8; 32] =
373 let mut bytes = [0u8; 32];
374 bytes.copy_from_slice(b);
375 let cey = CompressedEdwardsY(bytes);
376 // CompressedEdwardsY::new(b)
380 fn basepoint_mult(s: &c2_Scalar) -> c2_Element {
381 //c2_Element::basepoint_mult(s)
382 ED25519_BASEPOINT_POINT * s
384 fn scalarmult(e: &c2_Element, s: &c2_Scalar) -> c2_Element {
388 fn add(a: &c2_Element, b: &c2_Element) -> c2_Element {
394 fn ed25519_hash_to_scalar(s: &[u8]) -> c2_Scalar {
395 //c2_Scalar::hash_from_bytes::<Sha512>(&s)
397 // h = HKDF(salt=b"", ikm=s, hash=SHA256, info=b"SPAKE2 pw", len=32+16)
401 let mut okm = [0u8; 32 + 16];
402 Hkdf::<Sha256>::new(Some(b""), s)
403 .expand(b"SPAKE2 pw", &mut okm)
405 //println!("expanded: {}{}", "................................", okm.iter().to_hex()); // ok
407 let mut reducible = [0u8; 64]; // little-endian
408 for (i, x) in okm.iter().enumerate().take(32 + 16) {
409 reducible[32 + 16 - 1 - i] = *x;
411 //println!("reducible: {}", reducible.iter().to_hex());
412 c2_Scalar::from_bytes_mod_order_wide(&reducible)
413 //let reduced = c2_Scalar::reduce(&reducible);
414 //println!("reduced: {}", reduced.as_bytes().to_hex());
427 assert_eq!(first_msg.len(), 32);
428 assert_eq!(second_msg.len(), 32);
429 // the transcript is fixed-length, made up of 6 32-byte values:
430 // byte 0-31 : sha256(pw)
431 // byte 32-63 : sha256(idA)
432 // byte 64-95 : sha256(idB)
433 // byte 96-127 : X_msg
434 // byte 128-159: Y_msg
435 // byte 160-191: K_bytes
436 let mut transcript = [0u8; 6 * 32];
438 let mut pw_hash = Sha256::new();
439 pw_hash.update(password_vec);
440 transcript[0..32].copy_from_slice(&pw_hash.finalize());
442 let mut ida_hash = Sha256::new();
443 ida_hash.update(id_a);
444 transcript[32..64].copy_from_slice(&ida_hash.finalize());
446 let mut idb_hash = Sha256::new();
447 idb_hash.update(id_b);
448 transcript[64..96].copy_from_slice(&idb_hash.finalize());
450 transcript[96..128].copy_from_slice(first_msg);
451 transcript[128..160].copy_from_slice(second_msg);
452 transcript[160..192].copy_from_slice(key_bytes);
454 //println!("transcript: {:?}", transcript.iter().to_hex());
456 //let mut hash = G::TranscriptHash::default();
457 let mut hash = Sha256::new();
458 hash.update(transcript.to_vec());
459 hash.finalize().to_vec()
462 fn ed25519_hash_symmetric(
469 assert_eq!(msg_u.len(), 32);
470 assert_eq!(msg_v.len(), 32);
471 // # since we don't know which side is which, we must sort the messages
472 // first_msg, second_msg = sorted([msg1, msg2])
473 // transcript = b"".join([sha256(pw).digest(),
474 // sha256(idSymmetric).digest(),
475 // first_msg, second_msg, K_bytes])
477 // the transcript is fixed-length, made up of 5 32-byte values:
478 // byte 0-31 : sha256(pw)
479 // byte 32-63 : sha256(idSymmetric)
480 // byte 64-95 : X_msg
481 // byte 96-127 : Y_msg
482 // byte 128-159: K_bytes
483 let mut transcript = [0u8; 5 * 32];
485 let mut pw_hash = Sha256::new();
486 pw_hash.update(password_vec);
487 transcript[0..32].copy_from_slice(&pw_hash.finalize());
489 let mut ids_hash = Sha256::new();
490 ids_hash.update(id_s);
491 transcript[32..64].copy_from_slice(&ids_hash.finalize());
494 transcript[64..96].copy_from_slice(msg_u);
495 transcript[96..128].copy_from_slice(msg_v);
497 transcript[64..96].copy_from_slice(msg_v);
498 transcript[96..128].copy_from_slice(msg_u);
500 transcript[128..160].copy_from_slice(key_bytes);
502 let mut hash = Sha256::new();
503 hash.update(transcript.to_vec());
504 hash.finalize().to_vec()
507 /* "session type pattern" */
509 #[derive(Debug, PartialEq, Eq)]
516 // we implement a custom Debug below, to avoid revealing secrets in a dump
517 #[derive(PartialEq, Eq)]
518 pub struct SPAKE2<G: Group> {
519 //where &G::Scalar: Neg {
521 xy_scalar: G::Scalar,
522 password_vec: Vec<u8>,
527 password_scalar: G::Scalar,
530 impl<G: Group> SPAKE2<G> {
537 xy_scalar: G::Scalar,
538 ) -> (SPAKE2<G>, Vec<u8>) {
539 //let password_scalar: G::Scalar = hash_to_scalar::<G::Scalar>(password);
540 let password_scalar: G::Scalar = G::hash_to_scalar(password);
544 // sym: X = B*x * S*pw
545 let blinding = match side {
546 Side::A => G::const_m(),
547 Side::B => G::const_n(),
548 Side::Symmetric => G::const_s(),
550 let m1: G::Element = G::add(
551 &G::basepoint_mult(&xy_scalar),
552 &G::scalarmult(&blinding, &password_scalar),
554 //let m1: G::Element = &G::basepoint_mult(&x) + &(blinding * &password_scalar);
555 let msg1: Vec<u8> = G::element_to_bytes(&m1);
556 let mut password_vec = Vec::new();
557 password_vec.extend_from_slice(password);
558 let mut id_a_copy = Vec::new();
559 id_a_copy.extend_from_slice(id_a);
560 let mut id_b_copy = Vec::new();
561 id_b_copy.extend_from_slice(id_b);
562 let mut id_s_copy = Vec::new();
563 id_s_copy.extend_from_slice(id_s);
565 let mut msg_and_side = vec![match side {
566 Side::A => 0x41, // 'A'
567 Side::B => 0x42, // 'B'
568 Side::Symmetric => 0x53, // 'S'
570 msg_and_side.extend_from_slice(&msg1);
576 password_vec, // string
581 password_scalar, // scalar
591 xy_scalar: G::Scalar,
592 ) -> (SPAKE2<G>, Vec<u8>) {
593 Self::start_internal(
607 xy_scalar: G::Scalar,
608 ) -> (SPAKE2<G>, Vec<u8>) {
609 Self::start_internal(
619 fn start_symmetric_internal(
622 xy_scalar: G::Scalar,
623 ) -> (SPAKE2<G>, Vec<u8>) {
624 Self::start_internal(
634 pub fn start_a(password: &Password, id_a: &Identity, id_b: &Identity) -> (SPAKE2<G>, Vec<u8>) {
635 let mut cspring: OsRng = OsRng::new().unwrap();
636 let xy_scalar: G::Scalar = G::random_scalar(&mut cspring);
637 Self::start_a_internal(password, id_a, id_b, xy_scalar)
640 pub fn start_b(password: &Password, id_a: &Identity, id_b: &Identity) -> (SPAKE2<G>, Vec<u8>) {
641 let mut cspring: OsRng = OsRng::new().unwrap();
642 let xy_scalar: G::Scalar = G::random_scalar(&mut cspring);
643 Self::start_b_internal(password, id_a, id_b, xy_scalar)
646 pub fn start_symmetric(password: &Password, id_s: &Identity) -> (SPAKE2<G>, Vec<u8>) {
647 let mut cspring: OsRng = OsRng::new().unwrap();
648 let xy_scalar: G::Scalar = G::random_scalar(&mut cspring);
649 Self::start_symmetric_internal(password, id_s, xy_scalar)
652 pub fn finish(self, msg2: &[u8]) -> Result<Vec<u8>, SPAKEErr> {
653 if msg2.len() != 1 + G::element_length() {
654 return Err(SPAKEErr {
655 kind: ErrorType::WrongLength,
658 let msg_side = msg2[0];
661 Side::A => match msg_side {
664 return Err(SPAKEErr {
665 kind: ErrorType::BadSide,
669 Side::B => match msg_side {
672 return Err(SPAKEErr {
673 kind: ErrorType::BadSide,
677 Side::Symmetric => match msg_side {
680 return Err(SPAKEErr {
681 kind: ErrorType::BadSide,
687 let msg2_element = match G::bytes_to_element(&msg2[1..]) {
690 return Err(SPAKEErr {
691 kind: ErrorType::CorruptMessage,
696 // a: K = (Y+N*(-pw))*x
697 // b: K = (X+M*(-pw))*y
698 let unblinding = match self.side {
699 Side::A => G::const_n(),
700 Side::B => G::const_m(),
701 Side::Symmetric => G::const_s(),
703 let tmp1 = G::scalarmult(&unblinding, &G::scalar_neg(&self.password_scalar));
704 let tmp2 = G::add(&msg2_element, &tmp1);
705 let key_element = G::scalarmult(&tmp2, &self.xy_scalar);
706 let key_bytes = G::element_to_bytes(&key_element);
708 // key = H(H(pw) + H(idA) + H(idB) + X + Y + K)
709 //transcript = b"".join([sha256(pw).digest(),
710 // sha256(idA).digest(), sha256(idB).digest(),
711 // X_msg, Y_msg, K_bytes])
712 //key = sha256(transcript).digest()
713 // note that both sides must use the same order
716 Side::A => ed25519_hash_ab(
720 self.msg1.as_slice(),
724 Side::B => ed25519_hash_ab(
729 self.msg1.as_slice(),
732 Side::Symmetric => ed25519_hash_symmetric(
743 fn maybe_utf8(s: &[u8]) -> String {
744 match String::from_utf8(s.to_vec()) {
745 Ok(m) => format!("(s={})", m),
746 Err(_) => format!("(hex={})", hex::encode(s)),
750 impl<G: Group> fmt::Debug for SPAKE2<G> {
751 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
752 fmt.debug_struct("SPAKE2")
753 .field("group", &G::name())
754 .field("side", &self.side)
755 .field("idA", &maybe_utf8(&self.id_a))
756 .field("idB", &maybe_utf8(&self.id_b))
757 .field("idS", &maybe_utf8(&self.id_s))