]> git.lizzy.rs Git - PAKEs.git/blob - spake2/src/lib.rs
spake2: refactor internals (#91)
[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 mod ed25519;
234 mod error;
235 mod group;
236
237 pub use self::{
238     ed25519::Ed25519Group,
239     error::{Error, Result},
240     group::Group,
241 };
242
243 use alloc::vec::Vec;
244 use core::{fmt, ops::Deref, str};
245 use curve25519_dalek::{edwards::EdwardsPoint as c2_Element, scalar::Scalar as c2_Scalar};
246 use rand_core::{CryptoRng, RngCore};
247
248 #[cfg(feature = "getrandom")]
249 use rand_core::OsRng;
250
251 /// Password
252 // TODO(tarcieri): avoid allocation?
253 #[derive(PartialEq, Eq, Clone)]
254 pub struct Password(Vec<u8>);
255
256 impl Password {
257     /// Create a new password.
258     pub fn new(p: impl AsRef<[u8]>) -> Password {
259         Password(p.as_ref().to_vec())
260     }
261 }
262
263 impl Deref for Password {
264     type Target = Vec<u8>;
265
266     fn deref(&self) -> &Vec<u8> {
267         &self.0
268     }
269 }
270
271 /// SPAKE2 identity.
272 // TODO(tarcieri): avoid allocation?
273 #[derive(PartialEq, Eq, Clone)]
274 pub struct Identity(Vec<u8>);
275
276 impl Deref for Identity {
277     type Target = Vec<u8>;
278
279     fn deref(&self) -> &Vec<u8> {
280         &self.0
281     }
282 }
283
284 impl Identity {
285     /// Create a new identity.
286     pub fn new(p: &[u8]) -> Identity {
287         Identity(p.to_vec())
288     }
289 }
290
291 /// Session type identifying the "side" in a SPAKE2 exchange.
292 #[derive(Debug, PartialEq, Eq)]
293 enum Side {
294     A,
295     B,
296     Symmetric,
297 }
298
299 /// SPAKE2 algorithm.
300 #[derive(Eq, PartialEq)]
301 pub struct Spake2<G: Group> {
302     //where &G::Scalar: Neg {
303     side: Side,
304     xy_scalar: G::Scalar,
305     password_vec: Vec<u8>,
306     id_a: Vec<u8>,
307     id_b: Vec<u8>,
308     id_s: Vec<u8>,
309     msg1: Vec<u8>,
310     password_scalar: G::Scalar,
311 }
312
313 impl<G: Group> Spake2<G> {
314     /// Start with identity `idA`.
315     ///
316     /// Uses the system RNG.
317     #[cfg(feature = "getrandom")]
318     #[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))]
319     pub fn start_a(password: &Password, id_a: &Identity, id_b: &Identity) -> (Spake2<G>, Vec<u8>) {
320         Self::start_a_with_rng(password, id_a, id_b, OsRng)
321     }
322
323     /// Start with identity `idB`.
324     ///
325     /// Uses the system RNG.
326     #[cfg(feature = "getrandom")]
327     #[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))]
328     pub fn start_b(password: &Password, id_a: &Identity, id_b: &Identity) -> (Spake2<G>, Vec<u8>) {
329         Self::start_b_with_rng(password, id_a, id_b, OsRng)
330     }
331
332     /// Start with symmetric identity.
333     ///
334     /// Uses the system RNG.
335     #[cfg(feature = "getrandom")]
336     #[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))]
337     pub fn start_symmetric(password: &Password, id_s: &Identity) -> (Spake2<G>, Vec<u8>) {
338         Self::start_symmetric_with_rng(password, id_s, OsRng)
339     }
340
341     /// Start with identity `idA` and the provided cryptographically secure RNG.
342     pub fn start_a_with_rng(
343         password: &Password,
344         id_a: &Identity,
345         id_b: &Identity,
346         mut csrng: impl CryptoRng + RngCore,
347     ) -> (Spake2<G>, Vec<u8>) {
348         let xy_scalar: G::Scalar = G::random_scalar(&mut csrng);
349         Self::start_a_internal(password, id_a, id_b, xy_scalar)
350     }
351
352     /// Start with identity `idB` and the provided cryptographically secure RNG.
353     pub fn start_b_with_rng(
354         password: &Password,
355         id_a: &Identity,
356         id_b: &Identity,
357         mut csrng: impl CryptoRng + RngCore,
358     ) -> (Spake2<G>, Vec<u8>) {
359         let xy_scalar: G::Scalar = G::random_scalar(&mut csrng);
360         Self::start_b_internal(password, id_a, id_b, xy_scalar)
361     }
362
363     /// Start with symmetric identity and the provided cryptographically secure RNG.
364     pub fn start_symmetric_with_rng(
365         password: &Password,
366         id_s: &Identity,
367         mut csrng: impl CryptoRng + RngCore,
368     ) -> (Spake2<G>, Vec<u8>) {
369         let xy_scalar: G::Scalar = G::random_scalar(&mut csrng);
370         Self::start_symmetric_internal(password, id_s, xy_scalar)
371     }
372
373     /// Finish SPAKE2.
374     pub fn finish(self, msg2: &[u8]) -> Result<Vec<u8>> {
375         if msg2.len() != 1 + G::element_length() {
376             return Err(Error::WrongLength);
377         }
378         let msg_side = msg2[0];
379
380         match self.side {
381             Side::A => match msg_side {
382                 0x42 => (), // 'B'
383                 _ => return Err(Error::BadSide),
384             },
385             Side::B => match msg_side {
386                 0x41 => (), // 'A'
387                 _ => return Err(Error::BadSide),
388             },
389             Side::Symmetric => match msg_side {
390                 0x53 => (), // 'S'
391                 _ => return Err(Error::BadSide),
392             },
393         }
394
395         let msg2_element = match G::bytes_to_element(&msg2[1..]) {
396             Some(x) => x,
397             None => return Err(Error::CorruptMessage),
398         };
399
400         // a: K = (Y+N*(-pw))*x
401         // b: K = (X+M*(-pw))*y
402         let unblinding = match self.side {
403             Side::A => G::const_n(),
404             Side::B => G::const_m(),
405             Side::Symmetric => G::const_s(),
406         };
407         let tmp1 = G::scalarmult(&unblinding, &G::scalar_neg(&self.password_scalar));
408         let tmp2 = G::add(&msg2_element, &tmp1);
409         let key_element = G::scalarmult(&tmp2, &self.xy_scalar);
410         let key_bytes = G::element_to_bytes(&key_element);
411
412         // key = H(H(pw) + H(idA) + H(idB) + X + Y + K)
413         //transcript = b"".join([sha256(pw).digest(),
414         //                       sha256(idA).digest(), sha256(idB).digest(),
415         //                       X_msg, Y_msg, K_bytes])
416         //key = sha256(transcript).digest()
417         // note that both sides must use the same order
418
419         Ok(match self.side {
420             Side::A => ed25519::hash_ab(
421                 &self.password_vec,
422                 &self.id_a,
423                 &self.id_b,
424                 self.msg1.as_slice(),
425                 &msg2[1..],
426                 &key_bytes,
427             ),
428             Side::B => ed25519::hash_ab(
429                 &self.password_vec,
430                 &self.id_a,
431                 &self.id_b,
432                 &msg2[1..],
433                 self.msg1.as_slice(),
434                 &key_bytes,
435             ),
436             Side::Symmetric => ed25519::hash_symmetric(
437                 &self.password_vec,
438                 &self.id_s,
439                 &self.msg1,
440                 &msg2[1..],
441                 &key_bytes,
442             ),
443         })
444     }
445
446     fn start_internal(
447         side: Side,
448         password: &Password,
449         id_a: &Identity,
450         id_b: &Identity,
451         id_s: &Identity,
452         xy_scalar: G::Scalar,
453     ) -> (Spake2<G>, Vec<u8>) {
454         //let password_scalar: G::Scalar = hash_to_scalar::<G::Scalar>(password);
455         let password_scalar: G::Scalar = G::hash_to_scalar(password);
456
457         // a: X = B*x + M*pw
458         // b: Y = B*y + N*pw
459         // sym: X = B*x * S*pw
460         let blinding = match side {
461             Side::A => G::const_m(),
462             Side::B => G::const_n(),
463             Side::Symmetric => G::const_s(),
464         };
465         let m1: G::Element = G::add(
466             &G::basepoint_mult(&xy_scalar),
467             &G::scalarmult(&blinding, &password_scalar),
468         );
469         //let m1: G::Element = &G::basepoint_mult(&x) + &(blinding * &password_scalar);
470         let msg1: Vec<u8> = G::element_to_bytes(&m1);
471         let mut password_vec = Vec::new();
472         password_vec.extend_from_slice(password);
473         let mut id_a_copy = Vec::new();
474         id_a_copy.extend_from_slice(id_a);
475         let mut id_b_copy = Vec::new();
476         id_b_copy.extend_from_slice(id_b);
477         let mut id_s_copy = Vec::new();
478         id_s_copy.extend_from_slice(id_s);
479
480         let mut msg_and_side = vec![match side {
481             Side::A => 0x41,         // 'A'
482             Side::B => 0x42,         // 'B'
483             Side::Symmetric => 0x53, // 'S'
484         }];
485         msg_and_side.extend_from_slice(&msg1);
486
487         (
488             Spake2 {
489                 side,
490                 xy_scalar,
491                 password_vec, // string
492                 id_a: id_a_copy,
493                 id_b: id_b_copy,
494                 id_s: id_s_copy,
495                 msg1,
496                 password_scalar, // scalar
497             },
498             msg_and_side,
499         )
500     }
501
502     fn start_a_internal(
503         password: &Password,
504         id_a: &Identity,
505         id_b: &Identity,
506         xy_scalar: G::Scalar,
507     ) -> (Spake2<G>, Vec<u8>) {
508         Self::start_internal(
509             Side::A,
510             password,
511             id_a,
512             id_b,
513             &Identity::new(b""),
514             xy_scalar,
515         )
516     }
517
518     fn start_b_internal(
519         password: &Password,
520         id_a: &Identity,
521         id_b: &Identity,
522         xy_scalar: G::Scalar,
523     ) -> (Spake2<G>, Vec<u8>) {
524         Self::start_internal(
525             Side::B,
526             password,
527             id_a,
528             id_b,
529             &Identity::new(b""),
530             xy_scalar,
531         )
532     }
533
534     fn start_symmetric_internal(
535         password: &Password,
536         id_s: &Identity,
537         xy_scalar: G::Scalar,
538     ) -> (Spake2<G>, Vec<u8>) {
539         Self::start_internal(
540             Side::Symmetric,
541             password,
542             &Identity::new(b""),
543             &Identity::new(b""),
544             id_s,
545             xy_scalar,
546         )
547     }
548 }
549
550 impl<G: Group> fmt::Debug for Spake2<G> {
551     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
552         fmt.debug_struct("SPAKE2")
553             .field("group", &G::name())
554             .field("side", &self.side)
555             .field("idA", &MaybeUtf8(&self.id_a))
556             .field("idB", &MaybeUtf8(&self.id_b))
557             .field("idS", &MaybeUtf8(&self.id_s))
558             .finish()
559     }
560 }
561
562 struct MaybeUtf8<'a>(&'a [u8]);
563
564 impl fmt::Debug for MaybeUtf8<'_> {
565     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
566         if let Ok(s) = str::from_utf8(self.0) {
567             write!(fmt, "(s={})", s)
568         } else {
569             write!(fmt, "(hex=")?;
570
571             for byte in self.0 {
572                 write!(fmt, "{:x}", byte)?;
573             }
574
575             write!(fmt, ")")
576         }
577     }
578 }
579
580 /// This compares results against the python compatibility tests:
581 /// spake2.test.test_compat.SPAKE2.test_asymmetric . The python test passes a
582 /// deterministic RNG (used only for tests, of course) into the per-Group
583 /// "random_scalar()" function, which results in some particular scalar.
584 #[cfg(all(test, feature = "std"))]
585 mod tests {
586     use crate::*;
587     use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
588     use num_bigint::BigUint;
589
590     // the python tests show the long-integer form of scalars. the rust code
591     // wants an array of bytes (little-endian). Make sure the way we convert
592     // things works correctly.
593     fn decimal_to_scalar(d: &[u8]) -> c2_Scalar {
594         let bytes = BigUint::parse_bytes(d, 10).unwrap().to_bytes_le();
595         assert_eq!(bytes.len(), 32);
596         let mut b2 = [0u8; 32];
597         b2.copy_from_slice(&bytes);
598         c2_Scalar::from_bytes_mod_order(b2)
599     }
600
601     #[test]
602     fn test_convert() {
603         let t1_decimal =
604             b"2238329342913194256032495932344128051776374960164957527413114840482143558222";
605         let t1_scalar = decimal_to_scalar(t1_decimal);
606         let t1_bytes = t1_scalar.to_bytes();
607         let expected = [
608             0x4e, 0x5a, 0xb4, 0x34, 0x5d, 0x47, 0x08, 0x84, 0x59, 0x13, 0xb4, 0x64, 0x1b, 0xc2,
609             0x7d, 0x52, 0x52, 0xa5, 0x85, 0x10, 0x1b, 0xcc, 0x42, 0x44, 0xd4, 0x49, 0xf4, 0xa8,
610             0x79, 0xd9, 0xf2, 0x04,
611         ];
612         assert_eq!(t1_bytes, expected);
613         //println!("t1_scalar is {:?}", t1_scalar);
614     }
615
616     #[test]
617     fn test_serialize_basepoint() {
618         // make sure elements are serialized same as the python library
619         let exp = "5866666666666666666666666666666666666666666666666666666666666666";
620         let base_vec = ED25519_BASEPOINT_POINT.compress().as_bytes().to_vec();
621         let base_hex = hex::encode(base_vec);
622         println!("exp: {:?}", exp);
623         println!("got: {:?}", base_hex);
624         assert_eq!(exp, base_hex);
625     }
626
627     #[test]
628     fn test_password_to_scalar() {
629         let password = Password::new(b"password");
630         let expected_pw_scalar = decimal_to_scalar(
631             b"3515301705789368674385125653994241092664323519848410154015274772661223168839",
632         );
633         let pw_scalar = Ed25519Group::hash_to_scalar(&password);
634         println!("exp: {:?}", hex::encode(expected_pw_scalar.as_bytes()));
635         println!("got: {:?}", hex::encode(pw_scalar.as_bytes()));
636         assert_eq!(&pw_scalar, &expected_pw_scalar);
637     }
638
639     #[test]
640     fn test_sizes() {
641         let (s1, msg1) = Spake2::<Ed25519Group>::start_a(
642             &Password::new(b"password"),
643             &Identity::new(b"idA"),
644             &Identity::new(b"idB"),
645         );
646         assert_eq!(msg1.len(), 1 + 32);
647         let (s2, msg2) = Spake2::<Ed25519Group>::start_b(
648             &Password::new(b"password"),
649             &Identity::new(b"idA"),
650             &Identity::new(b"idB"),
651         );
652         assert_eq!(msg2.len(), 1 + 32);
653         let key1 = s1.finish(&msg2).unwrap();
654         let key2 = s2.finish(&msg1).unwrap();
655         assert_eq!(key1.len(), 32);
656         assert_eq!(key2.len(), 32);
657
658         let (s1, msg1) = Spake2::<Ed25519Group>::start_symmetric(
659             &Password::new(b"password"),
660             &Identity::new(b"idS"),
661         );
662         assert_eq!(msg1.len(), 1 + 32);
663         let (s2, msg2) = Spake2::<Ed25519Group>::start_symmetric(
664             &Password::new(b"password"),
665             &Identity::new(b"idS"),
666         );
667         assert_eq!(msg2.len(), 1 + 32);
668         let key1 = s1.finish(&msg2).unwrap();
669         let key2 = s2.finish(&msg1).unwrap();
670         assert_eq!(key1.len(), 32);
671         assert_eq!(key2.len(), 32);
672     }
673
674     #[test]
675     fn test_hash_ab() {
676         let key = ed25519::hash_ab(
677             b"pw",
678             b"idA",
679             b"idB",
680             b"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", // len=32
681             b"YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY",
682             b"KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK",
683         );
684         let expected_key = "d59d9ba920f7092565cec747b08d5b2e981d553ac32fde0f25e5b4a4cfca3efd";
685         assert_eq!(hex::encode(key), expected_key);
686     }
687
688     #[test]
689     fn test_hash_symmetric() {
690         let key = ed25519::hash_symmetric(
691             b"pw",
692             b"idSymmetric",
693             b"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
694             b"YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY",
695             b"KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK",
696         );
697         let expected_key = "b0b31e4401aae37d91a9a8bf6fbb1298cafc005ff9142e3ffc5b9799fb11128b";
698         assert_eq!(hex::encode(key), expected_key);
699     }
700
701     #[test]
702     fn test_asymmetric() {
703         let scalar_a = decimal_to_scalar(
704             b"2611694063369306139794446498317402240796898290761098242657700742213257926693",
705         );
706         let scalar_b = decimal_to_scalar(
707             b"7002393159576182977806091886122272758628412261510164356026361256515836884383",
708         );
709         let expected_pw_scalar = decimal_to_scalar(
710             b"3515301705789368674385125653994241092664323519848410154015274772661223168839",
711         );
712
713         println!("scalar_a is {}", hex::encode(scalar_a.as_bytes()));
714
715         let (s1, msg1) = Spake2::<Ed25519Group>::start_a_internal(
716             &Password::new(b"password"),
717             &Identity::new(b"idA"),
718             &Identity::new(b"idB"),
719             scalar_a,
720         );
721         let expected_msg1 = "416fc960df73c9cf8ed7198b0c9534e2e96a5984bfc5edc023fd24dacf371f2af9";
722
723         println!();
724         println!("xys1: {:?}", hex::encode(s1.xy_scalar.as_bytes()));
725         println!();
726         println!("pws1: {:?}", hex::encode(s1.password_scalar.as_bytes()));
727         println!("exp : {:?}", hex::encode(expected_pw_scalar.as_bytes()));
728         println!();
729         println!("msg1: {:?}", hex::encode(&msg1));
730         println!("exp : {:?}", expected_msg1);
731         println!();
732
733         assert_eq!(
734             hex::encode(expected_pw_scalar.as_bytes()),
735             hex::encode(s1.password_scalar.as_bytes())
736         );
737         assert_eq!(hex::encode(&msg1), expected_msg1);
738
739         let (s2, msg2) = Spake2::<Ed25519Group>::start_b_internal(
740             &Password::new(b"password"),
741             &Identity::new(b"idA"),
742             &Identity::new(b"idB"),
743             scalar_b,
744         );
745         assert_eq!(expected_pw_scalar, s2.password_scalar);
746         assert_eq!(
747             hex::encode(&msg2),
748             "42354e97b88406922b1df4bea1d7870f17aed3dba7c720b313edae315b00959309"
749         );
750
751         let key1 = s1.finish(&msg2).unwrap();
752         let key2 = s2.finish(&msg1).unwrap();
753         assert_eq!(key1, key2);
754         assert_eq!(
755             hex::encode(key1),
756             "712295de7219c675ddd31942184aa26e0a957cf216bc230d165b215047b520c1"
757         );
758     }
759
760     #[test]
761     fn test_debug() {
762         let (s1, _msg1) = Spake2::<Ed25519Group>::start_a(
763             &Password::new(b"password"),
764             &Identity::new(b"idA"),
765             &Identity::new(b"idB"),
766         );
767         println!("s1: {:?}", s1);
768         assert_eq!(
769             format!("{:?}", s1),
770             "SPAKE2 { group: \"Ed25519\", side: A, idA: (s=idA), idB: (s=idB), idS: (s=) }"
771         );
772
773         let (s2, _msg1) = Spake2::<Ed25519Group>::start_symmetric(
774             &Password::new(b"password"),
775             &Identity::new(b"idS"),
776         );
777         println!("s2: {:?}", s2);
778         assert_eq!(
779             format!("{:?}", s2),
780             "SPAKE2 { group: \"Ed25519\", side: Symmetric, idA: (s=), idB: (s=), idS: (s=idS) }"
781         );
782     }
783 }