2 #![cfg_attr(docsrs, feature(doc_cfg))]
3 #![doc = include_str!("../README.md")]
5 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
6 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
8 #![forbid(unsafe_code)]
9 #![warn(rust_2018_idioms, unused_qualifications)]
13 //! Alice and Bob both initialize their SPAKE2 instances with the same (weak)
14 //! password. They will exchange messages to (hopefully) derive a shared secret
15 //! key. The protocol is symmetric: for each operation that Alice does, Bob will
18 //! However, there are two roles in the SPAKE2 protocol, "A" and "B". The two
19 //! sides must agree ahead of time which one will play which role (the
20 //! messages they generate depend upon which side they play). There are two
21 //! separate constructor functions, `start_a()` and `start_b()`, and a
22 //! complete interaction will use one of each (one `start_a` on one computer,
23 //! and one `start_b` on the other computer).
25 //! Each instance of a SPAKE2 protocol uses a set of shared parameters. These
26 //! include a group, a generator, and a pair of arbitrary group elements.
27 //! This library comes a single pre-generated parameter set, but could be
28 //! extended with others.
30 //! You start by calling `start_a()` (or `_b)` with the password and identity
31 //! strings for both sides. This gives you back a state object and the first
32 //! message, which you must send to your partner. Once you receive the
33 //! corresponding inbound message, you pass it into the state object
34 //! (consuming both in the process) by calling `s.finish()`, and you get back
35 //! the shared key as a bytestring.
37 //! The password and identity strings must each be wrapped in a "newtype",
38 //! which is a simple `struct` that protects against swapping the different
39 //! types of bytestrings.
41 //! Thus a client-side program start with:
44 //! use spake2::{Ed25519Group, Identity, Password, SPAKE2};
45 //! # fn send(msg: &[u8]) {}
46 //! let (s1, outbound_msg) = SPAKE2::<Ed25519Group>::start_a(
47 //! &Password::new(b"password"),
48 //! &Identity::new(b"client id string"),
49 //! &Identity::new(b"server id string"));
50 //! send(&outbound_msg);
52 //! # 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 }
53 //! let inbound_msg = receive();
54 //! let key1 = s1.finish(&inbound_msg).unwrap();
57 //! while the server-side might do:
60 //! # fn send(msg: &[u8]) {}
61 //! use spake2::{Ed25519Group, Identity, Password, SPAKE2};
62 //! let (s1, outbound_msg) = SPAKE2::<Ed25519Group>::start_b(
63 //! &Password::new(b"password"),
64 //! &Identity::new(b"client id string"),
65 //! &Identity::new(b"server id string"));
66 //! send(&outbound_msg);
68 //! # 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 }
69 //! let inbound_msg = receive();
70 //! let key2 = s1.finish(&inbound_msg).unwrap();
73 //! If both sides used the same password, and there is no man-in-the-middle,
74 //! then `key1` and `key2` will be identical. If not, the two sides will get
75 //! different keys. When one side encrypts with `key1`, and the other side
76 //! attempts to decrypt with `key2`, they'll get nothing but garbled noise.
78 //! The shared key can be used as an HMAC key to provide data integrity on
79 //! subsequent messages, or as an authenticated-encryption key (e.g.
80 //! nacl.secretbox). It can also be fed into [HKDF][1] to derive other
81 //! session keys as necessary.
83 //! The `SPAKE2` instances, and the messages they create, are single-use. Create
84 //! a new one for each new session. `finish` consumes the instance.
88 //! A single SPAKE2 instance must be used asymmetrically: the two sides must
89 //! somehow decide (ahead of time) which role they will each play. The
90 //! implementation includes the side identifier in the exchanged message to
91 //! guard against a `start_a` talking to another `start_a`. Typically a
92 //! "client" will take on the `A` role, and the "server" will be `B`.
94 //! This is a nuisance for more egalitarian protocols, where there's no clear
95 //! way to assign these roles ahead of time. In this case, use
96 //! `start_symmetric()` on both sides. This uses a different set of
97 //! parameters (so it is not interoperable with `start_A` or `start_b`), but
98 //! should otherwise behave the same way. The symmetric mode uses only one
99 //! identity string, not two.
104 //! # fn send(msg: &[u8]) {}
105 //! use spake2::{Ed25519Group, Identity, Password, SPAKE2};
106 //! let (s1, outbound_msg) = SPAKE2::<Ed25519Group>::start_symmetric(
107 //! &Password::new(b"password"),
108 //! &Identity::new(b"shared id string"));
109 //! send(&outbound_msg);
111 //! # fn receive() -> Vec<u8> { let (s2, i2) = SPAKE2::<Ed25519Group>::start_symmetric(&Password::new(b"password"), &Identity::new(b"shared id string")); i2 }
112 //! let inbound_msg = receive();
113 //! let key1 = s1.finish(&inbound_msg).unwrap();
116 //! Dave does exactly the same:
119 //! # fn send(msg: &[u8]) {}
120 //! use spake2::{Ed25519Group, Identity, Password, SPAKE2};
121 //! let (s1, outbound_msg) = SPAKE2::<Ed25519Group>::start_symmetric(
122 //! &Password::new(b"password"),
123 //! &Identity::new(b"shared id string"));
124 //! send(&outbound_msg);
126 //! # fn receive() -> Vec<u8> { let (s2, i2) = SPAKE2::<Ed25519Group>::start_symmetric(&Password::new(b"password"), &Identity::new(b"shared id string")); i2 }
127 //! let inbound_msg = receive();
128 //! let key1 = s1.finish(&inbound_msg).unwrap();
131 //! # Identifier Strings
133 //! The SPAKE2 protocol includes a pair of "identity strings" `idA` and `idB`
134 //! that are included in the final key-derivation hash. This binds the key to a
135 //! single pair of parties, or for some specific purpose.
137 //! For example, when user "alice" logs into "example.com", both sides should set
138 //! `idA = b"alice"` and `idB = b"example.com"`. This prevents an attacker from
139 //! substituting messages from unrelated login sessions (other users on the same
140 //! server, or other servers for the same user).
142 //! This also makes sure the session is established with the correct service. If
143 //! Alice has one password for "example.com" but uses it for both login and
144 //! file-transfer services, `idB` should be different for the two services.
145 //! Otherwise if Alice is simultaneously connecting to both services, and
146 //! attacker could rearrange the messages and cause her login client to connect
147 //! to the file-transfer server, and vice versa.
149 //! `idA` and `idB` must be bytestrings (slices of `<u8>`).
151 //! `start_symmetric` uses a single `idSymmetric=` string, instead of `idA`
152 //! and `idB`. Both sides must provide the same `idSymmetric=`, or leave it
157 //! Sometimes, you can't hold the SPAKE2 instance in memory for the whole
158 //! negotiation: perhaps all your program state is stored in a database, and
159 //! nothing lives in RAM for more than a few moments.
161 //! Unfortunately the Rust implementation does not yet provide serialization
162 //! of the state object. A future version should correct this.
166 //! This library is probably not constant-time, and does not protect against
167 //! timing attacks. Do not allow attackers to measure how long it takes you
168 //! to create or respond to a message. This matters somewhat less for pairing
169 //! protocols, because their passwords are single-use randomly-generated
170 //! keys, so an attacker has much less to work with.
172 //! This library depends upon a strong source of random numbers. Do not use it on
173 //! a system where os.urandom() is weak.
177 //! To run the built-in speed tests, just run `cargo bench`.
179 //! SPAKE2 consists of two phases, separated by a single message exchange.
180 //! The time these phases take is split roughly 50/50. On my 2.8GHz Core-i7
181 //! (i7-7600U) cpu, the built-in Ed25519Group parameters take about 112
182 //! microseconds for each phase, and the message exchanged is 33 bytes long.
186 //! Run `cargo test` to run the built-in test suite.
190 //! The protocol was described as "PAKE2" in ["cryptobook"] [2] from Dan Boneh
191 //! and Victor Shoup. This is a form of "SPAKE2", defined by Abdalla and
192 //! Pointcheval at [RSA 2005] [3]. Additional recommendations for groups and
193 //! distinguished elements were published in [Ladd's IETF draft] [4].
195 //! The Ed25519 implementation uses code adapted from Daniel Bernstein (djb),
196 //! Matthew Dempsky, Daniel Holth, Ron Garret, with further optimizations by
197 //! Brian Warner[5]. The "arbitrary element" computation, which must be the same
198 //! for both participants, is from python-pure25519 version 0.5.
200 //! The Boneh/Shoup chapter that defines PAKE2 also defines an augmented variant
201 //! named "PAKE2+", which changes one side (typically a server) to record a
202 //! derivative of the password instead of the actual password. In PAKE2+, a
203 //! server compromise does not immediately give access to the passwords: instead,
204 //! the attacker must perform an offline dictionary attack against the stolen
205 //! data before they can learn the passwords. PAKE2+ support is planned, but not
208 //! The security of the symmetric case was proved by Kobara/Imai[6] in 2003, and
209 //! uses different (slightly weaker?) reductions than that of the asymmetric
210 //! form. See also Mike Hamburg's analysis[7] from 2015.
212 //! Brian Warner first wrote the Python version in July 2010. He wrote this
213 //! Rust version in in May 2017.
217 //! [1]: https://tools.ietf.org/html/rfc5869 "HKDF"
218 //! [2]: http://crypto.stanford.edu/~dabo/cryptobook/ "cryptobook"
219 //! [3]: http://www.di.ens.fr/~pointche/Documents/Papers/2005_rsa.pdf "RSA 2005"
220 //! [4]: https://tools.ietf.org/html/draft-ladd-spake2-01 "Ladd's IETF draft"
221 //! [5]: https://github.com/warner/python-pure25519
222 //! [6]: http://eprint.iacr.org/2003/038.pdf "Pretty-Simple Password-Authenticated Key-Exchange Under Standard Assumptions"
223 //! [7]: https://moderncrypto.org/mail-archive/curves/2015/000419.html "PAKE questions"
225 #[allow(unused_imports)]
229 #[cfg(feature = "std")]
230 #[cfg_attr(test, macro_use)]
234 use core::{fmt, ops::Deref, str};
235 use curve25519_dalek::{
236 constants::ED25519_BASEPOINT_POINT,
237 edwards::{CompressedEdwardsY, EdwardsPoint as c2_Element},
238 scalar::Scalar as c2_Scalar,
241 use rand_core::{CryptoRng, RngCore};
242 use sha2::{Digest, Sha256};
244 #[cfg(feature = "getrandom")]
245 use rand_core::OsRng;
247 /* "newtype pattern": it's a Vec<u8>, but only used for a specific argument
248 * type, to distinguish between ones that are meant as passwords, and ones
249 * that are meant as identity strings */
251 #[derive(PartialEq, Eq, Clone)]
252 pub struct Password(Vec<u8>);
254 pub fn new(p: &[u8]) -> Password {
258 impl Deref for Password {
259 type Target = Vec<u8>;
260 fn deref(&self) -> &Vec<u8> {
265 #[derive(PartialEq, Eq, Clone)]
266 pub struct Identity(Vec<u8>);
267 impl Deref for Identity {
268 type Target = Vec<u8>;
269 fn deref(&self) -> &Vec<u8> {
274 pub fn new(p: &[u8]) -> Identity {
279 #[derive(Debug, PartialEq, Eq)]
286 #[derive(Debug, PartialEq, Eq)]
287 pub struct SPAKEErr {
294 //type Element: Add<Output=Self::Element>
295 // + Mul<Self::Scalar, Output=Self::Element>;
296 // const element_length: usize; // in unstable, or u8
297 //type ElementBytes : Index<usize, Output=u8>+IndexMut<usize>; // later
299 fn name() -> &'static str;
300 fn const_m() -> Self::Element;
301 fn const_n() -> Self::Element;
302 fn const_s() -> Self::Element;
303 fn hash_to_scalar(s: &[u8]) -> Self::Scalar;
304 fn random_scalar<T>(cspring: &mut T) -> Self::Scalar
306 T: RngCore + CryptoRng;
307 fn scalar_neg(s: &Self::Scalar) -> Self::Scalar;
308 fn element_to_bytes(e: &Self::Element) -> Vec<u8>;
309 fn bytes_to_element(b: &[u8]) -> Option<Self::Element>;
310 fn element_length() -> usize;
311 fn basepoint_mult(s: &Self::Scalar) -> Self::Element;
312 fn scalarmult(e: &Self::Element, s: &Self::Scalar) -> Self::Element;
313 fn add(a: &Self::Element, b: &Self::Element) -> Self::Element;
316 #[derive(Debug, PartialEq, Eq)]
317 pub struct Ed25519Group;
319 impl Group for Ed25519Group {
320 type Scalar = c2_Scalar;
321 type Element = c2_Element;
322 //type ElementBytes = Vec<u8>;
323 //type ElementBytes = [u8; 32];
325 type TranscriptHash = Sha256;
327 fn name() -> &'static str {
331 fn const_m() -> c2_Element {
332 // 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)]))"
333 // 15cfd18e385952982b6a8f8c7854963b58e34388c8e6dae891db756481a02312
335 0x15, 0xcf, 0xd1, 0x8e, 0x38, 0x59, 0x52, 0x98, 0x2b, 0x6a, 0x8f, 0x8c, 0x78, 0x54,
336 0x96, 0x3b, 0x58, 0xe3, 0x43, 0x88, 0xc8, 0xe6, 0xda, 0xe8, 0x91, 0xdb, 0x75, 0x64,
337 0x81, 0xa0, 0x23, 0x12,
343 fn const_n() -> c2_Element {
344 // 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)]))"
345 // f04f2e7eb734b2a8f8b472eaf9c3c632576ac64aea650b496a8a20ff00e583c3
347 0xf0, 0x4f, 0x2e, 0x7e, 0xb7, 0x34, 0xb2, 0xa8, 0xf8, 0xb4, 0x72, 0xea, 0xf9, 0xc3,
348 0xc6, 0x32, 0x57, 0x6a, 0xc6, 0x4a, 0xea, 0x65, 0x0b, 0x49, 0x6a, 0x8a, 0x20, 0xff,
349 0x00, 0xe5, 0x83, 0xc3,
355 fn const_s() -> c2_Element {
356 // 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)]))"
357 // 6f00dae87c1be1a73b5922ef431cd8f57879569c222d22b1cd71e8546ab8e6f1
359 0x6f, 0x00, 0xda, 0xe8, 0x7c, 0x1b, 0xe1, 0xa7, 0x3b, 0x59, 0x22, 0xef, 0x43, 0x1c,
360 0xd8, 0xf5, 0x78, 0x79, 0x56, 0x9c, 0x22, 0x2d, 0x22, 0xb1, 0xcd, 0x71, 0xe8, 0x54,
361 0x6a, 0xb8, 0xe6, 0xf1,
367 fn hash_to_scalar(s: &[u8]) -> c2_Scalar {
368 ed25519_hash_to_scalar(s)
370 fn random_scalar<T>(cspring: &mut T) -> c2_Scalar
372 T: RngCore + CryptoRng,
374 c2_Scalar::random(cspring)
376 fn scalar_neg(s: &c2_Scalar) -> c2_Scalar {
379 fn element_to_bytes(s: &c2_Element) -> Vec<u8> {
380 s.compress().as_bytes().to_vec()
382 fn element_length() -> usize {
385 fn bytes_to_element(b: &[u8]) -> Option<c2_Element> {
389 //let mut bytes: [u8; 32] =
390 let mut bytes = [0u8; 32];
391 bytes.copy_from_slice(b);
392 let cey = CompressedEdwardsY(bytes);
393 // CompressedEdwardsY::new(b)
397 fn basepoint_mult(s: &c2_Scalar) -> c2_Element {
398 //c2_Element::basepoint_mult(s)
399 ED25519_BASEPOINT_POINT * s
401 fn scalarmult(e: &c2_Element, s: &c2_Scalar) -> c2_Element {
405 fn add(a: &c2_Element, b: &c2_Element) -> c2_Element {
411 fn ed25519_hash_to_scalar(s: &[u8]) -> c2_Scalar {
412 //c2_Scalar::hash_from_bytes::<Sha512>(&s)
414 // h = HKDF(salt=b"", ikm=s, hash=SHA256, info=b"SPAKE2 pw", len=32+16)
418 let mut okm = [0u8; 32 + 16];
419 Hkdf::<Sha256>::new(Some(b""), s)
420 .expand(b"SPAKE2 pw", &mut okm)
422 //println!("expanded: {}{}", "................................", okm.iter().to_hex()); // ok
424 let mut reducible = [0u8; 64]; // little-endian
425 for (i, x) in okm.iter().enumerate().take(32 + 16) {
426 reducible[32 + 16 - 1 - i] = *x;
428 //println!("reducible: {}", reducible.iter().to_hex());
429 c2_Scalar::from_bytes_mod_order_wide(&reducible)
430 //let reduced = c2_Scalar::reduce(&reducible);
431 //println!("reduced: {}", reduced.as_bytes().to_hex());
444 assert_eq!(first_msg.len(), 32);
445 assert_eq!(second_msg.len(), 32);
446 // the transcript is fixed-length, made up of 6 32-byte values:
447 // byte 0-31 : sha256(pw)
448 // byte 32-63 : sha256(idA)
449 // byte 64-95 : sha256(idB)
450 // byte 96-127 : X_msg
451 // byte 128-159: Y_msg
452 // byte 160-191: K_bytes
453 let mut transcript = [0u8; 6 * 32];
455 let mut pw_hash = Sha256::new();
456 pw_hash.update(password_vec);
457 transcript[0..32].copy_from_slice(&pw_hash.finalize());
459 let mut ida_hash = Sha256::new();
460 ida_hash.update(id_a);
461 transcript[32..64].copy_from_slice(&ida_hash.finalize());
463 let mut idb_hash = Sha256::new();
464 idb_hash.update(id_b);
465 transcript[64..96].copy_from_slice(&idb_hash.finalize());
467 transcript[96..128].copy_from_slice(first_msg);
468 transcript[128..160].copy_from_slice(second_msg);
469 transcript[160..192].copy_from_slice(key_bytes);
471 //println!("transcript: {:?}", transcript.iter().to_hex());
473 //let mut hash = G::TranscriptHash::default();
474 let mut hash = Sha256::new();
475 hash.update(transcript.to_vec());
476 hash.finalize().to_vec()
479 fn ed25519_hash_symmetric(
486 assert_eq!(msg_u.len(), 32);
487 assert_eq!(msg_v.len(), 32);
488 // # since we don't know which side is which, we must sort the messages
489 // first_msg, second_msg = sorted([msg1, msg2])
490 // transcript = b"".join([sha256(pw).digest(),
491 // sha256(idSymmetric).digest(),
492 // first_msg, second_msg, K_bytes])
494 // the transcript is fixed-length, made up of 5 32-byte values:
495 // byte 0-31 : sha256(pw)
496 // byte 32-63 : sha256(idSymmetric)
497 // byte 64-95 : X_msg
498 // byte 96-127 : Y_msg
499 // byte 128-159: K_bytes
500 let mut transcript = [0u8; 5 * 32];
502 let mut pw_hash = Sha256::new();
503 pw_hash.update(password_vec);
504 transcript[0..32].copy_from_slice(&pw_hash.finalize());
506 let mut ids_hash = Sha256::new();
507 ids_hash.update(id_s);
508 transcript[32..64].copy_from_slice(&ids_hash.finalize());
511 transcript[64..96].copy_from_slice(msg_u);
512 transcript[96..128].copy_from_slice(msg_v);
514 transcript[64..96].copy_from_slice(msg_v);
515 transcript[96..128].copy_from_slice(msg_u);
517 transcript[128..160].copy_from_slice(key_bytes);
519 let mut hash = Sha256::new();
520 hash.update(transcript.to_vec());
521 hash.finalize().to_vec()
524 /* "session type pattern" */
526 #[derive(Debug, PartialEq, Eq)]
533 // we implement a custom Debug below, to avoid revealing secrets in a dump
534 #[derive(PartialEq, Eq)]
535 pub struct SPAKE2<G: Group> {
536 //where &G::Scalar: Neg {
538 xy_scalar: G::Scalar,
539 password_vec: Vec<u8>,
544 password_scalar: G::Scalar,
547 impl<G: Group> SPAKE2<G> {
554 xy_scalar: G::Scalar,
555 ) -> (SPAKE2<G>, Vec<u8>) {
556 //let password_scalar: G::Scalar = hash_to_scalar::<G::Scalar>(password);
557 let password_scalar: G::Scalar = G::hash_to_scalar(password);
561 // sym: X = B*x * S*pw
562 let blinding = match side {
563 Side::A => G::const_m(),
564 Side::B => G::const_n(),
565 Side::Symmetric => G::const_s(),
567 let m1: G::Element = G::add(
568 &G::basepoint_mult(&xy_scalar),
569 &G::scalarmult(&blinding, &password_scalar),
571 //let m1: G::Element = &G::basepoint_mult(&x) + &(blinding * &password_scalar);
572 let msg1: Vec<u8> = G::element_to_bytes(&m1);
573 let mut password_vec = Vec::new();
574 password_vec.extend_from_slice(password);
575 let mut id_a_copy = Vec::new();
576 id_a_copy.extend_from_slice(id_a);
577 let mut id_b_copy = Vec::new();
578 id_b_copy.extend_from_slice(id_b);
579 let mut id_s_copy = Vec::new();
580 id_s_copy.extend_from_slice(id_s);
582 let mut msg_and_side = vec![match side {
583 Side::A => 0x41, // 'A'
584 Side::B => 0x42, // 'B'
585 Side::Symmetric => 0x53, // 'S'
587 msg_and_side.extend_from_slice(&msg1);
593 password_vec, // string
598 password_scalar, // scalar
608 xy_scalar: G::Scalar,
609 ) -> (SPAKE2<G>, Vec<u8>) {
610 Self::start_internal(
624 xy_scalar: G::Scalar,
625 ) -> (SPAKE2<G>, Vec<u8>) {
626 Self::start_internal(
636 fn start_symmetric_internal(
639 xy_scalar: G::Scalar,
640 ) -> (SPAKE2<G>, Vec<u8>) {
641 Self::start_internal(
651 #[cfg(feature = "getrandom")]
652 #[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))]
653 pub fn start_a(password: &Password, id_a: &Identity, id_b: &Identity) -> (SPAKE2<G>, Vec<u8>) {
654 Self::start_a_with_rng(password, id_a, id_b, OsRng)
657 #[cfg(feature = "getrandom")]
658 #[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))]
659 pub fn start_b(password: &Password, id_a: &Identity, id_b: &Identity) -> (SPAKE2<G>, Vec<u8>) {
660 Self::start_b_with_rng(password, id_a, id_b, OsRng)
663 #[cfg(feature = "getrandom")]
664 #[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))]
665 pub fn start_symmetric(password: &Password, id_s: &Identity) -> (SPAKE2<G>, Vec<u8>) {
666 Self::start_symmetric_with_rng(password, id_s, OsRng)
669 pub fn start_a_with_rng(
673 mut csprng: impl CryptoRng + RngCore,
674 ) -> (SPAKE2<G>, Vec<u8>) {
675 let xy_scalar: G::Scalar = G::random_scalar(&mut csprng);
676 Self::start_a_internal(password, id_a, id_b, xy_scalar)
679 pub fn start_b_with_rng(
683 mut csprng: impl CryptoRng + RngCore,
684 ) -> (SPAKE2<G>, Vec<u8>) {
685 let xy_scalar: G::Scalar = G::random_scalar(&mut csprng);
686 Self::start_b_internal(password, id_a, id_b, xy_scalar)
689 pub fn start_symmetric_with_rng(
692 mut csprng: impl CryptoRng + RngCore,
693 ) -> (SPAKE2<G>, Vec<u8>) {
694 let xy_scalar: G::Scalar = G::random_scalar(&mut csprng);
695 Self::start_symmetric_internal(password, id_s, xy_scalar)
698 pub fn finish(self, msg2: &[u8]) -> Result<Vec<u8>, SPAKEErr> {
699 if msg2.len() != 1 + G::element_length() {
700 return Err(SPAKEErr {
701 kind: ErrorType::WrongLength,
704 let msg_side = msg2[0];
707 Side::A => match msg_side {
710 return Err(SPAKEErr {
711 kind: ErrorType::BadSide,
715 Side::B => match msg_side {
718 return Err(SPAKEErr {
719 kind: ErrorType::BadSide,
723 Side::Symmetric => match msg_side {
726 return Err(SPAKEErr {
727 kind: ErrorType::BadSide,
733 let msg2_element = match G::bytes_to_element(&msg2[1..]) {
736 return Err(SPAKEErr {
737 kind: ErrorType::CorruptMessage,
742 // a: K = (Y+N*(-pw))*x
743 // b: K = (X+M*(-pw))*y
744 let unblinding = match self.side {
745 Side::A => G::const_n(),
746 Side::B => G::const_m(),
747 Side::Symmetric => G::const_s(),
749 let tmp1 = G::scalarmult(&unblinding, &G::scalar_neg(&self.password_scalar));
750 let tmp2 = G::add(&msg2_element, &tmp1);
751 let key_element = G::scalarmult(&tmp2, &self.xy_scalar);
752 let key_bytes = G::element_to_bytes(&key_element);
754 // key = H(H(pw) + H(idA) + H(idB) + X + Y + K)
755 //transcript = b"".join([sha256(pw).digest(),
756 // sha256(idA).digest(), sha256(idB).digest(),
757 // X_msg, Y_msg, K_bytes])
758 //key = sha256(transcript).digest()
759 // note that both sides must use the same order
762 Side::A => ed25519_hash_ab(
766 self.msg1.as_slice(),
770 Side::B => ed25519_hash_ab(
775 self.msg1.as_slice(),
778 Side::Symmetric => ed25519_hash_symmetric(
789 impl<G: Group> fmt::Debug for SPAKE2<G> {
790 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
791 fmt.debug_struct("SPAKE2")
792 .field("group", &G::name())
793 .field("side", &self.side)
794 .field("idA", &MaybeUtf8(&self.id_a))
795 .field("idB", &MaybeUtf8(&self.id_b))
796 .field("idS", &MaybeUtf8(&self.id_s))
801 struct MaybeUtf8<'a>(&'a [u8]);
803 impl fmt::Debug for MaybeUtf8<'_> {
804 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
805 if let Ok(s) = str::from_utf8(self.0) {
806 write!(fmt, "(s={})", s)
808 write!(fmt, "(hex=")?;
811 write!(fmt, "{:x}", byte)?;