strategy:
matrix:
rust:
- - 1.41.0 # MSRV
+ - 1.56.0 # MSRV
- stable
steps:
- uses: actions/checkout@v2
strategy:
matrix:
rust:
- - 1.41.0 # MSRV
+ - 1.56.0 # MSRV
- stable
steps:
- uses: actions/checkout@v2
- 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
[[package]]
name = "spake2"
-version = "0.2.1-alpha.0"
+version = "0.3.0-pre"
dependencies = [
"bencher",
"curve25519-dalek",
[[package]]
name = "srp"
-version = "0.5.0"
+version = "0.6.0-pre"
dependencies = [
"digest 0.9.0",
"generic-array 0.14.4",
[workspace]
+resolver = "2"
members = [
"srp",
"spake2",
[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"
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"
-# 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
-//! 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;
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
//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);
(
) -> (SPAKE2<G>, Vec<u8>) {
Self::start_internal(
Side::A,
- &password,
- &id_a,
- &id_b,
+ password,
+ id_a,
+ id_b,
&Identity::new(b""),
xy_scalar,
)
) -> (SPAKE2<G>, Vec<u8>) {
Self::start_internal(
Side::B,
- &password,
- &id_a,
- &id_b,
+ password,
+ id_a,
+ id_b,
&Identity::new(b""),
xy_scalar,
)
) -> (SPAKE2<G>, Vec<u8>) {
Self::start_internal(
Side::Symmetric,
- &password,
+ password,
&Identity::new(b""),
&Identity::new(b""),
- &id_s,
+ id_s,
xy_scalar,
)
}
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> {
//! "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
+++ /dev/null
-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);
-}
--- /dev/null
+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);
+}
[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"
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"
--- /dev/null
+# [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
-//! [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`:
//!
//! [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;
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
+++ /dev/null
-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(®_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");
-}
--- /dev/null
+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(®_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");
+}