]> git.lizzy.rs Git - PAKEs.git/commitdiff
2021 edition bump + doc improvements; MSRV 1.56 (#80)
authorTony Arcieri <bascule@gmail.com>
Tue, 21 Dec 2021 01:46:23 +0000 (18:46 -0700)
committerGitHub <noreply@github.com>
Tue, 21 Dec 2021 01:46:23 +0000 (18:46 -0700)
- Bumps both `spake2` and `srp` to Rust 2021 edition
- Uses the new `doc = include_str!(...)` attribute to include README.md
  files in the rustdoc
- Improves the README.md files, adding an initial one for `srp`
- clippy fixes for Rust 1.56

17 files changed:
.github/workflows/spake2.yml
.github/workflows/srp.yml
.github/workflows/workspace.yml
Cargo.lock
Cargo.toml
spake2/Cargo.toml
spake2/README.md
spake2/src/lib.rs
spake2/src/tests.rs
spake2/tests/mod.rs [deleted file]
spake2/tests/spake2.rs [new file with mode: 0644]
srp/Cargo.toml
srp/README.md [new file with mode: 0644]
srp/src/lib.rs
srp/src/types.rs
srp/tests/mod.rs [deleted file]
srp/tests/srp.rs [new file with mode: 0644]

index 21ab11597e46a0ab7b3e1449c16122b211519484..e3b6c0c5a9beb27c93091fa763fc4fc060a1bdf4 100644 (file)
@@ -22,7 +22,7 @@ jobs:
     strategy:
       matrix:
         rust:
-          - 1.41.0 # MSRV
+          - 1.56.0 # MSRV
           - stable
     steps:
       - uses: actions/checkout@v2
index d66d135af575d089ef188ebb3fb364eb3eeedacb..4e6485f1ba9e8968e9bfc3e887cc601274b67b7b 100644 (file)
@@ -22,7 +22,7 @@ jobs:
     strategy:
       matrix:
         rust:
-          - 1.41.0 # MSRV
+          - 1.56.0 # MSRV
           - stable
     steps:
       - uses: actions/checkout@v2
index 9117e88727d6fb8eb81ad08e2f3cac40fe2989cb..128fc900291789ba61f12a86c00f96b2d655f1c7 100644 (file)
@@ -16,7 +16,7 @@ jobs:
       - uses: actions/checkout@v1
       - uses: actions-rs/toolchain@v1
         with:
-          toolchain: 1.41.0 # MSRV
+          toolchain: 1.56.0 # MSRV
           components: clippy
           override: true
           profile: minimal
index d5a0eb6f0816adce478f239f4492874e212e7013..5789c25cdbc3a8b1584865b9810c326887ee21ad 100644 (file)
@@ -363,7 +363,7 @@ dependencies = [
 
 [[package]]
 name = "spake2"
-version = "0.2.1-alpha.0"
+version = "0.3.0-pre"
 dependencies = [
  "bencher",
  "curve25519-dalek",
@@ -376,7 +376,7 @@ dependencies = [
 
 [[package]]
 name = "srp"
-version = "0.5.0"
+version = "0.6.0-pre"
 dependencies = [
  "digest 0.9.0",
  "generic-array 0.14.4",
index 0dc3885f6c6709d4b1d4c9b8a3eba3811be8a2d0..14a3005e6cc5b1bb06fb879488535cb1d74ee38c 100644 (file)
@@ -1,4 +1,5 @@
 [workspace]
+resolver = "2"
 members = [
     "srp",
     "spake2",
index 4436183813b713e30808b6de76c9d02d2a8c2333..5df4705b933312f43460915c2191af1e6a04a96c 100644 (file)
@@ -1,7 +1,6 @@
 [package]
 name = "spake2"
-version = "0.2.1-alpha.0"
-edition = "2018"
+version = "0.3.0-pre"
 authors = ["Brian Warner <warner@lothar.com>"]
 description = "The SPAKE2 password-authenticated key-exchange algorithm."
 documentation = "https://docs.rs/spake2"
@@ -10,9 +9,10 @@ repository = "https://github.com/RustCrypto/PAKEs"
 license = "MIT OR Apache-2.0"
 keywords = ["crypto", "pake", "authentication"]
 categories = ["cryptography", "authentication"]
-exclude = [
-    ".gitignore"
-]
+exclude = [".gitignore"]
+readme = "README.md"
+edition = "2018"
+rust-version = "1.56"
 
 [package.metadata.release]
 tag-prefix = "spake2-v"
index 50fa5bff9a8e2cd2cb7b66f61eeaa1e2be6c5c0e..e2cd59f7acc7aaa8bda40119676b94d6013bcee3 100644 (file)
-# spake2.rs
-The SPAKE2 password-authenticated key-exchange algorithm, in Rust.
-
-[![Build Status][build-status-image]][build-status-url]
-[![Codecov][codecov-image]][codecov-url]
-[![Is-It-Maintained-Resolution-Time][iim-resolution-image]][iim-resolution-url]
-[![Is-It-Maintained-Open-Issues][iim-open-image]][iim-open-url]
-[![Crates.io][crates-io-image]][crates-io-url]
-[![Docs.rs][docs-image]][docs-url]
-[![License][license-image]][license-url]
-
-This is still pretty early, but seems to do the job. It needs a proper security review before you should consider using it for anything serious.
-
-Note that the API has changed since 0.0.8 . I released 0.0.9 by mistake.
-
-[build-status-image]: https://travis-ci.org/RustCrypto/PAKEs.svg?branch=master
-[build-status-url]: https://travis-ci.org/RustCrypto/PAKEs
-[codecov-image]: https://codecov.io/gh/RustCrypto/PAKEs/branch/master/graph/badge.svg
-[codecov-url]: https://codecov.io/gh/RustCrypto/PAKEs
-[crates-io-image]: https://img.shields.io/crates/v/spake2.svg
-[crates-io-url]: https://crates.io/crates/spake2
+# [RustCrypto]: SPAKE2
+
+[![crate][crate-image]][crate-link]
+[![Docs][docs-image]][docs-link]
+![Apache2/MIT licensed][license-image]
+![Rust Version][rustc-image]
+[![Project Chat][chat-image]][chat-link]
+[![Build Status][build-image]][build-link]
+
+Pure Rust implementation of the [SPAKE2] password-authenticated key-exchange algorithm.
+
+[Documentation][docs-link]
+
+## About
+
+This library implements the SPAKE2 password-authenticated key exchange
+("PAKE") algorithm. This allows two parties, who share a weak password, to
+safely derive a strong shared secret (and therefore build an
+encrypted+authenticated channel).
+
+A passive attacker who eavesdrops on the connection learns no information
+about the password or the generated secret. An active attacker
+(man-in-the-middle) gets exactly one guess at the password, and unless they
+get it right, they learn no information about the password or the generated
+secret. Each execution of the protocol enables one guess. The use of a weak
+password is made safer by the rate-limiting of guesses: no off-line
+dictionary attack is available to the network-level attacker, and the
+protocol does not depend upon having previously-established confidentiality
+of the network (unlike e.g. sending a plaintext password over TLS).
+
+The protocol requires the exchange of one pair of messages, so only one round
+trip is necessary to establish the session key. If key-confirmation is
+necessary, that will require a second round trip.
+
+All messages are bytestrings. For the default security level (using the
+Ed25519 elliptic curve, roughly equivalent to an 128-bit symmetric key), the
+message is 33 bytes long.
+
+This implementation is generic over a `Group`, which defines the cyclic
+group to use, the functions which convert group elements and scalars to
+and from bytestrings, and the three distinctive group elements used in
+the blinding process. Only one such Group is implemented, named
+`Ed25519Group`, which provides fast operations and high security, and is
+compatible with my [python implementation](https://github.com/warner/python-spake2).
+
+# What Is It Good For?
+
+PAKE can be used in a pairing protocol, like the initial version of Firefox
+Sync (the one with J-PAKE), to introduce one device to another and help them
+share secrets. In this mode, one device creates a random code, the user
+copies that code to the second device, then both devices use the code as a
+one-time password and run the PAKE protocol. Once both devices have a shared
+strong key, they can exchange other secrets safely.
+
+PAKE can also be used (carefully) in a login protocol, where SRP is perhaps
+the best-known approach. Traditional non-PAKE login consists of sending a
+plaintext password through a TLS-encrypted channel, to a server which then
+checks it (by hashing/stretching and comparing against a stored verifier). In
+a PAKE login, both sides put the password into their PAKE protocol, and then
+confirm that their generated key is the same. This nominally does not require
+the initial TLS-protected channel. However note that it requires other,
+deeper design considerations (the PAKE protocol must be bound to whatever
+protected channel you end up using, else the attacker can wait for PAKE to
+complete normally and then steal the channel), and is not simply a drop-in
+replacement. In addition, the server cannot hash/stretch the password very
+much (see the note on "Augmented PAKE" below), so unless the client is
+willing to perform key-stretching before running PAKE, the server's stored
+verifier will be vulnerable to a low-cost dictionary attack.
+
+## ⚠️ Security Warning
+
+This crate has never received an independent third party audit for security and
+correctness.
+
+USE AT YOUR OWN RISK!
+
+## Minimum Supported Rust Version
+
+Rust **1.56** or higher.
+
+Minimum supported Rust version can be changed in the future, but it will be
+done with a minor version bump.
+
+## License
+
+Licensed under either of:
+
+ * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
+ * [MIT license](http://opensource.org/licenses/MIT)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
+dual licensed as above, without any additional terms or conditions.
+
+[//]: # (badges)
+
+[crate-image]: https://img.shields.io/crates/v/spake2.svg
+[crate-link]: https://crates.io/crates/spake2
 [docs-image]: https://docs.rs/spake2/badge.svg
-[docs-url]: https://docs.rs/spake2
-[license-image]: https://img.shields.io/crates/l/spake2.svg
-[license-url]: LICENSE-MIT
-[iim-resolution-image]: http://isitmaintained.com/badge/resolution/RustCrypto/PAKEs.svg
-[iim-resolution-url]: http://isitmaintained.com/project/RustCrypto/PAKEs
-[iim-open-image]: http://isitmaintained.com/badge/open/RustCrypto/PAKEs.svg
-[iim-open-url]: http://isitmaintained.com/project/RustCrypto/PAKEs
+[docs-link]: https://docs.rs/spake2/
+[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
+[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg
+[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg
+[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260045-PAKEs
+[build-image]: https://github.com/RustCrypto/PAKEs/actions/workflows/spake2.yml/badge.svg
+[build-link]: https://github.com/RustCrypto/PAKEs/actions/workflows/spake2.yml
+
+[//]: # (general links)
+
+[RustCrypto]: https://github.com/RustCrypto
+[SPAKE2]: https://tools.ietf.org/id/draft-irtf-cfrg-spake2-10.html
index 562f3896798b3a5c6408e7bc16df94c443c63c56..5980b952cd667cd4c46df6f91ba1757cb02ccd35 100644 (file)
@@ -1,77 +1,10 @@
-//! An implementation of the [SPAKE2][1] password-authenticated key-exchange
-//! algorithm
-//!
-//! This library implements the SPAKE2 password-authenticated key exchange
-//! ("PAKE") algorithm. This allows two parties, who share a weak password, to
-//! safely derive a strong shared secret (and therefore build an
-//! encrypted+authenticated channel).
-//!
-//! A passive attacker who eavesdrops on the connection learns no information
-//! about the password or the generated secret. An active attacker
-//! (man-in-the-middle) gets exactly one guess at the password, and unless they
-//! get it right, they learn no information about the password or the generated
-//! secret. Each execution of the protocol enables one guess. The use of a weak
-//! password is made safer by the rate-limiting of guesses: no off-line
-//! dictionary attack is available to the network-level attacker, and the
-//! protocol does not depend upon having previously-established confidentiality
-//! of the network (unlike e.g. sending a plaintext password over TLS).
-//!
-//! The protocol requires the exchange of one pair of messages, so only one round
-//! trip is necessary to establish the session key. If key-confirmation is
-//! necessary, that will require a second round trip.
-//!
-//! All messages are bytestrings. For the default security level (using the
-//! Ed25519 elliptic curve, roughly equivalent to an 128-bit symmetric key), the
-//! message is 33 bytes long.
-//!
-//! This implementation is generic over a `Group`, which defines the cyclic
-//! group to use, the functions which convert group elements and scalars to
-//! and from bytestrings, and the three distinctive group elements used in
-//! the blinding process. Only one such Group is implemented, named
-//! `Ed25519Group`, which provides fast operations and high security, and is
-//! compatible with my [python
-//! implementation](https://github.com/warner/python-spake2).
-//!
-//! # What Is It Good For?
-//!
-//! PAKE can be used in a pairing protocol, like the initial version of Firefox
-//! Sync (the one with J-PAKE), to introduce one device to another and help them
-//! share secrets. In this mode, one device creates a random code, the user
-//! copies that code to the second device, then both devices use the code as a
-//! one-time password and run the PAKE protocol. Once both devices have a shared
-//! strong key, they can exchange other secrets safely.
-//!
-//! PAKE can also be used (carefully) in a login protocol, where SRP is perhaps
-//! the best-known approach. Traditional non-PAKE login consists of sending a
-//! plaintext password through a TLS-encrypted channel, to a server which then
-//! checks it (by hashing/stretching and comparing against a stored verifier). In
-//! a PAKE login, both sides put the password into their PAKE protocol, and then
-//! confirm that their generated key is the same. This nominally does not require
-//! the initial TLS-protected channel. However note that it requires other,
-//! deeper design considerations (the PAKE protocol must be bound to whatever
-//! protected channel you end up using, else the attacker can wait for PAKE to
-//! complete normally and then steal the channel), and is not simply a drop-in
-//! replacement. In addition, the server cannot hash/stretch the password very
-//! much (see the note on "Augmented PAKE" below), so unless the client is
-//! willing to perform key-stretching before running PAKE, the server's stored
-//! verifier will be vulnerable to a low-cost dictionary attack.
-//!
+#![forbid(unsafe_code)]
+#![warn(rust_2018_idioms, unused_qualifications)]
+#![doc(html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo_small.png")]
+#![doc = include_str!("../README.md")]
+
 //! # Usage
 //!
-//! Add the `spake2 dependency to your `Cargo.toml`:
-//!
-//! ```toml
-//! [dependencies]
-//! spake2 = "0.1"
-//! ```
-//!
-//! and this to your crate root:
-//!
-//! ```rust
-//! extern crate spake2;
-//! ```
-//!
-//!
 //! Alice and Bob both initialize their SPAKE2 instances with the same (weak)
 //! password. They will exchange messages to (hopefully) derive a shared secret
 //! key. The protocol is symmetric: for each operation that Alice does, Bob will
 //!
 //! The shared key can be used as an HMAC key to provide data integrity on
 //! subsequent messages, or as an authenticated-encryption key (e.g.
-//! nacl.secretbox). It can also be fed into [HKDF] [1] to derive other
+//! nacl.secretbox). It can also be fed into [HKDF][1] to derive other
 //! session keys as necessary.
 //!
 //! The `SPAKE2` instances, and the messages they create, are single-use. Create
 //! [6]: http://eprint.iacr.org/2003/038.pdf "Pretty-Simple Password-Authenticated Key-Exchange Under Standard Assumptions"
 //! [7]: https://moderncrypto.org/mail-archive/curves/2015/000419.html "PAKE questions"
 
-#![doc(html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo_small.png")]
-#![deny(warnings)]
-#![forbid(unsafe_code)]
-
 use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
 use curve25519_dalek::edwards::CompressedEdwardsY;
 use curve25519_dalek::edwards::EdwardsPoint as c2_Element;
@@ -608,7 +537,7 @@ impl<G: Group> SPAKE2<G> {
         xy_scalar: G::Scalar,
     ) -> (SPAKE2<G>, Vec<u8>) {
         //let password_scalar: G::Scalar = hash_to_scalar::<G::Scalar>(password);
-        let password_scalar: G::Scalar = G::hash_to_scalar(&password);
+        let password_scalar: G::Scalar = G::hash_to_scalar(password);
 
         // a: X = B*x + M*pw
         // b: Y = B*y + N*pw
@@ -625,20 +554,19 @@ impl<G: Group> SPAKE2<G> {
         //let m1: G::Element = &G::basepoint_mult(&x) + &(blinding * &password_scalar);
         let msg1: Vec<u8> = G::element_to_bytes(&m1);
         let mut password_vec = Vec::new();
-        password_vec.extend_from_slice(&password);
+        password_vec.extend_from_slice(password);
         let mut id_a_copy = Vec::new();
-        id_a_copy.extend_from_slice(&id_a);
+        id_a_copy.extend_from_slice(id_a);
         let mut id_b_copy = Vec::new();
-        id_b_copy.extend_from_slice(&id_b);
+        id_b_copy.extend_from_slice(id_b);
         let mut id_s_copy = Vec::new();
-        id_s_copy.extend_from_slice(&id_s);
+        id_s_copy.extend_from_slice(id_s);
 
-        let mut msg_and_side = Vec::new();
-        msg_and_side.push(match side {
+        let mut msg_and_side = vec![match side {
             Side::A => 0x41,         // 'A'
             Side::B => 0x42,         // 'B'
             Side::Symmetric => 0x53, // 'S'
-        });
+        }];
         msg_and_side.extend_from_slice(&msg1);
 
         (
@@ -664,9 +592,9 @@ impl<G: Group> SPAKE2<G> {
     ) -> (SPAKE2<G>, Vec<u8>) {
         Self::start_internal(
             Side::A,
-            &password,
-            &id_a,
-            &id_b,
+            password,
+            id_a,
+            id_b,
             &Identity::new(b""),
             xy_scalar,
         )
@@ -680,9 +608,9 @@ impl<G: Group> SPAKE2<G> {
     ) -> (SPAKE2<G>, Vec<u8>) {
         Self::start_internal(
             Side::B,
-            &password,
-            &id_a,
-            &id_b,
+            password,
+            id_a,
+            id_b,
             &Identity::new(b""),
             xy_scalar,
         )
@@ -695,10 +623,10 @@ impl<G: Group> SPAKE2<G> {
     ) -> (SPAKE2<G>, Vec<u8>) {
         Self::start_internal(
             Side::Symmetric,
-            &password,
+            password,
             &Identity::new(b""),
             &Identity::new(b""),
-            &id_s,
+            id_s,
             xy_scalar,
         )
     }
@@ -706,19 +634,19 @@ impl<G: Group> SPAKE2<G> {
     pub fn start_a(password: &Password, id_a: &Identity, id_b: &Identity) -> (SPAKE2<G>, Vec<u8>) {
         let mut cspring: OsRng = OsRng::new().unwrap();
         let xy_scalar: G::Scalar = G::random_scalar(&mut cspring);
-        Self::start_a_internal(&password, &id_a, &id_b, xy_scalar)
+        Self::start_a_internal(password, id_a, id_b, xy_scalar)
     }
 
     pub fn start_b(password: &Password, id_a: &Identity, id_b: &Identity) -> (SPAKE2<G>, Vec<u8>) {
         let mut cspring: OsRng = OsRng::new().unwrap();
         let xy_scalar: G::Scalar = G::random_scalar(&mut cspring);
-        Self::start_b_internal(&password, &id_a, &id_b, xy_scalar)
+        Self::start_b_internal(password, id_a, id_b, xy_scalar)
     }
 
     pub fn start_symmetric(password: &Password, id_s: &Identity) -> (SPAKE2<G>, Vec<u8>) {
         let mut cspring: OsRng = OsRng::new().unwrap();
         let xy_scalar: G::Scalar = G::random_scalar(&mut cspring);
-        Self::start_symmetric_internal(&password, &id_s, xy_scalar)
+        Self::start_symmetric_internal(password, id_s, xy_scalar)
     }
 
     pub fn finish(self, msg2: &[u8]) -> Result<Vec<u8>, SPAKEErr> {
index 032826667006e000358fad0b14ad3fa6eec9572b..e452fce6152bc265c9e8000b0b1fb801bf3aaea7 100644 (file)
@@ -4,7 +4,7 @@
 //! "random_scalar()" function, which results in some particular scalar.
 use super::*;
 use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
-use hex;
+
 use num_bigint::BigUint;
 
 // the python tests show the long-integer form of scalars. the rust code
diff --git a/spake2/tests/mod.rs b/spake2/tests/mod.rs
deleted file mode 100644 (file)
index 07ba946..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-use spake2::{Ed25519Group, ErrorType, Identity, Password, SPAKEErr, SPAKE2};
-
-#[test]
-fn test_basic() {
-    let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(
-        &Password::new(b"password"),
-        &Identity::new(b"idA"),
-        &Identity::new(b"idB"),
-    );
-    let (s2, msg2) = SPAKE2::<Ed25519Group>::start_b(
-        &Password::new(b"password"),
-        &Identity::new(b"idA"),
-        &Identity::new(b"idB"),
-    );
-    let key1 = s1.finish(msg2.as_slice()).unwrap();
-    let key2 = s2.finish(msg1.as_slice()).unwrap();
-    assert_eq!(key1, key2);
-}
-
-#[test]
-fn test_mismatch() {
-    let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(
-        &Password::new(b"password"),
-        &Identity::new(b"idA"),
-        &Identity::new(b"idB"),
-    );
-    let (s2, msg2) = SPAKE2::<Ed25519Group>::start_b(
-        &Password::new(b"password2"),
-        &Identity::new(b"idA"),
-        &Identity::new(b"idB"),
-    );
-    let key1 = s1.finish(msg2.as_slice()).unwrap();
-    let key2 = s2.finish(msg1.as_slice()).unwrap();
-    assert_ne!(key1, key2);
-}
-
-#[test]
-fn test_reflected_message() {
-    let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(
-        &Password::new(b"password"),
-        &Identity::new(b"idA"),
-        &Identity::new(b"idB"),
-    );
-    let r = s1.finish(msg1.as_slice());
-    assert_eq!(
-        r.unwrap_err(),
-        SPAKEErr {
-            kind: ErrorType::BadSide,
-        }
-    );
-}
-
-#[test]
-fn test_bad_length() {
-    let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(
-        &Password::new(b"password"),
-        &Identity::new(b"idA"),
-        &Identity::new(b"idB"),
-    );
-    let mut msg2 = Vec::<u8>::with_capacity(msg1.len() + 1);
-    msg2.resize(msg1.len() + 1, 0u8);
-    let r = s1.finish(&msg2);
-    assert_eq!(
-        r.unwrap_err(),
-        SPAKEErr {
-            kind: ErrorType::WrongLength,
-        }
-    );
-}
-
-#[test]
-fn test_basic_symmetric() {
-    let (s1, msg1) = SPAKE2::<Ed25519Group>::start_symmetric(
-        &Password::new(b"password"),
-        &Identity::new(b"idS"),
-    );
-    let (s2, msg2) = SPAKE2::<Ed25519Group>::start_symmetric(
-        &Password::new(b"password"),
-        &Identity::new(b"idS"),
-    );
-    let key1 = s1.finish(msg2.as_slice()).unwrap();
-    let key2 = s2.finish(msg1.as_slice()).unwrap();
-    assert_eq!(key1, key2);
-}
diff --git a/spake2/tests/spake2.rs b/spake2/tests/spake2.rs
new file mode 100644 (file)
index 0000000..07ba946
--- /dev/null
@@ -0,0 +1,84 @@
+use spake2::{Ed25519Group, ErrorType, Identity, Password, SPAKEErr, SPAKE2};
+
+#[test]
+fn test_basic() {
+    let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(
+        &Password::new(b"password"),
+        &Identity::new(b"idA"),
+        &Identity::new(b"idB"),
+    );
+    let (s2, msg2) = SPAKE2::<Ed25519Group>::start_b(
+        &Password::new(b"password"),
+        &Identity::new(b"idA"),
+        &Identity::new(b"idB"),
+    );
+    let key1 = s1.finish(msg2.as_slice()).unwrap();
+    let key2 = s2.finish(msg1.as_slice()).unwrap();
+    assert_eq!(key1, key2);
+}
+
+#[test]
+fn test_mismatch() {
+    let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(
+        &Password::new(b"password"),
+        &Identity::new(b"idA"),
+        &Identity::new(b"idB"),
+    );
+    let (s2, msg2) = SPAKE2::<Ed25519Group>::start_b(
+        &Password::new(b"password2"),
+        &Identity::new(b"idA"),
+        &Identity::new(b"idB"),
+    );
+    let key1 = s1.finish(msg2.as_slice()).unwrap();
+    let key2 = s2.finish(msg1.as_slice()).unwrap();
+    assert_ne!(key1, key2);
+}
+
+#[test]
+fn test_reflected_message() {
+    let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(
+        &Password::new(b"password"),
+        &Identity::new(b"idA"),
+        &Identity::new(b"idB"),
+    );
+    let r = s1.finish(msg1.as_slice());
+    assert_eq!(
+        r.unwrap_err(),
+        SPAKEErr {
+            kind: ErrorType::BadSide,
+        }
+    );
+}
+
+#[test]
+fn test_bad_length() {
+    let (s1, msg1) = SPAKE2::<Ed25519Group>::start_a(
+        &Password::new(b"password"),
+        &Identity::new(b"idA"),
+        &Identity::new(b"idB"),
+    );
+    let mut msg2 = Vec::<u8>::with_capacity(msg1.len() + 1);
+    msg2.resize(msg1.len() + 1, 0u8);
+    let r = s1.finish(&msg2);
+    assert_eq!(
+        r.unwrap_err(),
+        SPAKEErr {
+            kind: ErrorType::WrongLength,
+        }
+    );
+}
+
+#[test]
+fn test_basic_symmetric() {
+    let (s1, msg1) = SPAKE2::<Ed25519Group>::start_symmetric(
+        &Password::new(b"password"),
+        &Identity::new(b"idS"),
+    );
+    let (s2, msg2) = SPAKE2::<Ed25519Group>::start_symmetric(
+        &Password::new(b"password"),
+        &Identity::new(b"idS"),
+    );
+    let key1 = s1.finish(msg2.as_slice()).unwrap();
+    let key2 = s2.finish(msg1.as_slice()).unwrap();
+    assert_eq!(key1, key2);
+}
index 92748e33db3fe7fba85c92822528ec74ba5a857a..9230f8ea909a3ec2ff5b0e2710bc6795c1ed0ab4 100644 (file)
@@ -1,7 +1,6 @@
 [package]
 name = "srp"
-version = "0.5.0"
-edition = "2018"
+version = "0.6.0-pre"
 authors = ["RustCrypto Developers"]
 license = "MIT OR Apache-2.0"
 description = "Secure Remote Password (SRP) protocol implementation"
@@ -9,6 +8,9 @@ documentation = "https://docs.rs/srp"
 repository = "https://github.com/RustCrypto/PAKEs"
 keywords = ["crypto", "pake", "authentication"]
 categories = ["cryptography", "authentication"]
+readme = "README.md"
+edition = "2018"
+rust-version = "1.56"
 
 [dependencies]
 num-bigint = "0.4"
diff --git a/srp/README.md b/srp/README.md
new file mode 100644 (file)
index 0000000..3aa7583
--- /dev/null
@@ -0,0 +1,73 @@
+# [RustCrypto]: SRP
+
+[![crate][crate-image]][crate-link]
+[![Docs][docs-image]][docs-link]
+![Apache2/MIT licensed][license-image]
+![Rust Version][rustc-image]
+[![Project Chat][chat-image]][chat-link]
+[![Build Status][build-image]][build-link]
+
+Pure Rust implementation of the [Secure Remote Password] password-authenticated
+key-exchange algorithm.
+
+[Documentation][docs-link]
+
+## About
+
+This implementation is generic over hash functions using the [`Digest`] trait,
+so you will need to choose a hash  function, e.g. `Sha256` from [`sha2`] crate.
+
+Additionally this crate allows to use a specialized password hashing
+algorithm for private key computation instead of method described in the
+SRP literature.
+
+Compatibility with other implementations has not yet been tested.
+
+## ⚠️ Security Warning
+
+This crate has never received an independent third party audit for security and
+correctness.
+
+USE AT YOUR OWN RISK!
+
+## Minimum Supported Rust Version
+
+Rust **1.56** or higher.
+
+Minimum supported Rust version can be changed in the future, but it will be
+done with a minor version bump.
+
+## License
+
+Licensed under either of:
+
+ * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
+ * [MIT license](http://opensource.org/licenses/MIT)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
+dual licensed as above, without any additional terms or conditions.
+
+[//]: # (badges)
+
+[crate-image]: https://img.shields.io/crates/v/srp.svg
+[crate-link]: https://crates.io/crates/srp
+[docs-image]: https://docs.rs/srp/badge.svg
+[docs-link]: https://docs.rs/srp/
+[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
+[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg
+[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg
+[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260045-PAKEs
+[build-image]: https://github.com/RustCrypto/PAKEs/actions/workflows/srp.yml/badge.svg
+[build-link]: https://github.com/RustCrypto/PAKEs/actions/workflows/srp.yml
+
+[//]: # (general links)
+
+[RustCrypto]: https://github.com/RustCrypto
+[Secure Remote Password]: https://en.wikipedia.org/wiki/Secure_Remote_Password_protocol
+[`Digest`]: https://docs.rs/digest
+[`sha2`]: https://crates.io/crates/sha2
index ada3b8c0f6e2d95d3cd10ee1774459bb28916f39..375dfb3605bc045b8c7d48b5331bc4b8c4ac3638 100644 (file)
@@ -1,14 +1,7 @@
-//! [Secure Remote Password][1] (SRP) protocol implementation.
-//!
-//! This implementation is generic over hash functions using
-//! [`Digest`](https://docs.rs/digest) trait, so you will need to choose a hash
-//! function, e.g. `Sha256` from [`sha2`](https://crates.io/crates/sha2) crate.
-//! Additionally this crate allows to use a specialized password hashing
-//! algorithm for private key computation instead of method described in the
-//! SRP literature.
-//!
-//! Compatibility with other implementations was not yet tested.
-//!
+#![allow(clippy::many_single_char_names)]
+#![doc(html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo_small.png")]
+#![doc = include_str!("../README.md")]
+
 //! # Usage
 //! Add `srp` dependency to your `Cargo.toml`:
 //!
@@ -63,9 +56,6 @@
 //! [1]: https://en.wikipedia.org/wiki/Secure_Remote_Password_protocol
 //! [2]: https://tools.ietf.org/html/rfc5054
 
-#![allow(clippy::many_single_char_names)]
-#![doc(html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo_small.png")]
-
 pub mod client;
 pub mod groups;
 pub mod server;
index 41742d53589af270b1c00bc9ff0e1a45c21ad58a..a23954e35b41ecfe8fee442565f29fc8a099c7d3 100644 (file)
@@ -46,7 +46,7 @@ impl SrpGroup {
         let mut d = D::new();
         d.update(&n);
         d.update(&buf);
-        BigUint::from_bytes_be(&d.finalize().as_slice())
+        BigUint::from_bytes_be(d.finalize().as_slice())
     }
 
     /// Compute `Hash(N) xor Hash(g)` with given hash function and return SRP parameters
diff --git a/srp/tests/mod.rs b/srp/tests/mod.rs
deleted file mode 100644 (file)
index 58bfa0f..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-use rand;
-use rand::RngCore;
-use sha2::Sha256;
-
-use srp::client::{srp_private_key, SrpClient};
-use srp::groups::G_2048;
-use srp::server::{SrpServer, UserRecord};
-
-fn auth_test(reg_pwd: &[u8], auth_pwd: &[u8]) {
-    let mut rng = rand::rngs::OsRng::new().unwrap();
-    let username = b"alice";
-
-    // Client instance creation
-    let mut a = [0u8; 64];
-    rng.fill_bytes(&mut a);
-    let client = SrpClient::<Sha256>::new(&a, &G_2048);
-
-    // Registration
-    let mut salt = [0u8; 16];
-    rng.fill_bytes(&mut salt);
-    let reg_priv_key = srp_private_key::<Sha256>(username, reg_pwd, &salt);
-    let verif = client.get_password_verifier(&reg_priv_key);
-
-    // User sends handshake
-    let a_pub = client.get_a_pub();
-
-    // Server retrieve user record from db and processes handshake
-    let user = UserRecord {
-        username,
-        salt: &salt,
-        verifier: &verif,
-    };
-    let mut b = [0u8; 64];
-    rng.fill_bytes(&mut b);
-    let server = SrpServer::<Sha256>::new(&user, &a_pub, &b, &G_2048).unwrap();
-    let (salt, b_pub) = (&user.salt, server.get_b_pub());
-
-    // Client processes handshake reply
-    let auth_priv_key = srp_private_key::<Sha256>(username, auth_pwd, salt);
-    let client2 = client.process_reply(&auth_priv_key, &b_pub).unwrap();
-    let proof = client2.get_proof();
-
-    // Server processes verification data
-    println!("Client verification");
-    let proof2 = server.verify(&proof).unwrap();
-    let server_key = server.get_key();
-
-    // Client verifies server
-    println!("Server verification");
-    let user_key = client2.verify_server(&proof2).unwrap();
-    assert_eq!(server_key, user_key, "server and client keys are not equal");
-}
-
-#[test]
-fn good_password() {
-    auth_test(b"password", b"password");
-}
-
-#[test]
-#[should_panic]
-fn bad_password() {
-    auth_test(b"password", b"paSsword");
-}
diff --git a/srp/tests/srp.rs b/srp/tests/srp.rs
new file mode 100644 (file)
index 0000000..bf6c30f
--- /dev/null
@@ -0,0 +1,62 @@
+use rand::RngCore;
+use sha2::Sha256;
+
+use srp::client::{srp_private_key, SrpClient};
+use srp::groups::G_2048;
+use srp::server::{SrpServer, UserRecord};
+
+fn auth_test(reg_pwd: &[u8], auth_pwd: &[u8]) {
+    let mut rng = rand::rngs::OsRng::new().unwrap();
+    let username = b"alice";
+
+    // Client instance creation
+    let mut a = [0u8; 64];
+    rng.fill_bytes(&mut a);
+    let client = SrpClient::<Sha256>::new(&a, &G_2048);
+
+    // Registration
+    let mut salt = [0u8; 16];
+    rng.fill_bytes(&mut salt);
+    let reg_priv_key = srp_private_key::<Sha256>(username, reg_pwd, &salt);
+    let verif = client.get_password_verifier(&reg_priv_key);
+
+    // User sends handshake
+    let a_pub = client.get_a_pub();
+
+    // Server retrieve user record from db and processes handshake
+    let user = UserRecord {
+        username,
+        salt: &salt,
+        verifier: &verif,
+    };
+    let mut b = [0u8; 64];
+    rng.fill_bytes(&mut b);
+    let server = SrpServer::<Sha256>::new(&user, &a_pub, &b, &G_2048).unwrap();
+    let (salt, b_pub) = (&user.salt, server.get_b_pub());
+
+    // Client processes handshake reply
+    let auth_priv_key = srp_private_key::<Sha256>(username, auth_pwd, salt);
+    let client2 = client.process_reply(&auth_priv_key, &b_pub).unwrap();
+    let proof = client2.get_proof();
+
+    // Server processes verification data
+    println!("Client verification");
+    let proof2 = server.verify(&proof).unwrap();
+    let server_key = server.get_key();
+
+    // Client verifies server
+    println!("Server verification");
+    let user_key = client2.verify_server(&proof2).unwrap();
+    assert_eq!(server_key, user_key, "server and client keys are not equal");
+}
+
+#[test]
+fn good_password() {
+    auth_test(b"password", b"password");
+}
+
+#[test]
+#[should_panic]
+fn bad_password() {
+    auth_test(b"password", b"paSsword");
+}