]> git.lizzy.rs Git - PAKEs.git/blob - spake2/src/lib.rs
spake2: initial `no_std` support (#87)
[PAKEs.git] / spake2 / src / lib.rs
1 #![no_std]
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)]
6
7 //! # Usage
8 //!
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
12 //! do the same.
13 //!
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).
20 //!
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.
25 //!
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.
32 //!
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.
36 //!
37 //! Thus a client-side program start with:
38 //!
39 //! ```rust
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);
47 //!
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();
51 //! ```
52 //!
53 //! while the server-side might do:
54 //!
55 //! ```rust
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);
63 //!
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();
67 //! ```
68 //!
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.
73 //!
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.
78 //!
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.
81 //!
82 //! # Symmetric Usage
83 //!
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`.
89 //!
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.
96 //!
97 //! Carol does:
98 //!
99 //! ```rust
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);
106 //!
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();
110 //! ```
111 //!
112 //! Dave does exactly the same:
113 //!
114 //! ```rust
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);
121 //!
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();
125 //! ```
126 //!
127 //! # Identifier Strings
128 //!
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.
132 //!
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).
137 //!
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.
144 //!
145 //! `idA` and `idB` must be bytestrings (slices of `<u8>`).
146 //!
147 //! `start_symmetric` uses a single `idSymmetric=` string, instead of `idA`
148 //! and `idB`. Both sides must provide the same `idSymmetric=`, or leave it
149 //! empty.
150 //!
151 //! # Serialization
152 //!
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.
156 //!
157 //! Unfortunately the Rust implementation does not yet provide serialization
158 //! of the state object. A future version should correct this.
159 //!
160 //! # Security
161 //!
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.
167 //!
168 //! This library depends upon a strong source of random numbers. Do not use it on
169 //! a system where os.urandom() is weak.
170 //!
171 //! # Speed
172 //!
173 //! To run the built-in speed tests, just run `cargo bench`.
174 //!
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.
179 //!
180 //! # Testing
181 //!
182 //! Run `cargo test` to run the built-in test suite.
183 //!
184 //! # History
185 //!
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].
190 //!
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.
195 //!
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
202 //! yet implemented.
203 //!
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.
207 //!
208 //! Brian Warner first wrote the Python version in July 2010. He wrote this
209 //! Rust version in in May 2017.
210 //!
211 //! ### footnotes
212 //!
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"
220
221 #[allow(unused_imports)]
222 #[macro_use]
223 extern crate alloc;
224
225 #[cfg(feature = "std")]
226 #[cfg_attr(test, macro_use)]
227 extern crate std;
228
229 use alloc::vec::Vec;
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,
235 };
236 use hkdf::Hkdf;
237 use rand_core::{CryptoRng, OsRng, RngCore};
238 use sha2::{Digest, Sha256};
239
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 */
243
244 #[derive(PartialEq, Eq, Clone)]
245 pub struct Password(Vec<u8>);
246 impl Password {
247     pub fn new(p: &[u8]) -> Password {
248         Password(p.to_vec())
249     }
250 }
251 impl Deref for Password {
252     type Target = Vec<u8>;
253     fn deref(&self) -> &Vec<u8> {
254         &self.0
255     }
256 }
257
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> {
263         &self.0
264     }
265 }
266 impl Identity {
267     pub fn new(p: &[u8]) -> Identity {
268         Identity(p.to_vec())
269     }
270 }
271
272 #[derive(Debug, PartialEq, Eq)]
273 pub enum ErrorType {
274     BadSide,
275     WrongLength,
276     CorruptMessage,
277 }
278
279 #[derive(Debug, PartialEq, Eq)]
280 pub struct SPAKEErr {
281     pub kind: ErrorType,
282 }
283
284 pub trait Group {
285     type Scalar;
286     type Element;
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
291     type TranscriptHash;
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
298     where
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;
307 }
308
309 #[derive(Debug, PartialEq, Eq)]
310 pub struct Ed25519Group;
311
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];
317     //type ScalarBytes
318     type TranscriptHash = Sha256;
319
320     fn name() -> &'static str {
321         "Ed25519"
322     }
323
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
327         CompressedEdwardsY([
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,
331         ])
332         .decompress()
333         .unwrap()
334     }
335
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
339         CompressedEdwardsY([
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,
343         ])
344         .decompress()
345         .unwrap()
346     }
347
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
351         CompressedEdwardsY([
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,
355         ])
356         .decompress()
357         .unwrap()
358     }
359
360     fn hash_to_scalar(s: &[u8]) -> c2_Scalar {
361         ed25519_hash_to_scalar(s)
362     }
363     fn random_scalar<T>(cspring: &mut T) -> c2_Scalar
364     where
365         T: RngCore + CryptoRng,
366     {
367         c2_Scalar::random(cspring)
368     }
369     fn scalar_neg(s: &c2_Scalar) -> c2_Scalar {
370         -s
371     }
372     fn element_to_bytes(s: &c2_Element) -> Vec<u8> {
373         s.compress().as_bytes().to_vec()
374     }
375     fn element_length() -> usize {
376         32
377     }
378     fn bytes_to_element(b: &[u8]) -> Option<c2_Element> {
379         if b.len() != 32 {
380             return None;
381         }
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)
387         cey.decompress()
388     }
389
390     fn basepoint_mult(s: &c2_Scalar) -> c2_Element {
391         //c2_Element::basepoint_mult(s)
392         ED25519_BASEPOINT_POINT * s
393     }
394     fn scalarmult(e: &c2_Element, s: &c2_Scalar) -> c2_Element {
395         e * s
396         //e.scalar_mult(s)
397     }
398     fn add(a: &c2_Element, b: &c2_Element) -> c2_Element {
399         a + b
400         //a.add(b)
401     }
402 }
403
404 fn ed25519_hash_to_scalar(s: &[u8]) -> c2_Scalar {
405     //c2_Scalar::hash_from_bytes::<Sha512>(&s)
406     // spake2.py does:
407     //  h = HKDF(salt=b"", ikm=s, hash=SHA256, info=b"SPAKE2 pw", len=32+16)
408     //  i = int(h, 16)
409     //  i % q
410
411     let mut okm = [0u8; 32 + 16];
412     Hkdf::<Sha256>::new(Some(b""), s)
413         .expand(b"SPAKE2 pw", &mut okm)
414         .unwrap();
415     //println!("expanded:   {}{}", "................................", okm.iter().to_hex()); // ok
416
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;
420     }
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());
425     //println!("done");
426     //reduced
427 }
428
429 fn ed25519_hash_ab(
430     password_vec: &[u8],
431     id_a: &[u8],
432     id_b: &[u8],
433     first_msg: &[u8],
434     second_msg: &[u8],
435     key_bytes: &[u8],
436 ) -> Vec<u8> {
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];
447
448     let mut pw_hash = Sha256::new();
449     pw_hash.update(password_vec);
450     transcript[0..32].copy_from_slice(&pw_hash.finalize());
451
452     let mut ida_hash = Sha256::new();
453     ida_hash.update(id_a);
454     transcript[32..64].copy_from_slice(&ida_hash.finalize());
455
456     let mut idb_hash = Sha256::new();
457     idb_hash.update(id_b);
458     transcript[64..96].copy_from_slice(&idb_hash.finalize());
459
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);
463
464     //println!("transcript: {:?}", transcript.iter().to_hex());
465
466     //let mut hash = G::TranscriptHash::default();
467     let mut hash = Sha256::new();
468     hash.update(transcript.to_vec());
469     hash.finalize().to_vec()
470 }
471
472 fn ed25519_hash_symmetric(
473     password_vec: &[u8],
474     id_s: &[u8],
475     msg_u: &[u8],
476     msg_v: &[u8],
477     key_bytes: &[u8],
478 ) -> Vec<u8> {
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])
486
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];
494
495     let mut pw_hash = Sha256::new();
496     pw_hash.update(password_vec);
497     transcript[0..32].copy_from_slice(&pw_hash.finalize());
498
499     let mut ids_hash = Sha256::new();
500     ids_hash.update(id_s);
501     transcript[32..64].copy_from_slice(&ids_hash.finalize());
502
503     if msg_u < msg_v {
504         transcript[64..96].copy_from_slice(msg_u);
505         transcript[96..128].copy_from_slice(msg_v);
506     } else {
507         transcript[64..96].copy_from_slice(msg_v);
508         transcript[96..128].copy_from_slice(msg_u);
509     }
510     transcript[128..160].copy_from_slice(key_bytes);
511
512     let mut hash = Sha256::new();
513     hash.update(transcript.to_vec());
514     hash.finalize().to_vec()
515 }
516
517 /* "session type pattern" */
518
519 #[derive(Debug, PartialEq, Eq)]
520 enum Side {
521     A,
522     B,
523     Symmetric,
524 }
525
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 {
530     side: Side,
531     xy_scalar: G::Scalar,
532     password_vec: Vec<u8>,
533     id_a: Vec<u8>,
534     id_b: Vec<u8>,
535     id_s: Vec<u8>,
536     msg1: Vec<u8>,
537     password_scalar: G::Scalar,
538 }
539
540 impl<G: Group> SPAKE2<G> {
541     fn start_internal(
542         side: Side,
543         password: &Password,
544         id_a: &Identity,
545         id_b: &Identity,
546         id_s: &Identity,
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);
551
552         // a: X = B*x + M*pw
553         // b: Y = B*y + N*pw
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(),
559         };
560         let m1: G::Element = G::add(
561             &G::basepoint_mult(&xy_scalar),
562             &G::scalarmult(&blinding, &password_scalar),
563         );
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);
574
575         let mut msg_and_side = vec![match side {
576             Side::A => 0x41,         // 'A'
577             Side::B => 0x42,         // 'B'
578             Side::Symmetric => 0x53, // 'S'
579         }];
580         msg_and_side.extend_from_slice(&msg1);
581
582         (
583             SPAKE2 {
584                 side,
585                 xy_scalar,
586                 password_vec, // string
587                 id_a: id_a_copy,
588                 id_b: id_b_copy,
589                 id_s: id_s_copy,
590                 msg1,
591                 password_scalar, // scalar
592             },
593             msg_and_side,
594         )
595     }
596
597     fn start_a_internal(
598         password: &Password,
599         id_a: &Identity,
600         id_b: &Identity,
601         xy_scalar: G::Scalar,
602     ) -> (SPAKE2<G>, Vec<u8>) {
603         Self::start_internal(
604             Side::A,
605             password,
606             id_a,
607             id_b,
608             &Identity::new(b""),
609             xy_scalar,
610         )
611     }
612
613     fn start_b_internal(
614         password: &Password,
615         id_a: &Identity,
616         id_b: &Identity,
617         xy_scalar: G::Scalar,
618     ) -> (SPAKE2<G>, Vec<u8>) {
619         Self::start_internal(
620             Side::B,
621             password,
622             id_a,
623             id_b,
624             &Identity::new(b""),
625             xy_scalar,
626         )
627     }
628
629     fn start_symmetric_internal(
630         password: &Password,
631         id_s: &Identity,
632         xy_scalar: G::Scalar,
633     ) -> (SPAKE2<G>, Vec<u8>) {
634         Self::start_internal(
635             Side::Symmetric,
636             password,
637             &Identity::new(b""),
638             &Identity::new(b""),
639             id_s,
640             xy_scalar,
641         )
642     }
643
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)
648     }
649
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)
654     }
655
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)
660     }
661
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,
666             });
667         }
668         let msg_side = msg2[0];
669
670         match self.side {
671             Side::A => match msg_side {
672                 0x42 => (), // 'B'
673                 _ => {
674                     return Err(SPAKEErr {
675                         kind: ErrorType::BadSide,
676                     })
677                 }
678             },
679             Side::B => match msg_side {
680                 0x41 => (), // 'A'
681                 _ => {
682                     return Err(SPAKEErr {
683                         kind: ErrorType::BadSide,
684                     })
685                 }
686             },
687             Side::Symmetric => match msg_side {
688                 0x53 => (), // 'S'
689                 _ => {
690                     return Err(SPAKEErr {
691                         kind: ErrorType::BadSide,
692                     })
693                 }
694             },
695         }
696
697         let msg2_element = match G::bytes_to_element(&msg2[1..]) {
698             Some(x) => x,
699             None => {
700                 return Err(SPAKEErr {
701                     kind: ErrorType::CorruptMessage,
702                 })
703             }
704         };
705
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(),
712         };
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);
717
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
724
725         Ok(match self.side {
726             Side::A => ed25519_hash_ab(
727                 &self.password_vec,
728                 &self.id_a,
729                 &self.id_b,
730                 self.msg1.as_slice(),
731                 &msg2[1..],
732                 &key_bytes,
733             ),
734             Side::B => ed25519_hash_ab(
735                 &self.password_vec,
736                 &self.id_a,
737                 &self.id_b,
738                 &msg2[1..],
739                 self.msg1.as_slice(),
740                 &key_bytes,
741             ),
742             Side::Symmetric => ed25519_hash_symmetric(
743                 &self.password_vec,
744                 &self.id_s,
745                 &self.msg1,
746                 &msg2[1..],
747                 &key_bytes,
748             ),
749         })
750     }
751 }
752
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))
761             .finish()
762     }
763 }
764
765 struct MaybeUtf8<'a>(&'a [u8]);
766
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)
771         } else {
772             write!(fmt, "(hex=")?;
773
774             for byte in self.0 {
775                 write!(fmt, "{:x}", byte)?;
776             }
777
778             write!(fmt, ")")
779         }
780     }
781 }
782
783 #[cfg(test)]
784 mod tests;