]> git.lizzy.rs Git - PAKEs.git/blob - spake2/src/lib.rs
9e31fd4f6b3a8a18f392b0d6a0e3faf7a7cba1c4
[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(missing_docs, 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 /// SPAKE2 errors.
248 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
249 pub enum Error {
250     /// Bad side
251     BadSide,
252
253     /// Corrupt message
254     CorruptMessage,
255
256     /// Wrong length
257     WrongLength,
258 }
259
260 impl fmt::Display for Error {
261     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
262         match self {
263             Error::BadSide => fmt.write_str("bad side"),
264             Error::CorruptMessage => fmt.write_str("corrupt message"),
265             Error::WrongLength => fmt.write_str("invalid length"),
266         }
267     }
268 }
269
270 #[cfg(feature = "std")]
271 impl std::error::Error for Error {}
272
273 /// Password.
274 // TODO(tarcieri): avoid allocation?
275 #[derive(PartialEq, Eq, Clone)]
276 pub struct Password(Vec<u8>);
277
278 impl Password {
279     /// Create a new password
280     pub fn new(p: &[u8]) -> Password {
281         Password(p.to_vec())
282     }
283 }
284
285 impl Deref for Password {
286     type Target = Vec<u8>;
287
288     fn deref(&self) -> &Vec<u8> {
289         &self.0
290     }
291 }
292
293 /// SPAKE2 identity.
294 // TODO(tarcieri): avoid allocation?
295 #[derive(PartialEq, Eq, Clone)]
296 pub struct Identity(Vec<u8>);
297
298 impl Deref for Identity {
299     type Target = Vec<u8>;
300
301     fn deref(&self) -> &Vec<u8> {
302         &self.0
303     }
304 }
305
306 impl Identity {
307     /// Create a new identity
308     pub fn new(p: &[u8]) -> Identity {
309         Identity(p.to_vec())
310     }
311 }
312
313 /// Group trait
314 // TODO(tarcieri): replace with `group` crate?
315 pub trait Group {
316     /// Scalar element
317     type Scalar;
318
319     /// Base field element
320     type Element;
321
322     /// Transcript hash
323     type TranscriptHash;
324
325     /// Name
326     fn name() -> &'static str;
327
328     /// `m` constant
329     fn const_m() -> Self::Element;
330
331     /// `n` constant
332     fn const_n() -> Self::Element;
333
334     /// `s` constant
335     fn const_s() -> Self::Element;
336
337     /// Hash to scalar
338     fn hash_to_scalar(s: &[u8]) -> Self::Scalar;
339
340     /// Generate a random scalar
341     fn random_scalar<T>(cspring: &mut T) -> Self::Scalar
342     where
343         T: RngCore + CryptoRng;
344
345     /// Scalar negation
346     fn scalar_neg(s: &Self::Scalar) -> Self::Scalar;
347
348     /// Convert base field element to bytes
349     fn element_to_bytes(e: &Self::Element) -> Vec<u8>;
350
351     /// Convert bytes to base field element
352     fn bytes_to_element(b: &[u8]) -> Option<Self::Element>;
353
354     /// Length of a base field element
355     fn element_length() -> usize;
356
357     /// Fixed-base scalar multiplication
358     fn basepoint_mult(s: &Self::Scalar) -> Self::Element;
359
360     /// Variable-base scalar multiplication
361     fn scalarmult(e: &Self::Element, s: &Self::Scalar) -> Self::Element;
362
363     /// Group operation
364     fn add(a: &Self::Element, b: &Self::Element) -> Self::Element;
365 }
366
367 /// Ed25519 elliptic curve group
368 #[derive(Debug, PartialEq, Eq)]
369 pub struct Ed25519Group;
370
371 impl Group for Ed25519Group {
372     type Scalar = c2_Scalar;
373     type Element = c2_Element;
374     type TranscriptHash = Sha256;
375
376     fn name() -> &'static str {
377         "Ed25519"
378     }
379
380     fn const_m() -> c2_Element {
381         // 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)]))"
382         // 15cfd18e385952982b6a8f8c7854963b58e34388c8e6dae891db756481a02312
383         CompressedEdwardsY([
384             0x15, 0xcf, 0xd1, 0x8e, 0x38, 0x59, 0x52, 0x98, 0x2b, 0x6a, 0x8f, 0x8c, 0x78, 0x54,
385             0x96, 0x3b, 0x58, 0xe3, 0x43, 0x88, 0xc8, 0xe6, 0xda, 0xe8, 0x91, 0xdb, 0x75, 0x64,
386             0x81, 0xa0, 0x23, 0x12,
387         ])
388         .decompress()
389         .unwrap()
390     }
391
392     fn const_n() -> c2_Element {
393         // 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)]))"
394         // f04f2e7eb734b2a8f8b472eaf9c3c632576ac64aea650b496a8a20ff00e583c3
395         CompressedEdwardsY([
396             0xf0, 0x4f, 0x2e, 0x7e, 0xb7, 0x34, 0xb2, 0xa8, 0xf8, 0xb4, 0x72, 0xea, 0xf9, 0xc3,
397             0xc6, 0x32, 0x57, 0x6a, 0xc6, 0x4a, 0xea, 0x65, 0x0b, 0x49, 0x6a, 0x8a, 0x20, 0xff,
398             0x00, 0xe5, 0x83, 0xc3,
399         ])
400         .decompress()
401         .unwrap()
402     }
403
404     fn const_s() -> c2_Element {
405         // 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)]))"
406         // 6f00dae87c1be1a73b5922ef431cd8f57879569c222d22b1cd71e8546ab8e6f1
407         CompressedEdwardsY([
408             0x6f, 0x00, 0xda, 0xe8, 0x7c, 0x1b, 0xe1, 0xa7, 0x3b, 0x59, 0x22, 0xef, 0x43, 0x1c,
409             0xd8, 0xf5, 0x78, 0x79, 0x56, 0x9c, 0x22, 0x2d, 0x22, 0xb1, 0xcd, 0x71, 0xe8, 0x54,
410             0x6a, 0xb8, 0xe6, 0xf1,
411         ])
412         .decompress()
413         .unwrap()
414     }
415
416     fn hash_to_scalar(s: &[u8]) -> c2_Scalar {
417         ed25519_hash_to_scalar(s)
418     }
419
420     fn random_scalar<T>(cspring: &mut T) -> c2_Scalar
421     where
422         T: RngCore + CryptoRng,
423     {
424         c2_Scalar::random(cspring)
425     }
426
427     fn scalar_neg(s: &c2_Scalar) -> c2_Scalar {
428         -s
429     }
430
431     fn element_to_bytes(s: &c2_Element) -> Vec<u8> {
432         s.compress().as_bytes().to_vec()
433     }
434
435     fn element_length() -> usize {
436         32
437     }
438
439     fn bytes_to_element(b: &[u8]) -> Option<c2_Element> {
440         if b.len() != 32 {
441             return None;
442         }
443
444         let mut bytes = [0u8; 32];
445         bytes.copy_from_slice(b);
446
447         let cey = CompressedEdwardsY(bytes);
448         cey.decompress()
449     }
450
451     fn basepoint_mult(s: &c2_Scalar) -> c2_Element {
452         ED25519_BASEPOINT_POINT * s
453     }
454     fn scalarmult(e: &c2_Element, s: &c2_Scalar) -> c2_Element {
455         e * s
456     }
457
458     fn add(a: &c2_Element, b: &c2_Element) -> c2_Element {
459         a + b
460     }
461 }
462
463 fn ed25519_hash_to_scalar(s: &[u8]) -> c2_Scalar {
464     //c2_Scalar::hash_from_bytes::<Sha512>(&s)
465     // spake2.py does:
466     //  h = HKDF(salt=b"", ikm=s, hash=SHA256, info=b"SPAKE2 pw", len=32+16)
467     //  i = int(h, 16)
468     //  i % q
469
470     let mut okm = [0u8; 32 + 16];
471     Hkdf::<Sha256>::new(Some(b""), s)
472         .expand(b"SPAKE2 pw", &mut okm)
473         .unwrap();
474     //println!("expanded:   {}{}", "................................", okm.iter().to_hex()); // ok
475
476     let mut reducible = [0u8; 64]; // little-endian
477     for (i, x) in okm.iter().enumerate().take(32 + 16) {
478         reducible[32 + 16 - 1 - i] = *x;
479     }
480     //println!("reducible:  {}", reducible.iter().to_hex());
481     c2_Scalar::from_bytes_mod_order_wide(&reducible)
482     //let reduced = c2_Scalar::reduce(&reducible);
483     //println!("reduced:    {}", reduced.as_bytes().to_hex());
484     //println!("done");
485     //reduced
486 }
487
488 fn ed25519_hash_ab(
489     password_vec: &[u8],
490     id_a: &[u8],
491     id_b: &[u8],
492     first_msg: &[u8],
493     second_msg: &[u8],
494     key_bytes: &[u8],
495 ) -> Vec<u8> {
496     assert_eq!(first_msg.len(), 32);
497     assert_eq!(second_msg.len(), 32);
498     // the transcript is fixed-length, made up of 6 32-byte values:
499     // byte 0-31   : sha256(pw)
500     // byte 32-63  : sha256(idA)
501     // byte 64-95  : sha256(idB)
502     // byte 96-127 : X_msg
503     // byte 128-159: Y_msg
504     // byte 160-191: K_bytes
505     let mut transcript = [0u8; 6 * 32];
506
507     let mut pw_hash = Sha256::new();
508     pw_hash.update(password_vec);
509     transcript[0..32].copy_from_slice(&pw_hash.finalize());
510
511     let mut ida_hash = Sha256::new();
512     ida_hash.update(id_a);
513     transcript[32..64].copy_from_slice(&ida_hash.finalize());
514
515     let mut idb_hash = Sha256::new();
516     idb_hash.update(id_b);
517     transcript[64..96].copy_from_slice(&idb_hash.finalize());
518
519     transcript[96..128].copy_from_slice(first_msg);
520     transcript[128..160].copy_from_slice(second_msg);
521     transcript[160..192].copy_from_slice(key_bytes);
522
523     //println!("transcript: {:?}", transcript.iter().to_hex());
524
525     //let mut hash = G::TranscriptHash::default();
526     let mut hash = Sha256::new();
527     hash.update(transcript.to_vec());
528     hash.finalize().to_vec()
529 }
530
531 fn ed25519_hash_symmetric(
532     password_vec: &[u8],
533     id_s: &[u8],
534     msg_u: &[u8],
535     msg_v: &[u8],
536     key_bytes: &[u8],
537 ) -> Vec<u8> {
538     assert_eq!(msg_u.len(), 32);
539     assert_eq!(msg_v.len(), 32);
540     // # since we don't know which side is which, we must sort the messages
541     // first_msg, second_msg = sorted([msg1, msg2])
542     // transcript = b"".join([sha256(pw).digest(),
543     //                        sha256(idSymmetric).digest(),
544     //                        first_msg, second_msg, K_bytes])
545
546     // the transcript is fixed-length, made up of 5 32-byte values:
547     // byte 0-31   : sha256(pw)
548     // byte 32-63  : sha256(idSymmetric)
549     // byte 64-95  : X_msg
550     // byte 96-127 : Y_msg
551     // byte 128-159: K_bytes
552     let mut transcript = [0u8; 5 * 32];
553
554     let mut pw_hash = Sha256::new();
555     pw_hash.update(password_vec);
556     transcript[0..32].copy_from_slice(&pw_hash.finalize());
557
558     let mut ids_hash = Sha256::new();
559     ids_hash.update(id_s);
560     transcript[32..64].copy_from_slice(&ids_hash.finalize());
561
562     if msg_u < msg_v {
563         transcript[64..96].copy_from_slice(msg_u);
564         transcript[96..128].copy_from_slice(msg_v);
565     } else {
566         transcript[64..96].copy_from_slice(msg_v);
567         transcript[96..128].copy_from_slice(msg_u);
568     }
569     transcript[128..160].copy_from_slice(key_bytes);
570
571     let mut hash = Sha256::new();
572     hash.update(transcript.to_vec());
573     hash.finalize().to_vec()
574 }
575
576 /* "session type pattern" */
577
578 #[derive(Debug, PartialEq, Eq)]
579 enum Side {
580     A,
581     B,
582     Symmetric,
583 }
584
585 /// SPAKE2 algorithm.
586 #[derive(Eq, PartialEq)]
587 pub struct Spake2<G: Group> {
588     //where &G::Scalar: Neg {
589     side: Side,
590     xy_scalar: G::Scalar,
591     password_vec: Vec<u8>,
592     id_a: Vec<u8>,
593     id_b: Vec<u8>,
594     id_s: Vec<u8>,
595     msg1: Vec<u8>,
596     password_scalar: G::Scalar,
597 }
598
599 impl<G: Group> Spake2<G> {
600     fn start_internal(
601         side: Side,
602         password: &Password,
603         id_a: &Identity,
604         id_b: &Identity,
605         id_s: &Identity,
606         xy_scalar: G::Scalar,
607     ) -> (Spake2<G>, Vec<u8>) {
608         //let password_scalar: G::Scalar = hash_to_scalar::<G::Scalar>(password);
609         let password_scalar: G::Scalar = G::hash_to_scalar(password);
610
611         // a: X = B*x + M*pw
612         // b: Y = B*y + N*pw
613         // sym: X = B*x * S*pw
614         let blinding = match side {
615             Side::A => G::const_m(),
616             Side::B => G::const_n(),
617             Side::Symmetric => G::const_s(),
618         };
619         let m1: G::Element = G::add(
620             &G::basepoint_mult(&xy_scalar),
621             &G::scalarmult(&blinding, &password_scalar),
622         );
623         //let m1: G::Element = &G::basepoint_mult(&x) + &(blinding * &password_scalar);
624         let msg1: Vec<u8> = G::element_to_bytes(&m1);
625         let mut password_vec = Vec::new();
626         password_vec.extend_from_slice(password);
627         let mut id_a_copy = Vec::new();
628         id_a_copy.extend_from_slice(id_a);
629         let mut id_b_copy = Vec::new();
630         id_b_copy.extend_from_slice(id_b);
631         let mut id_s_copy = Vec::new();
632         id_s_copy.extend_from_slice(id_s);
633
634         let mut msg_and_side = vec![match side {
635             Side::A => 0x41,         // 'A'
636             Side::B => 0x42,         // 'B'
637             Side::Symmetric => 0x53, // 'S'
638         }];
639         msg_and_side.extend_from_slice(&msg1);
640
641         (
642             Spake2 {
643                 side,
644                 xy_scalar,
645                 password_vec, // string
646                 id_a: id_a_copy,
647                 id_b: id_b_copy,
648                 id_s: id_s_copy,
649                 msg1,
650                 password_scalar, // scalar
651             },
652             msg_and_side,
653         )
654     }
655
656     fn start_a_internal(
657         password: &Password,
658         id_a: &Identity,
659         id_b: &Identity,
660         xy_scalar: G::Scalar,
661     ) -> (Spake2<G>, Vec<u8>) {
662         Self::start_internal(
663             Side::A,
664             password,
665             id_a,
666             id_b,
667             &Identity::new(b""),
668             xy_scalar,
669         )
670     }
671
672     fn start_b_internal(
673         password: &Password,
674         id_a: &Identity,
675         id_b: &Identity,
676         xy_scalar: G::Scalar,
677     ) -> (Spake2<G>, Vec<u8>) {
678         Self::start_internal(
679             Side::B,
680             password,
681             id_a,
682             id_b,
683             &Identity::new(b""),
684             xy_scalar,
685         )
686     }
687
688     fn start_symmetric_internal(
689         password: &Password,
690         id_s: &Identity,
691         xy_scalar: G::Scalar,
692     ) -> (Spake2<G>, Vec<u8>) {
693         Self::start_internal(
694             Side::Symmetric,
695             password,
696             &Identity::new(b""),
697             &Identity::new(b""),
698             id_s,
699             xy_scalar,
700         )
701     }
702
703     /// Start with identity `a`.
704     ///
705     /// Uses the system RNG.
706     #[cfg(feature = "getrandom")]
707     #[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))]
708     pub fn start_a(password: &Password, id_a: &Identity, id_b: &Identity) -> (Spake2<G>, Vec<u8>) {
709         Self::start_a_with_rng(password, id_a, id_b, OsRng)
710     }
711
712     /// Start with identity `b`.
713     ///
714     /// Uses the system RNG.
715     #[cfg(feature = "getrandom")]
716     #[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))]
717     pub fn start_b(password: &Password, id_a: &Identity, id_b: &Identity) -> (Spake2<G>, Vec<u8>) {
718         Self::start_b_with_rng(password, id_a, id_b, OsRng)
719     }
720
721     /// Start with symmetric identity.
722     ///
723     /// Uses the system RNG.
724     #[cfg(feature = "getrandom")]
725     #[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))]
726     pub fn start_symmetric(password: &Password, id_s: &Identity) -> (Spake2<G>, Vec<u8>) {
727         Self::start_symmetric_with_rng(password, id_s, OsRng)
728     }
729
730     /// Start with identity `a` and the provided cryptographically secure RNG.
731     pub fn start_a_with_rng(
732         password: &Password,
733         id_a: &Identity,
734         id_b: &Identity,
735         mut csrng: impl CryptoRng + RngCore,
736     ) -> (Spake2<G>, Vec<u8>) {
737         let xy_scalar: G::Scalar = G::random_scalar(&mut csrng);
738         Self::start_a_internal(password, id_a, id_b, xy_scalar)
739     }
740
741     /// Start with identity `b` and the provided cryptographically secure RNG.
742     pub fn start_b_with_rng(
743         password: &Password,
744         id_a: &Identity,
745         id_b: &Identity,
746         mut csrng: impl CryptoRng + RngCore,
747     ) -> (Spake2<G>, Vec<u8>) {
748         let xy_scalar: G::Scalar = G::random_scalar(&mut csrng);
749         Self::start_b_internal(password, id_a, id_b, xy_scalar)
750     }
751
752     /// Start with symmetric identity and the provided cryptographically secure RNG.
753     pub fn start_symmetric_with_rng(
754         password: &Password,
755         id_s: &Identity,
756         mut csrng: impl CryptoRng + RngCore,
757     ) -> (Spake2<G>, Vec<u8>) {
758         let xy_scalar: G::Scalar = G::random_scalar(&mut csrng);
759         Self::start_symmetric_internal(password, id_s, xy_scalar)
760     }
761
762     /// Finish SPAKE2.
763     pub fn finish(self, msg2: &[u8]) -> Result<Vec<u8>, Error> {
764         if msg2.len() != 1 + G::element_length() {
765             return Err(Error::WrongLength);
766         }
767         let msg_side = msg2[0];
768
769         match self.side {
770             Side::A => match msg_side {
771                 0x42 => (), // 'B'
772                 _ => return Err(Error::BadSide),
773             },
774             Side::B => match msg_side {
775                 0x41 => (), // 'A'
776                 _ => return Err(Error::BadSide),
777             },
778             Side::Symmetric => match msg_side {
779                 0x53 => (), // 'S'
780                 _ => return Err(Error::BadSide),
781             },
782         }
783
784         let msg2_element = match G::bytes_to_element(&msg2[1..]) {
785             Some(x) => x,
786             None => return Err(Error::CorruptMessage),
787         };
788
789         // a: K = (Y+N*(-pw))*x
790         // b: K = (X+M*(-pw))*y
791         let unblinding = match self.side {
792             Side::A => G::const_n(),
793             Side::B => G::const_m(),
794             Side::Symmetric => G::const_s(),
795         };
796         let tmp1 = G::scalarmult(&unblinding, &G::scalar_neg(&self.password_scalar));
797         let tmp2 = G::add(&msg2_element, &tmp1);
798         let key_element = G::scalarmult(&tmp2, &self.xy_scalar);
799         let key_bytes = G::element_to_bytes(&key_element);
800
801         // key = H(H(pw) + H(idA) + H(idB) + X + Y + K)
802         //transcript = b"".join([sha256(pw).digest(),
803         //                       sha256(idA).digest(), sha256(idB).digest(),
804         //                       X_msg, Y_msg, K_bytes])
805         //key = sha256(transcript).digest()
806         // note that both sides must use the same order
807
808         Ok(match self.side {
809             Side::A => ed25519_hash_ab(
810                 &self.password_vec,
811                 &self.id_a,
812                 &self.id_b,
813                 self.msg1.as_slice(),
814                 &msg2[1..],
815                 &key_bytes,
816             ),
817             Side::B => ed25519_hash_ab(
818                 &self.password_vec,
819                 &self.id_a,
820                 &self.id_b,
821                 &msg2[1..],
822                 self.msg1.as_slice(),
823                 &key_bytes,
824             ),
825             Side::Symmetric => ed25519_hash_symmetric(
826                 &self.password_vec,
827                 &self.id_s,
828                 &self.msg1,
829                 &msg2[1..],
830                 &key_bytes,
831             ),
832         })
833     }
834 }
835
836 impl<G: Group> fmt::Debug for Spake2<G> {
837     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
838         fmt.debug_struct("SPAKE2")
839             .field("group", &G::name())
840             .field("side", &self.side)
841             .field("idA", &MaybeUtf8(&self.id_a))
842             .field("idB", &MaybeUtf8(&self.id_b))
843             .field("idS", &MaybeUtf8(&self.id_s))
844             .finish()
845     }
846 }
847
848 struct MaybeUtf8<'a>(&'a [u8]);
849
850 impl fmt::Debug for MaybeUtf8<'_> {
851     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
852         if let Ok(s) = str::from_utf8(self.0) {
853             write!(fmt, "(s={})", s)
854         } else {
855             write!(fmt, "(hex=")?;
856
857             for byte in self.0 {
858                 write!(fmt, "{:x}", byte)?;
859             }
860
861             write!(fmt, ")")
862         }
863     }
864 }
865
866 #[cfg(test)]
867 mod tests;