diff options
Diffstat (limited to 'spake2')
-rw-r--r-- | spake2/Cargo.toml | 10 | ||||
-rw-r--r-- | spake2/README.md | 142 | ||||
-rw-r--r-- | spake2/src/lib.rs | 120 | ||||
-rw-r--r-- | spake2/src/tests.rs | 2 | ||||
-rw-r--r-- | spake2/tests/spake2.rs (renamed from spake2/tests/mod.rs) | 0 |
5 files changed, 144 insertions, 130 deletions
diff --git a/spake2/Cargo.toml b/spake2/Cargo.toml index 4436183..5df4705 100644 --- a/spake2/Cargo.toml +++ b/spake2/Cargo.toml @@ -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" diff --git a/spake2/README.md b/spake2/README.md index 50fa5bf..e2cd59f 100644 --- a/spake2/README.md +++ b/spake2/README.md @@ -1,29 +1,115 @@ -# 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 diff --git a/spake2/src/lib.rs b/spake2/src/lib.rs index 562f389..5980b95 100644 --- a/spake2/src/lib.rs +++ b/spake2/src/lib.rs @@ -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 @@ -139,7 +72,7 @@ //! //! 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 @@ -284,10 +217,6 @@ //! [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> { diff --git a/spake2/src/tests.rs b/spake2/src/tests.rs index 0328266..e452fce 100644 --- a/spake2/src/tests.rs +++ b/spake2/src/tests.rs @@ -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/spake2.rs index 07ba946..07ba946 100644 --- a/spake2/tests/mod.rs +++ b/spake2/tests/spake2.rs |