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