diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b2576a9..d2f21a7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -70,7 +70,7 @@ jobs: steps: - uses: actions/checkout@v2 - uses: hecrj/setup-rust-action@v1 - - run: cargo test --verbose --features slow-hash --no-default-features --features ${{ matrix.backend_feature }} + - run: cargo test --verbose --features scrypt,argon --no-default-features --features ${{ matrix.backend_feature }} serde-test: name: Test on ${{ matrix.target }} with serde support diff --git a/Cargo.lock b/Cargo.lock index 529e9db..fd6215d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,16 @@ version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" +[[package]] +name = "argon2" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d60f5f3113c903294dc81dd8cf0012963ed4dda8bc931c864e12e175356ff98b" +dependencies = [ + "blake2", + "password-hash", +] + [[package]] name = "atty" version = "0.2.14" @@ -40,6 +50,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "base64ct" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d27fb6b6f1e43147af148af49d49329413ba781aa0d5e10979831c210173b5" + [[package]] name = "bincode" version = "1.3.3" @@ -70,6 +86,17 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "blake2" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a5720225ef5daecf08657f23791354e1685a8c91a4c60c7f3d3b2892f978f4" +dependencies = [ + "crypto-mac 0.8.0", + "digest", + "opaque-debug", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -273,6 +300,16 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "crypto-mac" version = "0.10.0" @@ -449,7 +486,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" dependencies = [ - "crypto-mac", + "crypto-mac 0.10.0", "digest", ] @@ -570,6 +607,7 @@ name = "opaque-ke" version = "0.6.0-pre.1" dependencies = [ "anyhow", + "argon2", "base64", "bincode", "chacha20poly1305", @@ -595,13 +633,24 @@ dependencies = [ "zeroize", ] +[[package]] +name = "password-hash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1a5d4e9c205d2c1ae73b84aab6240e98218c0e72e63b50422cfb2d1ca952282" +dependencies = [ + "base64ct", + "rand_core 0.6.2", + "subtle", +] + [[package]] name = "pbkdf2" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3b8c0d71734018084da0c0354193a5edfb81b20d2d57a92c5b154aefc554a4a" dependencies = [ - "crypto-mac", + "crypto-mac 0.10.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 7749533..126f904 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,13 +11,14 @@ readme = "README.md" [features] default = ["u64_backend", "serialize"] -slow-hash = ["scrypt"] +argon = ["argon2"] bench = [] u64_backend = ["curve25519-dalek/u64_backend"] u32_backend = ["curve25519-dalek/u32_backend"] serialize = ["serde", "base64"] [dependencies] +argon2 = { version = "0.2", optional = true } base64 = { version = "0.13", optional = true } curve25519-dalek = { version = "3.0.0", default-features = false, features = ["std"] } digest = "0.9.0" diff --git a/src/lib.rs b/src/lib.rs index 3cacd08..8e57308 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,7 +33,7 @@ //! //! Note that our choice of slow hashing function in this example, `NoOpHash`, is selected only to ensure //! that the tests execute quickly. A real application should use an actual slow hashing function, such as `scrypt`, -//! which can be enabled through the `slow-hash` feature. See more details in the [features](#features) section. +//! which can be enabled through the `scrypt` feature. See more details in the [features](#features) section. //! //! ## Setup //! To set up the protocol, the server begins by creating a `ServerSetup` object: @@ -718,13 +718,16 @@ //! //! # Features //! -//! - The `slow-hash` feature, when enabled, introduces a dependency on `scrypt` and implements the `SlowHash` trait for `scrypt` +//! - The `scrypt` feature, when enabled, introduces a dependency on `scrypt` and implements the `SlowHash` trait for `scrypt` //! with a set of default parameters. In general, secure instantiations should choose to invoke a memory-hard password //! hashing function when the client's password is expected to have low entropy, instead of relying on [slow_hash::NoOpHash] //! as done in the above example. The more computationally intensive the `SlowHash` function is, the more resistant the server's //! password file records will be against offline dictionary and precomputation attacks; see //! [the OPAQUE paper](https://eprint.iacr.org/2018/163.pdf) for more details. //! +//! - The `argon` feature, when enabled, introduces a dependency on `argon2` and implements the `SlowHash` trait for `argon2` +//! with default parameters. This is an alternative to `scrypt`. +//! //! - The `serialize` feature, enabled by default, provides convenience functions for serializing and deserializing with //! [serde](https://serde.rs/). //! diff --git a/src/slow_hash.rs b/src/slow_hash.rs index 1e0c726..b60ffd9 100644 --- a/src/slow_hash.rs +++ b/src/slow_hash.rs @@ -7,7 +7,7 @@ use crate::{errors::InternalPakeError, hash::Hash}; use digest::Digest; -#[cfg(feature = "slow-hash")] +#[cfg(any(feature = "argon", feature = "scrypt"))] use generic_array::typenum::Unsigned; use generic_array::GenericArray; @@ -30,14 +30,14 @@ impl SlowHash for NoOpHash { } } -#[cfg(feature = "slow-hash")] +#[cfg(feature = "scrypt")] const DEFAULT_SCRYPT_LOG_N: u8 = 15u8; -#[cfg(feature = "slow-hash")] +#[cfg(feature = "scrypt")] const DEFAULT_SCRYPT_R: u32 = 8u32; -#[cfg(feature = "slow-hash")] +#[cfg(feature = "scrypt")] const DEFAULT_SCRYPT_P: u32 = 1u32; -#[cfg(feature = "slow-hash")] +#[cfg(feature = "scrypt")] impl SlowHash for scrypt::ScryptParams { fn hash( input: GenericArray::OutputSize>, @@ -51,3 +51,23 @@ impl SlowHash for scrypt::ScryptParams { Ok(output) } } + +#[cfg(feature = "argon")] +impl SlowHash for argon2::Argon2<'_> { + fn hash( + input: GenericArray::OutputSize>, + ) -> Result, InternalPakeError> { + let params = argon2::Argon2::default(); + let mut output = vec![0u8; ::OutputSize::to_usize()]; + params + .hash_password_into( + argon2::Algorithm::Argon2id, + &input, + &[0; argon2::MIN_SALT_LENGTH], + &[], + &mut output, + ) + .map_err(|_| InternalPakeError::SlowHashError)?; + Ok(output) + } +}