]> git.lizzy.rs Git - PAKEs.git/blob - spake2/src/lib.rs
2021 edition bump + doc improvements; MSRV 1.56 (#80)
[PAKEs.git] / spake2 / src / lib.rs
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")]
5
6 //! # Usage
7 //!
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
11 //! do the same.
12 //!
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).
19 //!
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.
24 //!
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.
31 //!
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.
35 //!
36 //! Thus a client-side program start with:
37 //!
38 //! ```rust
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);
46 //!
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();
50 //! ```
51 //!
52 //! while the server-side might do:
53 //!
54 //! ```rust
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);
62 //!
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();
66 //! ```
67 //!
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.
72 //!
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.
77 //!
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.
80 //!
81 //! # Symmetric Usage
82 //!
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`.
88 //!
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.
95 //!
96 //! Carol does:
97 //!
98 //! ```rust
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);
105 //!
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();
109 //! ```
110 //!
111 //! Dave does exactly the same:
112 //!
113 //! ```rust
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);
120 //!
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();
124 //! ```
125 //!
126 //! # Identifier Strings
127 //!
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.
131 //!
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).
136 //!
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.
143 //!
144 //! `idA` and `idB` must be bytestrings (slices of `<u8>`).
145 //!
146 //! `start_symmetric` uses a single `idSymmetric=` string, instead of `idA`
147 //! and `idB`. Both sides must provide the same `idSymmetric=`, or leave it
148 //! empty.
149 //!
150 //! # Serialization
151 //!
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.
155 //!
156 //! Unfortunately the Rust implementation does not yet provide serialization
157 //! of the state object. A future version should correct this.
158 //!
159 //! # Security
160 //!
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.
166 //!
167 //! This library depends upon a strong source of random numbers. Do not use it on
168 //! a system where os.urandom() is weak.
169 //!
170 //! # Speed
171 //!
172 //! To run the built-in speed tests, just run `cargo bench`.
173 //!
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.
178 //!
179 //! # Testing
180 //!
181 //! Run `cargo test` to run the built-in test suite.
182 //!
183 //! # History
184 //!
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].
189 //!
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.
194 //!
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
201 //! yet implemented.
202 //!
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.
206 //!
207 //! Brian Warner first wrote the Python version in July 2010. He wrote this
208 //! Rust version in in May 2017.
209 //!
210 //! ### footnotes
211 //!
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"
219
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;
224 use hkdf::Hkdf;
225 use rand::{rngs::OsRng, CryptoRng, Rng};
226 use sha2::{Digest, Sha256};
227 use std::fmt;
228 use std::ops::Deref;
229
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 */
233
234 #[derive(PartialEq, Eq, Clone)]
235 pub struct Password(Vec<u8>);
236 impl Password {
237     pub fn new(p: &[u8]) -> Password {
238         Password(p.to_vec())
239     }
240 }
241 impl Deref for Password {
242     type Target = Vec<u8>;
243     fn deref(&self) -> &Vec<u8> {
244         &self.0
245     }
246 }
247
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> {
253         &self.0
254     }
255 }
256 impl Identity {
257     pub fn new(p: &[u8]) -> Identity {
258         Identity(p.to_vec())
259     }
260 }
261
262 #[derive(Debug, PartialEq, Eq)]
263 pub enum ErrorType {
264     BadSide,
265     WrongLength,
266     CorruptMessage,
267 }
268
269 #[derive(Debug, PartialEq, Eq)]
270 pub struct SPAKEErr {
271     pub kind: ErrorType,
272 }
273
274 pub trait Group {
275     type Scalar;
276     type Element;
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
281     type TranscriptHash;
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
288     where
289         T: Rng + CryptoRng;
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;
297 }
298
299 #[derive(Debug, PartialEq, Eq)]
300 pub struct Ed25519Group;
301
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];
307     //type ScalarBytes
308     type TranscriptHash = Sha256;
309
310     fn name() -> &'static str {
311         "Ed25519"
312     }
313
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
317         CompressedEdwardsY([
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,
321         ])
322         .decompress()
323         .unwrap()
324     }
325
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
329         CompressedEdwardsY([
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,
333         ])
334         .decompress()
335         .unwrap()
336     }
337
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
341         CompressedEdwardsY([
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,
345         ])
346         .decompress()
347         .unwrap()
348     }
349
350     fn hash_to_scalar(s: &[u8]) -> c2_Scalar {
351         ed25519_hash_to_scalar(s)
352     }
353     fn random_scalar<T>(cspring: &mut T) -> c2_Scalar
354     where
355         T: Rng + CryptoRng,
356     {
357         c2_Scalar::random(cspring)
358     }
359     fn scalar_neg(s: &c2_Scalar) -> c2_Scalar {
360         -s
361     }
362     fn element_to_bytes(s: &c2_Element) -> Vec<u8> {
363         s.compress().as_bytes().to_vec()
364     }
365     fn element_length() -> usize {
366         32
367     }
368     fn bytes_to_element(b: &[u8]) -> Option<c2_Element> {
369         if b.len() != 32 {
370             return None;
371         }
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)
377         cey.decompress()
378     }
379
380     fn basepoint_mult(s: &c2_Scalar) -> c2_Element {
381         //c2_Element::basepoint_mult(s)
382         ED25519_BASEPOINT_POINT * s
383     }
384     fn scalarmult(e: &c2_Element, s: &c2_Scalar) -> c2_Element {
385         e * s
386         //e.scalar_mult(s)
387     }
388     fn add(a: &c2_Element, b: &c2_Element) -> c2_Element {
389         a + b
390         //a.add(b)
391     }
392 }
393
394 fn ed25519_hash_to_scalar(s: &[u8]) -> c2_Scalar {
395     //c2_Scalar::hash_from_bytes::<Sha512>(&s)
396     // spake2.py does:
397     //  h = HKDF(salt=b"", ikm=s, hash=SHA256, info=b"SPAKE2 pw", len=32+16)
398     //  i = int(h, 16)
399     //  i % q
400
401     let mut okm = [0u8; 32 + 16];
402     Hkdf::<Sha256>::new(Some(b""), s)
403         .expand(b"SPAKE2 pw", &mut okm)
404         .unwrap();
405     //println!("expanded:   {}{}", "................................", okm.iter().to_hex()); // ok
406
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;
410     }
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());
415     //println!("done");
416     //reduced
417 }
418
419 fn ed25519_hash_ab(
420     password_vec: &[u8],
421     id_a: &[u8],
422     id_b: &[u8],
423     first_msg: &[u8],
424     second_msg: &[u8],
425     key_bytes: &[u8],
426 ) -> Vec<u8> {
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];
437
438     let mut pw_hash = Sha256::new();
439     pw_hash.update(password_vec);
440     transcript[0..32].copy_from_slice(&pw_hash.finalize());
441
442     let mut ida_hash = Sha256::new();
443     ida_hash.update(id_a);
444     transcript[32..64].copy_from_slice(&ida_hash.finalize());
445
446     let mut idb_hash = Sha256::new();
447     idb_hash.update(id_b);
448     transcript[64..96].copy_from_slice(&idb_hash.finalize());
449
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);
453
454     //println!("transcript: {:?}", transcript.iter().to_hex());
455
456     //let mut hash = G::TranscriptHash::default();
457     let mut hash = Sha256::new();
458     hash.update(transcript.to_vec());
459     hash.finalize().to_vec()
460 }
461
462 fn ed25519_hash_symmetric(
463     password_vec: &[u8],
464     id_s: &[u8],
465     msg_u: &[u8],
466     msg_v: &[u8],
467     key_bytes: &[u8],
468 ) -> Vec<u8> {
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])
476
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];
484
485     let mut pw_hash = Sha256::new();
486     pw_hash.update(password_vec);
487     transcript[0..32].copy_from_slice(&pw_hash.finalize());
488
489     let mut ids_hash = Sha256::new();
490     ids_hash.update(id_s);
491     transcript[32..64].copy_from_slice(&ids_hash.finalize());
492
493     if msg_u < msg_v {
494         transcript[64..96].copy_from_slice(msg_u);
495         transcript[96..128].copy_from_slice(msg_v);
496     } else {
497         transcript[64..96].copy_from_slice(msg_v);
498         transcript[96..128].copy_from_slice(msg_u);
499     }
500     transcript[128..160].copy_from_slice(key_bytes);
501
502     let mut hash = Sha256::new();
503     hash.update(transcript.to_vec());
504     hash.finalize().to_vec()
505 }
506
507 /* "session type pattern" */
508
509 #[derive(Debug, PartialEq, Eq)]
510 enum Side {
511     A,
512     B,
513     Symmetric,
514 }
515
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 {
520     side: Side,
521     xy_scalar: G::Scalar,
522     password_vec: Vec<u8>,
523     id_a: Vec<u8>,
524     id_b: Vec<u8>,
525     id_s: Vec<u8>,
526     msg1: Vec<u8>,
527     password_scalar: G::Scalar,
528 }
529
530 impl<G: Group> SPAKE2<G> {
531     fn start_internal(
532         side: Side,
533         password: &Password,
534         id_a: &Identity,
535         id_b: &Identity,
536         id_s: &Identity,
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);
541
542         // a: X = B*x + M*pw
543         // b: Y = B*y + N*pw
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(),
549         };
550         let m1: G::Element = G::add(
551             &G::basepoint_mult(&xy_scalar),
552             &G::scalarmult(&blinding, &password_scalar),
553         );
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);
564
565         let mut msg_and_side = vec![match side {
566             Side::A => 0x41,         // 'A'
567             Side::B => 0x42,         // 'B'
568             Side::Symmetric => 0x53, // 'S'
569         }];
570         msg_and_side.extend_from_slice(&msg1);
571
572         (
573             SPAKE2 {
574                 side,
575                 xy_scalar,
576                 password_vec, // string
577                 id_a: id_a_copy,
578                 id_b: id_b_copy,
579                 id_s: id_s_copy,
580                 msg1,
581                 password_scalar, // scalar
582             },
583             msg_and_side,
584         )
585     }
586
587     fn start_a_internal(
588         password: &Password,
589         id_a: &Identity,
590         id_b: &Identity,
591         xy_scalar: G::Scalar,
592     ) -> (SPAKE2<G>, Vec<u8>) {
593         Self::start_internal(
594             Side::A,
595             password,
596             id_a,
597             id_b,
598             &Identity::new(b""),
599             xy_scalar,
600         )
601     }
602
603     fn start_b_internal(
604         password: &Password,
605         id_a: &Identity,
606         id_b: &Identity,
607         xy_scalar: G::Scalar,
608     ) -> (SPAKE2<G>, Vec<u8>) {
609         Self::start_internal(
610             Side::B,
611             password,
612             id_a,
613             id_b,
614             &Identity::new(b""),
615             xy_scalar,
616         )
617     }
618
619     fn start_symmetric_internal(
620         password: &Password,
621         id_s: &Identity,
622         xy_scalar: G::Scalar,
623     ) -> (SPAKE2<G>, Vec<u8>) {
624         Self::start_internal(
625             Side::Symmetric,
626             password,
627             &Identity::new(b""),
628             &Identity::new(b""),
629             id_s,
630             xy_scalar,
631         )
632     }
633
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)
638     }
639
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)
644     }
645
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)
650     }
651
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,
656             });
657         }
658         let msg_side = msg2[0];
659
660         match self.side {
661             Side::A => match msg_side {
662                 0x42 => (), // 'B'
663                 _ => {
664                     return Err(SPAKEErr {
665                         kind: ErrorType::BadSide,
666                     })
667                 }
668             },
669             Side::B => match msg_side {
670                 0x41 => (), // 'A'
671                 _ => {
672                     return Err(SPAKEErr {
673                         kind: ErrorType::BadSide,
674                     })
675                 }
676             },
677             Side::Symmetric => match msg_side {
678                 0x53 => (), // 'S'
679                 _ => {
680                     return Err(SPAKEErr {
681                         kind: ErrorType::BadSide,
682                     })
683                 }
684             },
685         }
686
687         let msg2_element = match G::bytes_to_element(&msg2[1..]) {
688             Some(x) => x,
689             None => {
690                 return Err(SPAKEErr {
691                     kind: ErrorType::CorruptMessage,
692                 })
693             }
694         };
695
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(),
702         };
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);
707
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
714
715         Ok(match self.side {
716             Side::A => ed25519_hash_ab(
717                 &self.password_vec,
718                 &self.id_a,
719                 &self.id_b,
720                 self.msg1.as_slice(),
721                 &msg2[1..],
722                 &key_bytes,
723             ),
724             Side::B => ed25519_hash_ab(
725                 &self.password_vec,
726                 &self.id_a,
727                 &self.id_b,
728                 &msg2[1..],
729                 self.msg1.as_slice(),
730                 &key_bytes,
731             ),
732             Side::Symmetric => ed25519_hash_symmetric(
733                 &self.password_vec,
734                 &self.id_s,
735                 &self.msg1,
736                 &msg2[1..],
737                 &key_bytes,
738             ),
739         })
740     }
741 }
742
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)),
747     }
748 }
749
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))
758             .finish()
759     }
760 }
761
762 #[cfg(test)]
763 mod tests;