]> git.lizzy.rs Git - PAKEs.git/blob - spake2/src/lib.rs
reformat using rustfmt from latest 1.31.0
[PAKEs.git] / spake2 / src / lib.rs
1 //! An implementation of the [SPAKE2][1] password-authenticated key-exchange
2 //! algorithm
3 //!
4 //! This library implements the SPAKE2 password-authenticated key exchange
5 //! ("PAKE") algorithm. This allows two parties, who share a weak password, to
6 //! safely derive a strong shared secret (and therefore build an
7 //! encrypted+authenticated channel).
8 //!
9 //! A passive attacker who eavesdrops on the connection learns no information
10 //! about the password or the generated secret. An active attacker
11 //! (man-in-the-middle) gets exactly one guess at the password, and unless they
12 //! get it right, they learn no information about the password or the generated
13 //! secret. Each execution of the protocol enables one guess. The use of a weak
14 //! password is made safer by the rate-limiting of guesses: no off-line
15 //! dictionary attack is available to the network-level attacker, and the
16 //! protocol does not depend upon having previously-established confidentiality
17 //! of the network (unlike e.g. sending a plaintext password over TLS).
18 //!
19 //! The protocol requires the exchange of one pair of messages, so only one round
20 //! trip is necessary to establish the session key. If key-confirmation is
21 //! necessary, that will require a second round trip.
22 //!
23 //! All messages are bytestrings. For the default security level (using the
24 //! Ed25519 elliptic curve, roughly equivalent to an 128-bit symmetric key), the
25 //! message is 33 bytes long.
26 //!
27 //! This implementation is generic over a `Group`, which defines the cyclic
28 //! group to use, the functions which convert group elements and scalars to
29 //! and from bytestrings, and the three distinctive group elements used in
30 //! the blinding process. Only one such Group is implemented, named
31 //! `Ed25519Group`, which provides fast operations and high security, and is
32 //! compatible with my [python
33 //! implementation](https://github.com/warner/python-spake2).
34 //!
35 //! # What Is It Good For?
36 //!
37 //! PAKE can be used in a pairing protocol, like the initial version of Firefox
38 //! Sync (the one with J-PAKE), to introduce one device to another and help them
39 //! share secrets. In this mode, one device creates a random code, the user
40 //! copies that code to the second device, then both devices use the code as a
41 //! one-time password and run the PAKE protocol. Once both devices have a shared
42 //! strong key, they can exchange other secrets safely.
43 //!
44 //! PAKE can also be used (carefully) in a login protocol, where SRP is perhaps
45 //! the best-known approach. Traditional non-PAKE login consists of sending a
46 //! plaintext password through a TLS-encrypted channel, to a server which then
47 //! checks it (by hashing/stretching and comparing against a stored verifier). In
48 //! a PAKE login, both sides put the password into their PAKE protocol, and then
49 //! confirm that their generated key is the same. This nominally does not require
50 //! the initial TLS-protected channel. However note that it requires other,
51 //! deeper design considerations (the PAKE protocol must be bound to whatever
52 //! protected channel you end up using, else the attacker can wait for PAKE to
53 //! complete normally and then steal the channel), and is not simply a drop-in
54 //! replacement. In addition, the server cannot hash/stretch the password very
55 //! much (see the note on "Augmented PAKE" below), so unless the client is
56 //! willing to perform key-stretching before running PAKE, the server's stored
57 //! verifier will be vulnerable to a low-cost dictionary attack.
58 //!
59 //! # Usage
60 //!
61 //! Add the `spake2 dependency to your `Cargo.toml`:
62 //!
63 //! ```toml
64 //! [dependencies]
65 //! spake2 = "0.1"
66 //! ```
67 //!
68 //! and this to your crate root:
69 //!
70 //! ```rust
71 //! extern crate spake2;
72 //! ```
73 //!
74 //!
75 //! Alice and Bob both initialize their SPAKE2 instances with the same (weak)
76 //! password. They will exchange messages to (hopefully) derive a shared secret
77 //! key. The protocol is symmetric: for each operation that Alice does, Bob will
78 //! do the same.
79 //!
80 //! However, there are two roles in the SPAKE2 protocol, "A" and "B". The two
81 //! sides must agree ahead of time which one will play which role (the
82 //! messages they generate depend upon which side they play). There are two
83 //! separate constructor functions, `start_a()` and `start_b()`, and a
84 //! complete interaction will use one of each (one `start_a` on one computer,
85 //! and one `start_b` on the other computer).
86 //!
87 //! Each instance of a SPAKE2 protocol uses a set of shared parameters. These
88 //! include a group, a generator, and a pair of arbitrary group elements.
89 //! This library comes a single pre-generated parameter set, but could be
90 //! extended with others.
91 //!
92 //! You start by calling `start_a()` (or `_b)` with the password and identity
93 //! strings for both sides. This gives you back a state object and the first
94 //! message, which you must send to your partner. Once you receive the
95 //! corresponding inbound message, you pass it into the state object
96 //! (consuming both in the process) by calling `s.finish()`, and you get back
97 //! the shared key as a bytestring.
98 //!
99 //! The password and identity strings must each be wrapped in a "newtype",
100 //! which is a simple `struct` that protects against swapping the different
101 //! types of bytestrings.
102 //!
103 //! Thus a client-side program start with:
104 //!
105 //! ```rust
106 //! use spake2::{Ed25519Group, Identity, Password, SPAKE2};
107 //! # fn send(msg: &[u8]) {}
108 //! let (s1, outbound_msg) = SPAKE2::<Ed25519Group>::start_a(
109 //!    &Password::new(b"password"),
110 //!    &Identity::new(b"client id string"),
111 //!    &Identity::new(b"server id string"));
112 //! send(&outbound_msg);
113 //!
114 //! # 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 }
115 //! let inbound_msg = receive();
116 //! let key1 = s1.finish(&inbound_msg).unwrap();
117 //! ```
118 //!
119 //! while the server-side might do:
120 //!
121 //! ```rust
122 //! # fn send(msg: &[u8]) {}
123 //! use spake2::{Ed25519Group, Identity, Password, SPAKE2};
124 //! let (s1, outbound_msg) = SPAKE2::<Ed25519Group>::start_b(
125 //!    &Password::new(b"password"),
126 //!    &Identity::new(b"client id string"),
127 //!    &Identity::new(b"server id string"));
128 //! send(&outbound_msg);
129 //!
130 //! # 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 }
131 //! let inbound_msg = receive();
132 //! let key2 = s1.finish(&inbound_msg).unwrap();
133 //! ```
134 //!
135 //! If both sides used the same password, and there is no man-in-the-middle,
136 //! then `key1` and `key2` will be identical. If not, the two sides will get
137 //! different keys. When one side encrypts with `key1`, and the other side
138 //! attempts to decrypt with `key2`, they'll get nothing but garbled noise.
139 //!
140 //! The shared key can be used as an HMAC key to provide data integrity on
141 //! subsequent messages, or as an authenticated-encryption key (e.g.
142 //! nacl.secretbox). It can also be fed into [HKDF] [1] to derive other
143 //! session keys as necessary.
144 //!
145 //! The `SPAKE2` instances, and the messages they create, are single-use. Create
146 //! a new one for each new session. `finish` consumes the instance.
147 //!
148 //! # Symmetric Usage
149 //!
150 //! A single SPAKE2 instance must be used asymmetrically: the two sides must
151 //! somehow decide (ahead of time) which role they will each play. The
152 //! implementation includes the side identifier in the exchanged message to
153 //! guard against a `start_a` talking to another `start_a`. Typically a
154 //! "client" will take on the `A` role, and the "server" will be `B`.
155 //!
156 //! This is a nuisance for more egalitarian protocols, where there's no clear
157 //! way to assign these roles ahead of time. In this case, use
158 //! `start_symmetric()` on both sides. This uses a different set of
159 //! parameters (so it is not interoperable with `start_A` or `start_b`), but
160 //! should otherwise behave the same way. The symmetric mode uses only one
161 //! identity string, not two.
162 //!
163 //! Carol does:
164 //!
165 //! ```rust
166 //! # fn send(msg: &[u8]) {}
167 //! use spake2::{Ed25519Group, Identity, Password, SPAKE2};
168 //! let (s1, outbound_msg) = SPAKE2::<Ed25519Group>::start_symmetric(
169 //!    &Password::new(b"password"),
170 //!    &Identity::new(b"shared id string"));
171 //! send(&outbound_msg);
172 //!
173 //! # fn receive() -> Vec<u8> { let (s2, i2) = SPAKE2::<Ed25519Group>::start_symmetric(&Password::new(b"password"), &Identity::new(b"shared id string")); i2 }
174 //! let inbound_msg = receive();
175 //! let key1 = s1.finish(&inbound_msg).unwrap();
176 //! ```
177 //!
178 //! Dave does exactly the same:
179 //!
180 //! ```rust
181 //! # fn send(msg: &[u8]) {}
182 //! use spake2::{Ed25519Group, Identity, Password, SPAKE2};
183 //! let (s1, outbound_msg) = SPAKE2::<Ed25519Group>::start_symmetric(
184 //!    &Password::new(b"password"),
185 //!    &Identity::new(b"shared id string"));
186 //! send(&outbound_msg);
187 //!
188 //! # fn receive() -> Vec<u8> { let (s2, i2) = SPAKE2::<Ed25519Group>::start_symmetric(&Password::new(b"password"), &Identity::new(b"shared id string")); i2 }
189 //! let inbound_msg = receive();
190 //! let key1 = s1.finish(&inbound_msg).unwrap();
191 //! ```
192 //!
193 //! # Identifier Strings
194 //!
195 //! The SPAKE2 protocol includes a pair of "identity strings" `idA` and `idB`
196 //! that are included in the final key-derivation hash. This binds the key to a
197 //! single pair of parties, or for some specific purpose.
198 //!
199 //! For example, when user "alice" logs into "example.com", both sides should set
200 //! `idA = b"alice"` and `idB = b"example.com"`. This prevents an attacker from
201 //! substituting messages from unrelated login sessions (other users on the same
202 //! server, or other servers for the same user).
203 //!
204 //! This also makes sure the session is established with the correct service. If
205 //! Alice has one password for "example.com" but uses it for both login and
206 //! file-transfer services, `idB` should be different for the two services.
207 //! Otherwise if Alice is simultaneously connecting to both services, and
208 //! attacker could rearrange the messages and cause her login client to connect
209 //! to the file-transfer server, and vice versa.
210 //!
211 //! `idA` and `idB` must be bytestrings (slices of `<u8>`).
212 //!
213 //! `start_symmetric` uses a single `idSymmetric=` string, instead of `idA`
214 //! and `idB`. Both sides must provide the same `idSymmetric=`, or leave it
215 //! empty.
216 //!
217 //! # Serialization
218 //!
219 //! Sometimes, you can't hold the SPAKE2 instance in memory for the whole
220 //! negotiation: perhaps all your program state is stored in a database, and
221 //! nothing lives in RAM for more than a few moments.
222 //!
223 //! Unfortunately the Rust implementation does not yet provide serialization
224 //! of the state object. A future version should correct this.
225 //!
226 //! # Security
227 //!
228 //! This library is probably not constant-time, and does not protect against
229 //! timing attacks. Do not allow attackers to measure how long it takes you
230 //! to create or respond to a message. This matters somewhat less for pairing
231 //! protocols, because their passwords are single-use randomly-generated
232 //! keys, so an attacker has much less to work with.
233 //!
234 //! This library depends upon a strong source of random numbers. Do not use it on
235 //! a system where os.urandom() is weak.
236 //!
237 //! # Speed
238 //!
239 //! To run the built-in speed tests, just run `cargo bench`.
240 //!
241 //! SPAKE2 consists of two phases, separated by a single message exchange.
242 //! The time these phases take is split roughly 50/50. On my 2.8GHz Core-i7
243 //! (i7-7600U) cpu, the built-in Ed25519Group parameters take about 112
244 //! microseconds for each phase, and the message exchanged is 33 bytes long.
245 //!
246 //! # Testing
247 //!
248 //! Run `cargo test` to run the built-in test suite.
249 //!
250 //! # History
251 //!
252 //! The protocol was described as "PAKE2" in ["cryptobook"] [2] from Dan Boneh
253 //! and Victor Shoup. This is a form of "SPAKE2", defined by Abdalla and
254 //! Pointcheval at [RSA 2005] [3]. Additional recommendations for groups and
255 //! distinguished elements were published in [Ladd's IETF draft] [4].
256 //!
257 //! The Ed25519 implementation uses code adapted from Daniel Bernstein (djb),
258 //! Matthew Dempsky, Daniel Holth, Ron Garret, with further optimizations by
259 //! Brian Warner[5]. The "arbitrary element" computation, which must be the same
260 //! for both participants, is from python-pure25519 version 0.5.
261 //!
262 //! The Boneh/Shoup chapter that defines PAKE2 also defines an augmented variant
263 //! named "PAKE2+", which changes one side (typically a server) to record a
264 //! derivative of the password instead of the actual password. In PAKE2+, a
265 //! server compromise does not immediately give access to the passwords: instead,
266 //! the attacker must perform an offline dictionary attack against the stolen
267 //! data before they can learn the passwords. PAKE2+ support is planned, but not
268 //! yet implemented.
269 //!
270 //! The security of the symmetric case was proved by Kobara/Imai[6] in 2003, and
271 //! uses different (slightly weaker?) reductions than that of the asymmetric
272 //! form. See also Mike Hamburg's analysis[7] from 2015.
273 //!
274 //! Brian Warner first wrote the Python version in July 2010. He wrote this
275 //! Rust version in in May 2017.
276 //!
277 //! ### footnotes
278 //!
279 //! [1]: https://tools.ietf.org/html/rfc5869 "HKDF"
280 //! [2]: http://crypto.stanford.edu/~dabo/cryptobook/  "cryptobook"
281 //! [3]: http://www.di.ens.fr/~pointche/Documents/Papers/2005_rsa.pdf "RSA 2005"
282 //! [4]: https://tools.ietf.org/html/draft-ladd-spake2-01 "Ladd's IETF draft"
283 //! [5]: https://github.com/warner/python-pure25519
284 //! [6]: http://eprint.iacr.org/2003/038.pdf "Pretty-Simple Password-Authenticated Key-Exchange Under Standard Assumptions"
285 //! [7]: https://moderncrypto.org/mail-archive/curves/2015/000419.html "PAKE questions"
286
287 #![doc(html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo_small.png")]
288 #![deny(warnings)]
289 #![forbid(unsafe_code)]
290
291 extern crate curve25519_dalek;
292 extern crate hex;
293 extern crate hkdf;
294 extern crate num_bigint;
295 extern crate rand;
296 extern crate sha2;
297
298 use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
299 use curve25519_dalek::edwards::CompressedEdwardsY;
300 use curve25519_dalek::edwards::EdwardsPoint as c2_Element;
301 use curve25519_dalek::scalar::Scalar as c2_Scalar;
302
303 use hkdf::Hkdf;
304 use rand::{rngs::OsRng, CryptoRng, Rng};
305 use sha2::{Digest, Sha256};
306 use std::fmt;
307 use std::ops::Deref;
308
309 /* "newtype pattern": it's a Vec<u8>, but only used for a specific argument
310  * type, to distinguish between ones that are meant as passwords, and ones
311  * that are meant as identity strings */
312
313 #[derive(PartialEq, Eq, Clone)]
314 pub struct Password(Vec<u8>);
315 impl Password {
316     pub fn new(p: &[u8]) -> Password {
317         Password(p.to_vec())
318     }
319 }
320 impl Deref for Password {
321     type Target = Vec<u8>;
322     fn deref(&self) -> &Vec<u8> {
323         &self.0
324     }
325 }
326
327 #[derive(PartialEq, Eq, Clone)]
328 pub struct Identity(Vec<u8>);
329 impl Deref for Identity {
330     type Target = Vec<u8>;
331     fn deref(&self) -> &Vec<u8> {
332         &self.0
333     }
334 }
335 impl Identity {
336     pub fn new(p: &[u8]) -> Identity {
337         Identity(p.to_vec())
338     }
339 }
340
341 #[derive(Debug, PartialEq, Eq)]
342 pub enum ErrorType {
343     BadSide,
344     WrongLength,
345     CorruptMessage,
346 }
347
348 #[derive(Debug, PartialEq, Eq)]
349 pub struct SPAKEErr {
350     pub kind: ErrorType,
351 }
352
353 pub trait Group {
354     type Scalar;
355     type Element;
356     //type Element: Add<Output=Self::Element>
357     //    + Mul<Self::Scalar, Output=Self::Element>;
358     // const element_length: usize; // in unstable, or u8
359     //type ElementBytes : Index<usize, Output=u8>+IndexMut<usize>; // later
360     type TranscriptHash;
361     fn name() -> &'static str;
362     fn const_m() -> Self::Element;
363     fn const_n() -> Self::Element;
364     fn const_s() -> Self::Element;
365     fn hash_to_scalar(s: &[u8]) -> Self::Scalar;
366     fn random_scalar<T>(cspring: &mut T) -> Self::Scalar
367     where
368         T: Rng + CryptoRng;
369     fn scalar_neg(s: &Self::Scalar) -> Self::Scalar;
370     fn element_to_bytes(e: &Self::Element) -> Vec<u8>;
371     fn bytes_to_element(b: &[u8]) -> Option<Self::Element>;
372     fn element_length() -> usize;
373     fn basepoint_mult(s: &Self::Scalar) -> Self::Element;
374     fn scalarmult(e: &Self::Element, s: &Self::Scalar) -> Self::Element;
375     fn add(a: &Self::Element, b: &Self::Element) -> Self::Element;
376 }
377
378 #[derive(Debug, PartialEq, Eq)]
379 pub struct Ed25519Group;
380
381 impl Group for Ed25519Group {
382     type Scalar = c2_Scalar;
383     type Element = c2_Element;
384     //type ElementBytes = Vec<u8>;
385     //type ElementBytes = [u8; 32];
386     //type ScalarBytes
387     type TranscriptHash = Sha256;
388
389     fn name() -> &'static str {
390         "Ed25519"
391     }
392
393     fn const_m() -> c2_Element {
394         // 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)]))"
395         // 15cfd18e385952982b6a8f8c7854963b58e34388c8e6dae891db756481a02312
396         CompressedEdwardsY([
397             0x15, 0xcf, 0xd1, 0x8e, 0x38, 0x59, 0x52, 0x98, 0x2b, 0x6a, 0x8f, 0x8c, 0x78, 0x54,
398             0x96, 0x3b, 0x58, 0xe3, 0x43, 0x88, 0xc8, 0xe6, 0xda, 0xe8, 0x91, 0xdb, 0x75, 0x64,
399             0x81, 0xa0, 0x23, 0x12,
400         ])
401         .decompress()
402         .unwrap()
403     }
404
405     fn const_n() -> c2_Element {
406         // 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)]))"
407         // f04f2e7eb734b2a8f8b472eaf9c3c632576ac64aea650b496a8a20ff00e583c3
408         CompressedEdwardsY([
409             0xf0, 0x4f, 0x2e, 0x7e, 0xb7, 0x34, 0xb2, 0xa8, 0xf8, 0xb4, 0x72, 0xea, 0xf9, 0xc3,
410             0xc6, 0x32, 0x57, 0x6a, 0xc6, 0x4a, 0xea, 0x65, 0x0b, 0x49, 0x6a, 0x8a, 0x20, 0xff,
411             0x00, 0xe5, 0x83, 0xc3,
412         ])
413         .decompress()
414         .unwrap()
415     }
416
417     fn const_s() -> c2_Element {
418         // 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)]))"
419         // 6f00dae87c1be1a73b5922ef431cd8f57879569c222d22b1cd71e8546ab8e6f1
420         CompressedEdwardsY([
421             0x6f, 0x00, 0xda, 0xe8, 0x7c, 0x1b, 0xe1, 0xa7, 0x3b, 0x59, 0x22, 0xef, 0x43, 0x1c,
422             0xd8, 0xf5, 0x78, 0x79, 0x56, 0x9c, 0x22, 0x2d, 0x22, 0xb1, 0xcd, 0x71, 0xe8, 0x54,
423             0x6a, 0xb8, 0xe6, 0xf1,
424         ])
425         .decompress()
426         .unwrap()
427     }
428
429     fn hash_to_scalar(s: &[u8]) -> c2_Scalar {
430         ed25519_hash_to_scalar(s)
431     }
432     fn random_scalar<T>(cspring: &mut T) -> c2_Scalar
433     where
434         T: Rng + CryptoRng,
435     {
436         c2_Scalar::random(cspring)
437     }
438     fn scalar_neg(s: &c2_Scalar) -> c2_Scalar {
439         -s
440     }
441     fn element_to_bytes(s: &c2_Element) -> Vec<u8> {
442         s.compress().as_bytes().to_vec()
443     }
444     fn element_length() -> usize {
445         32
446     }
447     fn bytes_to_element(b: &[u8]) -> Option<c2_Element> {
448         if b.len() != 32 {
449             return None;
450         }
451         //let mut bytes: [u8; 32] =
452         let mut bytes = [0u8; 32];
453         bytes.copy_from_slice(b);
454         let cey = CompressedEdwardsY(bytes);
455         // CompressedEdwardsY::new(b)
456         cey.decompress()
457     }
458
459     fn basepoint_mult(s: &c2_Scalar) -> c2_Element {
460         //c2_Element::basepoint_mult(s)
461         ED25519_BASEPOINT_POINT * s
462     }
463     fn scalarmult(e: &c2_Element, s: &c2_Scalar) -> c2_Element {
464         e * s
465         //e.scalar_mult(s)
466     }
467     fn add(a: &c2_Element, b: &c2_Element) -> c2_Element {
468         a + b
469         //a.add(b)
470     }
471 }
472
473 fn ed25519_hash_to_scalar(s: &[u8]) -> c2_Scalar {
474     //c2_Scalar::hash_from_bytes::<Sha512>(&s)
475     // spake2.py does:
476     //  h = HKDF(salt=b"", ikm=s, hash=SHA256, info=b"SPAKE2 pw", len=32+16)
477     //  i = int(h, 16)
478     //  i % q
479
480     let mut okm = [0u8; 32 + 16];
481     Hkdf::<Sha256>::extract(Some(b""), s)
482         .expand(b"SPAKE2 pw", &mut okm)
483         .unwrap();
484     //println!("expanded:   {}{}", "................................", okm.iter().to_hex()); // ok
485
486     let mut reducible = [0u8; 64]; // little-endian
487     for (i, x) in okm.iter().enumerate().take(32 + 16) {
488         reducible[32 + 16 - 1 - i] = *x;
489     }
490     //println!("reducible:  {}", reducible.iter().to_hex());
491     c2_Scalar::from_bytes_mod_order_wide(&reducible)
492     //let reduced = c2_Scalar::reduce(&reducible);
493     //println!("reduced:    {}", reduced.as_bytes().to_hex());
494     //println!("done");
495     //reduced
496 }
497
498 fn ed25519_hash_ab(
499     password_vec: &[u8],
500     id_a: &[u8],
501     id_b: &[u8],
502     first_msg: &[u8],
503     second_msg: &[u8],
504     key_bytes: &[u8],
505 ) -> Vec<u8> {
506     assert_eq!(first_msg.len(), 32);
507     assert_eq!(second_msg.len(), 32);
508     // the transcript is fixed-length, made up of 6 32-byte values:
509     // byte 0-31   : sha256(pw)
510     // byte 32-63  : sha256(idA)
511     // byte 64-95  : sha256(idB)
512     // byte 96-127 : X_msg
513     // byte 128-159: Y_msg
514     // byte 160-191: K_bytes
515     let mut transcript = [0u8; 6 * 32];
516
517     let mut pw_hash = Sha256::new();
518     pw_hash.input(password_vec);
519     transcript[0..32].copy_from_slice(&pw_hash.result());
520
521     let mut ida_hash = Sha256::new();
522     ida_hash.input(id_a);
523     transcript[32..64].copy_from_slice(&ida_hash.result());
524
525     let mut idb_hash = Sha256::new();
526     idb_hash.input(id_b);
527     transcript[64..96].copy_from_slice(&idb_hash.result());
528
529     transcript[96..128].copy_from_slice(first_msg);
530     transcript[128..160].copy_from_slice(second_msg);
531     transcript[160..192].copy_from_slice(key_bytes);
532
533     //println!("transcript: {:?}", transcript.iter().to_hex());
534
535     //let mut hash = G::TranscriptHash::default();
536     let mut hash = Sha256::new();
537     hash.input(transcript.to_vec());
538     hash.result().to_vec()
539 }
540
541 fn ed25519_hash_symmetric(
542     password_vec: &[u8],
543     id_s: &[u8],
544     msg_u: &[u8],
545     msg_v: &[u8],
546     key_bytes: &[u8],
547 ) -> Vec<u8> {
548     assert_eq!(msg_u.len(), 32);
549     assert_eq!(msg_v.len(), 32);
550     // # since we don't know which side is which, we must sort the messages
551     // first_msg, second_msg = sorted([msg1, msg2])
552     // transcript = b"".join([sha256(pw).digest(),
553     //                        sha256(idSymmetric).digest(),
554     //                        first_msg, second_msg, K_bytes])
555
556     // the transcript is fixed-length, made up of 5 32-byte values:
557     // byte 0-31   : sha256(pw)
558     // byte 32-63  : sha256(idSymmetric)
559     // byte 64-95  : X_msg
560     // byte 96-127 : Y_msg
561     // byte 128-159: K_bytes
562     let mut transcript = [0u8; 5 * 32];
563
564     let mut pw_hash = Sha256::new();
565     pw_hash.input(password_vec);
566     transcript[0..32].copy_from_slice(&pw_hash.result());
567
568     let mut ids_hash = Sha256::new();
569     ids_hash.input(id_s);
570     transcript[32..64].copy_from_slice(&ids_hash.result());
571
572     if msg_u < msg_v {
573         transcript[64..96].copy_from_slice(msg_u);
574         transcript[96..128].copy_from_slice(msg_v);
575     } else {
576         transcript[64..96].copy_from_slice(msg_v);
577         transcript[96..128].copy_from_slice(msg_u);
578     }
579     transcript[128..160].copy_from_slice(key_bytes);
580
581     let mut hash = Sha256::new();
582     hash.input(transcript.to_vec());
583     hash.result().to_vec()
584 }
585
586 /* "session type pattern" */
587
588 #[derive(Debug, PartialEq, Eq)]
589 enum Side {
590     A,
591     B,
592     Symmetric,
593 }
594
595 // we implement a custom Debug below, to avoid revealing secrets in a dump
596 #[derive(PartialEq, Eq)]
597 pub struct SPAKE2<G: Group> {
598     //where &G::Scalar: Neg {
599     side: Side,
600     xy_scalar: G::Scalar,
601     password_vec: Vec<u8>,
602     id_a: Vec<u8>,
603     id_b: Vec<u8>,
604     id_s: Vec<u8>,
605     msg1: Vec<u8>,
606     password_scalar: G::Scalar,
607 }
608
609 impl<G: Group> SPAKE2<G> {
610     fn start_internal(
611         side: Side,
612         password: &Password,
613         id_a: &Identity,
614         id_b: &Identity,
615         id_s: &Identity,
616         xy_scalar: G::Scalar,
617     ) -> (SPAKE2<G>, Vec<u8>) {
618         //let password_scalar: G::Scalar = hash_to_scalar::<G::Scalar>(password);
619         let password_scalar: G::Scalar = G::hash_to_scalar(&password);
620
621         // a: X = B*x + M*pw
622         // b: Y = B*y + N*pw
623         // sym: X = B*x * S*pw
624         let blinding = match side {
625             Side::A => G::const_m(),
626             Side::B => G::const_n(),
627             Side::Symmetric => G::const_s(),
628         };
629         let m1: G::Element = G::add(
630             &G::basepoint_mult(&xy_scalar),
631             &G::scalarmult(&blinding, &password_scalar),
632         );
633         //let m1: G::Element = &G::basepoint_mult(&x) + &(blinding * &password_scalar);
634         let msg1: Vec<u8> = G::element_to_bytes(&m1);
635         let mut password_vec = Vec::new();
636         password_vec.extend_from_slice(&password);
637         let mut id_a_copy = Vec::new();
638         id_a_copy.extend_from_slice(&id_a);
639         let mut id_b_copy = Vec::new();
640         id_b_copy.extend_from_slice(&id_b);
641         let mut id_s_copy = Vec::new();
642         id_s_copy.extend_from_slice(&id_s);
643
644         let mut msg_and_side = Vec::new();
645         msg_and_side.push(match side {
646             Side::A => 0x41,         // 'A'
647             Side::B => 0x42,         // 'B'
648             Side::Symmetric => 0x53, // 'S'
649         });
650         msg_and_side.extend_from_slice(&msg1);
651
652         (
653             SPAKE2 {
654                 side,
655                 xy_scalar,
656                 password_vec, // string
657                 id_a: id_a_copy,
658                 id_b: id_b_copy,
659                 id_s: id_s_copy,
660                 msg1: msg1.clone(),
661                 password_scalar, // scalar
662             },
663             msg_and_side,
664         )
665     }
666
667     fn start_a_internal(
668         password: &Password,
669         id_a: &Identity,
670         id_b: &Identity,
671         xy_scalar: G::Scalar,
672     ) -> (SPAKE2<G>, Vec<u8>) {
673         Self::start_internal(
674             Side::A,
675             &password,
676             &id_a,
677             &id_b,
678             &Identity::new(b""),
679             xy_scalar,
680         )
681     }
682
683     fn start_b_internal(
684         password: &Password,
685         id_a: &Identity,
686         id_b: &Identity,
687         xy_scalar: G::Scalar,
688     ) -> (SPAKE2<G>, Vec<u8>) {
689         Self::start_internal(
690             Side::B,
691             &password,
692             &id_a,
693             &id_b,
694             &Identity::new(b""),
695             xy_scalar,
696         )
697     }
698
699     fn start_symmetric_internal(
700         password: &Password,
701         id_s: &Identity,
702         xy_scalar: G::Scalar,
703     ) -> (SPAKE2<G>, Vec<u8>) {
704         Self::start_internal(
705             Side::Symmetric,
706             &password,
707             &Identity::new(b""),
708             &Identity::new(b""),
709             &id_s,
710             xy_scalar,
711         )
712     }
713
714     pub fn start_a(password: &Password, id_a: &Identity, id_b: &Identity) -> (SPAKE2<G>, Vec<u8>) {
715         let mut cspring: OsRng = OsRng::new().unwrap();
716         let xy_scalar: G::Scalar = G::random_scalar(&mut cspring);
717         Self::start_a_internal(&password, &id_a, &id_b, xy_scalar)
718     }
719
720     pub fn start_b(password: &Password, id_a: &Identity, id_b: &Identity) -> (SPAKE2<G>, Vec<u8>) {
721         let mut cspring: OsRng = OsRng::new().unwrap();
722         let xy_scalar: G::Scalar = G::random_scalar(&mut cspring);
723         Self::start_b_internal(&password, &id_a, &id_b, xy_scalar)
724     }
725
726     pub fn start_symmetric(password: &Password, id_s: &Identity) -> (SPAKE2<G>, Vec<u8>) {
727         let mut cspring: OsRng = OsRng::new().unwrap();
728         let xy_scalar: G::Scalar = G::random_scalar(&mut cspring);
729         Self::start_symmetric_internal(&password, &id_s, xy_scalar)
730     }
731
732     pub fn finish(self, msg2: &[u8]) -> Result<Vec<u8>, SPAKEErr> {
733         if msg2.len() != 1 + G::element_length() {
734             return Err(SPAKEErr {
735                 kind: ErrorType::WrongLength,
736             });
737         }
738         let msg_side = msg2[0];
739
740         match self.side {
741             Side::A => match msg_side {
742                 0x42 => (), // 'B'
743                 _ => {
744                     return Err(SPAKEErr {
745                         kind: ErrorType::BadSide,
746                     })
747                 }
748             },
749             Side::B => match msg_side {
750                 0x41 => (), // 'A'
751                 _ => {
752                     return Err(SPAKEErr {
753                         kind: ErrorType::BadSide,
754                     })
755                 }
756             },
757             Side::Symmetric => match msg_side {
758                 0x53 => (), // 'S'
759                 _ => {
760                     return Err(SPAKEErr {
761                         kind: ErrorType::BadSide,
762                     })
763                 }
764             },
765         }
766
767         let msg2_element = match G::bytes_to_element(&msg2[1..]) {
768             Some(x) => x,
769             None => {
770                 return Err(SPAKEErr {
771                     kind: ErrorType::CorruptMessage,
772                 })
773             }
774         };
775
776         // a: K = (Y+N*(-pw))*x
777         // b: K = (X+M*(-pw))*y
778         let unblinding = match self.side {
779             Side::A => G::const_n(),
780             Side::B => G::const_m(),
781             Side::Symmetric => G::const_s(),
782         };
783         let tmp1 = G::scalarmult(&unblinding, &G::scalar_neg(&self.password_scalar));
784         let tmp2 = G::add(&msg2_element, &tmp1);
785         let key_element = G::scalarmult(&tmp2, &self.xy_scalar);
786         let key_bytes = G::element_to_bytes(&key_element);
787
788         // key = H(H(pw) + H(idA) + H(idB) + X + Y + K)
789         //transcript = b"".join([sha256(pw).digest(),
790         //                       sha256(idA).digest(), sha256(idB).digest(),
791         //                       X_msg, Y_msg, K_bytes])
792         //key = sha256(transcript).digest()
793         // note that both sides must use the same order
794
795         Ok(match self.side {
796             Side::A => ed25519_hash_ab(
797                 &self.password_vec,
798                 &self.id_a,
799                 &self.id_b,
800                 self.msg1.as_slice(),
801                 &msg2[1..],
802                 &key_bytes,
803             ),
804             Side::B => ed25519_hash_ab(
805                 &self.password_vec,
806                 &self.id_a,
807                 &self.id_b,
808                 &msg2[1..],
809                 self.msg1.as_slice(),
810                 &key_bytes,
811             ),
812             Side::Symmetric => ed25519_hash_symmetric(
813                 &self.password_vec,
814                 &self.id_s,
815                 &self.msg1,
816                 &msg2[1..],
817                 &key_bytes,
818             ),
819         })
820     }
821 }
822
823 fn maybe_utf8(s: &[u8]) -> String {
824     match String::from_utf8(s.to_vec()) {
825         Ok(m) => format!("(s={})", m),
826         Err(_) => format!("(hex={})", hex::encode(s)),
827     }
828 }
829
830 impl<G: Group> fmt::Debug for SPAKE2<G> {
831     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
832         fmt.debug_struct("SPAKE2")
833             .field("group", &G::name())
834             .field("side", &self.side)
835             .field("idA", &maybe_utf8(&self.id_a))
836             .field("idB", &maybe_utf8(&self.id_b))
837             .field("idS", &maybe_utf8(&self.id_s))
838             .finish()
839     }
840 }
841
842 #[cfg(test)]
843 mod tests;