diff --git a/Cargo.lock b/Cargo.lock index f74806e..2bfda6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,11 +50,25 @@ dependencies = [ "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "base64" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "base64" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "block-buffer" version = "0.7.3" @@ -118,6 +132,14 @@ dependencies = [ "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crypto-mac" version = "0.7.0" @@ -152,6 +174,11 @@ name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "generic-array" version = "0.12.3" @@ -236,6 +263,7 @@ dependencies = [ "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scrypt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -243,6 +271,15 @@ dependencies = [ "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pbkdf2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "poly1305" version = "0.5.2" @@ -276,6 +313,31 @@ dependencies = [ "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "rand_core" version = "0.5.1" @@ -289,6 +351,26 @@ name = "ryu" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "scrypt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "serde" version = "1.0.105" @@ -396,6 +478,25 @@ name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "x25519-dalek" version = "0.6.0" @@ -432,6 +533,8 @@ dependencies = [ "checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" "checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" "checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" +"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" "checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" "checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" @@ -440,10 +543,12 @@ dependencies = [ "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum chacha20 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bea8b86bdf2f2b18a0f28fbfed740ee395e6ba1785b4b7123c021172eaab8ef9" "checksum chacha20poly1305 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "48901293601228db2131606f741db33561f7576b5d19c99cd66222380a7dc863" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" "checksum curve25519-dalek 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26778518a7f6cffa1d25a44b602b62b979bd88adb9e99ffec546998cf3404839" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" "checksum ghash 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f0930ed19a7184089ea46d2fedead2f6dc2b674c5db4276b7da336c7cd83252" @@ -454,12 +559,18 @@ dependencies = [ "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +"checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" "checksum poly1305 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5829f50f48e9ddb79f3f7c3097029d0caee30f8286accb241416df603b080b8" "checksum polyval 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ec3341498978de3bfd12d1b22f1af1de22818f5473a11e8a6ef997989e3a212" "checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" +"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" "checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" "checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" +"checksum safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" +"checksum scrypt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "656c79d0e90d0ab28ac86bf3c3d10bfbbac91450d3f190113b4e76d9fec3cfdd" "checksum serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)" = "e707fbbf255b8fc8c3b99abb91e7257a622caeb20a9818cbadbeeede4e0932ff" "checksum serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" "checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" @@ -474,6 +585,9 @@ dependencies = [ "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum universal-hash 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df0c900f2f9b4116803415878ff48b63da9edb268668e08cf9292d7503114a01" "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum x25519-dalek 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "637ff90c9540fa3073bb577e65033069e4bae7c79d49d74aa3ffdf5342a53217" "checksum zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" "checksum zeroize_derive 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" diff --git a/Cargo.toml b/Cargo.toml index 700c711..f6bd789 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,9 @@ license = "MIT" edition = "2018" readme = "README.md" +[features] +slow-hash = ["scrypt"] + [dependencies] aead = "0.2.0" curve25519-dalek = "2.0.0" @@ -16,6 +19,7 @@ generic-array = "0.12.3" hkdf = "0.8.0" hmac = "0.7.1" rand_core = "0.5.1" +scrypt = {version = "0.2.0", optional = true} sha2 = "0.8" thiserror = "1" x25519-dalek = "0.6.0" diff --git a/src/errors.rs b/src/errors.rs index 636955b..b3d46eb 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -26,6 +26,8 @@ pub enum InternalPakeError { HkdfError, #[error("Computing HMAC failed while supplying a secret key")] HmacError, + #[error("Computing the slow hashing function failed")] + SlowHashError, } /// Represents an error in password checking diff --git a/src/lib.rs b/src/lib.rs index e0fd2e5..d206248 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,8 +21,12 @@ //! use chacha20poly1305::ChaCha20Poly1305; //! use curve25519_dalek::ristretto::RistrettoPoint; //! use opaque_ke::keypair::X25519KeyPair; +//! use opaque_ke::slow_hash::NoOpHash; //! ``` //! +//! 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`. +//! //! We have included a concrete instantiation of the authenticated key exchange protocol using 3DH. In the future, we plan to //! add support for other KE protocols as well. //! @@ -49,8 +53,12 @@ //! In the first step (client registration start), the client chooses a registration password and an optional "pepper", and //! runs `ClientRegistration::start` to produce a message `r1`: //! ``` -//! # use opaque_ke::{opaque::{ClientRegistration, ServerRegistration}, keypair::{KeyPair, X25519KeyPair, SizedBytes}}; -//! # use opaque_ke::errors::ProtocolError; +//! # use opaque_ke::{ +//! # errors::ProtocolError, +//! # opaque::{ClientRegistration, ServerRegistration}, +//! # keypair::{KeyPair, X25519KeyPair, SizedBytes}, +//! # slow_hash::NoOpHash, +//! # }; //! # use curve25519_dalek::ristretto::RistrettoPoint; //! # use chacha20poly1305::ChaCha20Poly1305; //! use rand_core::{OsRng, RngCore}; @@ -68,8 +76,12 @@ //! In the second step (server registration start), the server takes as input the `r1` message from the client and runs //! `ServerRegistration::start` to produce `r2`: //! ``` -//! # use opaque_ke::{opaque::{ClientRegistration, ServerRegistration}, keypair::{KeyPair, X25519KeyPair, SizedBytes}}; -//! # use opaque_ke::errors::ProtocolError; +//! # use opaque_ke::{ +//! # errors::ProtocolError, +//! # opaque::{ClientRegistration, ServerRegistration}, +//! # keypair::{KeyPair, X25519KeyPair, SizedBytes}, +//! # slow_hash::NoOpHash, +//! # }; //! # use curve25519_dalek::ristretto::RistrettoPoint; //! # use chacha20poly1305::ChaCha20Poly1305; //! # use rand_core::{OsRng, RngCore}; @@ -94,8 +106,12 @@ //! with the server's static public key `server_kp.public()`, and uses `client_state` from the first step to run //! `finish` and produce a message `r3` along with the key derivation key `kd_key_registration`: //! ``` -//! # use opaque_ke::{opaque::{ClientRegistration, ServerRegistration}, keypair::{KeyPair, X25519KeyPair, SizedBytes}}; -//! # use opaque_ke::errors::ProtocolError; +//! # use opaque_ke::{ +//! # errors::ProtocolError, +//! # opaque::{ClientRegistration, ServerRegistration}, +//! # keypair::{KeyPair, X25519KeyPair, SizedBytes}, +//! # slow_hash::NoOpHash, +//! # }; //! # use curve25519_dalek::ristretto::RistrettoPoint; //! # use chacha20poly1305::ChaCha20Poly1305; //! # use rand_core::{OsRng, RngCore}; @@ -113,7 +129,7 @@ //! # )?; //! # let server_kp = X25519KeyPair::generate_random(&mut server_rng)?; //! let (r3, kd_key_registration) = -//! client_state.finish::<_, X25519KeyPair>(r2, server_kp.public(), &mut client_rng)?; +//! client_state.finish::<_, X25519KeyPair, NoOpHash>(r2, server_kp.public(), &mut client_rng)?; //! # Ok::<(), ProtocolError>(()) //! ``` //! `r3` is sent to the server, and the client can optionally use `kd_key_registration` for applications that choose to @@ -122,8 +138,12 @@ //! In the fourth step of registration, the server takes as input the `r3` message from the client and uses //! `server_state` from the second step to run `finish` and produce `password_file`: //! ``` -//! # use opaque_ke::{opaque::{ClientRegistration, ServerRegistration}, keypair::{KeyPair, X25519KeyPair, SizedBytes}}; -//! # use opaque_ke::errors::ProtocolError; +//! # use opaque_ke::{ +//! # errors::ProtocolError, +//! # opaque::{ClientRegistration, ServerRegistration}, +//! # keypair::{KeyPair, X25519KeyPair, SizedBytes}, +//! # slow_hash::NoOpHash, +//! # }; //! # use curve25519_dalek::ristretto::RistrettoPoint; //! # use chacha20poly1305::ChaCha20Poly1305; //! # use rand_core::{OsRng, RngCore}; @@ -141,7 +161,7 @@ //! # )?; //! # let server_kp = X25519KeyPair::generate_random(&mut server_rng)?; //! # let (r3, kd_key_registration) = -//! # client_state.finish::<_, X25519KeyPair>(r2, server_kp.public(), &mut client_rng)?; +//! # client_state.finish::<_, X25519KeyPair, NoOpHash>(r2, server_kp.public(), &mut client_rng)?; //! let password_file = server_state.finish(r3)?; //! # Ok::<(), ProtocolError>(()) //! ``` @@ -159,8 +179,12 @@ //! In the first step (client login start), the client chooses a registration password and an optional "pepper", and runs //! `ClientLogin::start` to produce a message `l1`: //! ``` -//! # use opaque_ke::{opaque::{ClientRegistration, ServerRegistration, ClientLogin, ServerLogin, LoginThirdMessage}, keypair::{KeyPair, X25519KeyPair, SizedBytes}}; -//! # use opaque_ke::errors::ProtocolError; +//! # use opaque_ke::{ +//! # errors::ProtocolError, +//! # opaque::{ClientRegistration, ServerRegistration, ClientLogin, ServerLogin, LoginThirdMessage}, +//! # keypair::{KeyPair, X25519KeyPair, SizedBytes}, +//! # slow_hash::NoOpHash, +//! # }; //! # use curve25519_dalek::ristretto::RistrettoPoint; //! # use chacha20poly1305::ChaCha20Poly1305; //! # use rand_core::{OsRng, RngCore}; @@ -178,8 +202,12 @@ //! private key `server_kp.private()`, along with a serialized version of the password file, `password_file_bytes`, and //! runs `ServerLogin::start` to produce `l2`: //! ``` -//! # use opaque_ke::{opaque::{ClientRegistration, ServerRegistration, ClientLogin, ServerLogin, LoginThirdMessage}, keypair::{KeyPair, X25519KeyPair, SizedBytes}}; -//! # use opaque_ke::errors::ProtocolError; +//! # use opaque_ke::{ +//! # errors::ProtocolError, +//! # opaque::{ClientRegistration, ServerRegistration, ClientLogin, ServerLogin, LoginThirdMessage}, +//! # keypair::{KeyPair, X25519KeyPair, SizedBytes}, +//! # slow_hash::NoOpHash, +//! # }; //! # use curve25519_dalek::ristretto::RistrettoPoint; //! # use chacha20poly1305::ChaCha20Poly1305; //! # use rand_core::{OsRng, RngCore}; @@ -197,7 +225,7 @@ //! # )?; //! # let server_kp = X25519KeyPair::generate_random(&mut server_rng)?; //! # let (r3, kd_key_registration) = -//! # client_state.finish::<_, X25519KeyPair>(r2, server_kp.public(), &mut client_rng)?; +//! # client_state.finish::<_, X25519KeyPair, NoOpHash>(r2, server_kp.public(), &mut client_rng)?; //! # let password_file_bytes = server_state.finish(r3)?.to_bytes(); //! # let (l1, client_state) = ClientLogin::::start( //! # b"password", @@ -220,8 +248,12 @@ //! server's static public key `server_kp.public()`, and uses `client_state` from the first step to run `finish` and produce //! a message `l3`, the shared secret `client_shared_secret`, and the key derivation key `kd_key_login`: //! ``` -//! # use opaque_ke::{opaque::{ClientRegistration, ServerRegistration, ClientLogin, ServerLogin, LoginThirdMessage}, keypair::{KeyPair, X25519KeyPair, SizedBytes}}; -//! # use opaque_ke::errors::ProtocolError; +//! # use opaque_ke::{ +//! # errors::ProtocolError, +//! # opaque::{ClientRegistration, ServerRegistration, ClientLogin, ServerLogin, LoginThirdMessage}, +//! # keypair::{KeyPair, X25519KeyPair, SizedBytes}, +//! # slow_hash::NoOpHash, +//! # }; //! # use curve25519_dalek::ristretto::RistrettoPoint; //! # use chacha20poly1305::ChaCha20Poly1305; //! # use rand_core::{OsRng, RngCore}; @@ -239,7 +271,7 @@ //! # )?; //! # let server_kp = X25519KeyPair::generate_random(&mut server_rng)?; //! # let (r3, kd_key_registration) = -//! # client_state.finish::<_, X25519KeyPair>(r2, server_kp.public(), &mut client_rng)?; +//! # client_state.finish::<_, X25519KeyPair, NoOpHash>(r2, server_kp.public(), &mut client_rng)?; //! # let password_file_bytes = server_state.finish(r3)?.to_bytes(); //! # let (l1, client_state) = ClientLogin::::start( //! # b"password", @@ -253,7 +285,7 @@ //! # )?; //! # let (l2, server_state) = //! # ServerLogin::start(password_file, &server_kp.private(), l1, &mut server_rng)?; -//! let (l3, client_shared_secret, kd_key_login) = client_state.finish( +//! let (l3, client_shared_secret, kd_key_login) = client_state.finish::<_, NoOpHash>( //! l2, //! &server_kp.public(), //! &mut client_rng, @@ -271,8 +303,12 @@ //! In the fourth step of login, the server takes as input the `l3` message from the client and uses `server_state` from //! the second step to run `finish`: //! ``` -//! # use opaque_ke::{opaque::{ClientRegistration, ServerRegistration, ClientLogin, ServerLogin, LoginThirdMessage}, keypair::{KeyPair, X25519KeyPair, SizedBytes}}; -//! # use opaque_ke::errors::ProtocolError; +//! # use opaque_ke::{ +//! # errors::ProtocolError, +//! # opaque::{ClientRegistration, ServerRegistration, ClientLogin, ServerLogin, LoginThirdMessage}, +//! # keypair::{KeyPair, X25519KeyPair, SizedBytes}, +//! # slow_hash::NoOpHash, +//! # }; //! # use curve25519_dalek::ristretto::RistrettoPoint; //! # use chacha20poly1305::ChaCha20Poly1305; //! # use rand_core::{OsRng, RngCore}; @@ -290,7 +326,7 @@ //! # )?; //! # let server_kp = X25519KeyPair::generate_random(&mut server_rng)?; //! # let (r3, kd_key) = -//! # client_state.finish::<_, X25519KeyPair>(r2, server_kp.public(), &mut client_rng)?; +//! # client_state.finish::<_, X25519KeyPair, NoOpHash>(r2, server_kp.public(), &mut client_rng)?; //! # let password_file_bytes = server_state.finish(r3)?.to_bytes(); //! # let (l1, client_state) = ClientLogin::::start( //! # b"password", @@ -304,7 +340,7 @@ //! # )?; //! # let (l2, server_state) = //! # ServerLogin::start(password_file, &server_kp.private(), l1, &mut server_rng)?; -//! # let (l3, client_shared_secret, kd_key) = client_state.finish( +//! # let (l3, client_shared_secret, kd_key) = client_state.finish::<_, NoOpHash>( //! # l2, //! # &server_kp.public(), //! # &mut client_rng, @@ -332,6 +368,7 @@ mod oprf; // Technical module for your choice of cyclic subgroup to // do the oprf on mod group; +pub mod slow_hash; #[cfg(test)] mod tests; diff --git a/src/opaque.rs b/src/opaque.rs index 20f698c..86797ad 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -16,6 +16,7 @@ use crate::{ oprf, oprf::OprfClientBytes, rkr_encryption::{RKRCipher, RKRCiphertext}, + slow_hash::SlowHash, }; use generic_array::{ typenum::{Unsigned, U32, U64}, @@ -363,6 +364,7 @@ where /// use opaque_ke::{opaque::{ClientRegistration, ServerRegistration}, keypair::{X25519KeyPair, SizedBytes}}; /// # use opaque_ke::errors::ProtocolError; /// # use opaque_ke::keypair::KeyPair; + /// # use opaque_ke::slow_hash::NoOpHash; /// use rand_core::{OsRng, RngCore}; /// use chacha20poly1305::ChaCha20Poly1305; /// use curve25519_dalek::ristretto::RistrettoPoint; @@ -373,10 +375,10 @@ where /// let (register_m2, server_state) = /// ServerRegistration::::start(register_m1, &mut server_rng)?; /// let mut client_rng = OsRng; - /// let register_m3 = client_state.finish::<_, X25519KeyPair>(register_m2, server_kp.public(), &mut client_rng)?; + /// let register_m3 = client_state.finish::<_, X25519KeyPair, NoOpHash>(register_m2, server_kp.public(), &mut client_rng)?; /// # Ok::<(), ProtocolError>(()) /// ``` - pub fn finish( + pub fn finish( self, r2: RegisterSecondMessage, server_s_pk: &KeyFormat::Repr, @@ -384,8 +386,11 @@ where ) -> Result, ProtocolError> { let client_static_keypair = KeyFormat::generate_random(rng)?; - let password_derived_key = - get_password_derived_key::(self.password.clone(), r2.beta, &self.blinding_factor)?; + let password_derived_key = get_password_derived_key::( + self.password.clone(), + r2.beta, + &self.blinding_factor, + )?; let h = Hkdf::::new(None, &password_derived_key); let mut okm = [0u8; 3 * DERIVED_KEY_LEN]; h.expand(STR_ENVU, &mut okm) @@ -572,6 +577,7 @@ where /// use opaque_ke::{opaque::*, keypair::{X25519KeyPair, SizedBytes}}; /// # use opaque_ke::errors::ProtocolError; /// # use opaque_ke::keypair::KeyPair; + /// # use opaque_ke::slow_hash::NoOpHash; /// use rand_core::{OsRng, RngCore}; /// use chacha20poly1305::ChaCha20Poly1305; /// use curve25519_dalek::ristretto::RistrettoPoint; @@ -582,7 +588,7 @@ where /// let (register_m2, server_state) = /// ServerRegistration::::start(register_m1, &mut server_rng)?; /// let mut client_rng = OsRng; - /// let (register_m3, _opaque_key) = client_state.finish(register_m2, server_kp.public(), &mut client_rng)?; + /// let (register_m3, _opaque_key) = client_state.finish::<_, _, NoOpHash>(register_m2, server_kp.public(), &mut client_rng)?; /// let client_record = server_state.finish(register_m3)?; /// # Ok::<(), ProtocolError>(()) /// ``` @@ -722,6 +728,7 @@ where /// # use opaque_ke::opaque::{ClientRegistration, ServerRegistration}; /// # use opaque_ke::errors::ProtocolError; /// # use opaque_ke::keypair::{X25519KeyPair, KeyPair}; + /// # use opaque_ke::slow_hash::NoOpHash; /// use rand_core::{OsRng, RngCore}; /// use chacha20poly1305::ChaCha20Poly1305; /// use curve25519_dalek::ristretto::RistrettoPoint; @@ -730,14 +737,14 @@ where /// # let (register_m1, client_state) = ClientRegistration::::start(b"hunter2", None, &mut client_rng)?; /// # let server_kp = X25519KeyPair::generate_random(&mut server_rng)?; /// # let (register_m2, server_state) = ServerRegistration::::start(register_m1, &mut server_rng)?; - /// # let (register_m3, _opaque_key) = client_state.finish(register_m2, server_kp.public(), &mut client_rng)?; + /// # let (register_m3, _opaque_key) = client_state.finish::<_, _, NoOpHash>(register_m2, server_kp.public(), &mut client_rng)?; /// # let p_file = server_state.finish(register_m3)?; /// let (login_m1, client_login_state) = ClientLogin::::start(b"hunter2", None, &mut client_rng)?; /// let (login_m2, server_login_state) = ServerLogin::start(p_file, &server_kp.private(), login_m1, &mut server_rng)?; - /// let (login_m3, client_transport, _opaque_key) = client_login_state.finish(login_m2, &server_kp.public(), &mut client_rng)?; + /// let (login_m3, client_transport, _opaque_key) = client_login_state.finish::<_, NoOpHash>(login_m2, &server_kp.public(), &mut client_rng)?; /// # Ok::<(), ProtocolError>(()) /// ``` - pub fn finish( + pub fn finish( self, l2: LoginSecondMessage, server_s_pk: &KeyFormat::Repr, @@ -745,8 +752,11 @@ where ) -> Result { let l2_bytes: Vec = [l2.beta.to_bytes().as_slice(), &l2.envelope.to_bytes()].concat(); - let password_derived_key = - get_password_derived_key::(self.password.clone(), l2.beta, &self.blinding_factor)?; + let password_derived_key = get_password_derived_key::( + self.password.clone(), + l2.beta, + &self.blinding_factor, + )?; let h = Hkdf::::new(None, &password_derived_key); let mut okm = [0u8; 3 * DERIVED_KEY_LEN]; h.expand(STR_ENVU, &mut okm) @@ -812,6 +822,7 @@ impl ServerLogin { /// # use opaque_ke::opaque::{ClientRegistration, ServerRegistration}; /// # use opaque_ke::errors::ProtocolError; /// # use opaque_ke::keypair::{KeyPair, X25519KeyPair}; + /// # use opaque_ke::slow_hash::NoOpHash; /// use rand_core::{OsRng, RngCore}; /// use chacha20poly1305::ChaCha20Poly1305; /// use curve25519_dalek::ristretto::RistrettoPoint; @@ -821,7 +832,7 @@ impl ServerLogin { /// # let (register_m1, client_state) = ClientRegistration::::start(b"hunter2", None, &mut client_rng)?; /// # let (register_m2, server_state) = /// ServerRegistration::::start(register_m1, &mut server_rng)?; - /// # let (register_m3, _opaque_key) = client_state.finish(register_m2, server_kp.public(), &mut client_rng)?; + /// # let (register_m3, _opaque_key) = client_state.finish::<_, _, NoOpHash>(register_m2, server_kp.public(), &mut client_rng)?; /// # let p_file = server_state.finish(register_m3)?; /// let (login_m1, client_login_state) = ClientLogin::::start(b"hunter2", None, &mut client_rng)?; /// let (login_m2, server_login_state) = ServerLogin::start(p_file, &server_kp.private(), login_m1, &mut server_rng)?; @@ -880,6 +891,7 @@ impl ServerLogin { /// # use opaque_ke::opaque::{ClientRegistration, ServerRegistration}; /// # use opaque_ke::errors::ProtocolError; /// # use opaque_ke::keypair::{KeyPair, X25519KeyPair}; + /// # use opaque_ke::slow_hash::NoOpHash; /// use rand_core::{OsRng, RngCore}; /// use chacha20poly1305::ChaCha20Poly1305; /// use curve25519_dalek::ristretto::RistrettoPoint; @@ -889,11 +901,11 @@ impl ServerLogin { /// # let (register_m1, client_state) = ClientRegistration::::start(b"hunter2", None, &mut client_rng)?; /// # let (register_m2, server_state) = /// ServerRegistration::::start(register_m1, &mut server_rng)?; - /// # let (register_m3, _opaque_key) = client_state.finish(register_m2, server_kp.public(), &mut client_rng)?; + /// # let (register_m3, _opaque_key) = client_state.finish::<_, _, NoOpHash>(register_m2, server_kp.public(), &mut client_rng)?; /// # let p_file = server_state.finish(register_m3)?; /// let (login_m1, client_login_state) = ClientLogin::::start(b"hunter2", None, &mut client_rng)?; /// let (login_m2, server_login_state) = ServerLogin::start(p_file, &server_kp.private(), login_m1, &mut server_rng)?; - /// let (login_m3, client_transport, _opaque_key) = client_login_state.finish(login_m2, &server_kp.public(), &mut client_rng)?; + /// let (login_m3, client_transport, _opaque_key) = client_login_state.finish::<_, NoOpHash>(login_m2, &server_kp.public(), &mut client_rng)?; /// let mut server_transport = server_login_state.finish(login_m3)?; /// # Ok::<(), ProtocolError>(()) /// ``` @@ -909,10 +921,11 @@ impl ServerLogin { // Helper functions -fn get_password_derived_key( +fn get_password_derived_key( password: Vec, beta: G, blinding_factor: &G::Scalar, -) -> Result::OutputSize>, PakeError> { - Ok(oprf::generate_oprf3::(&password, beta, blinding_factor)?) +) -> Result, InternalPakeError> { + let oprf_output = oprf::generate_oprf3::(&password, beta, blinding_factor)?; + SH::hash(oprf_output) } diff --git a/src/slow_hash.rs b/src/slow_hash.rs new file mode 100644 index 0000000..ce71545 --- /dev/null +++ b/src/slow_hash.rs @@ -0,0 +1,38 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +use crate::errors::InternalPakeError; + +use generic_array::GenericArray; +use sha2::{Digest, Sha256}; + +pub trait SlowHash { + fn hash( + input: GenericArray::OutputSize>, + ) -> Result, InternalPakeError>; +} + +pub struct NoOpHash; + +impl SlowHash for NoOpHash { + fn hash( + input: GenericArray::OutputSize>, + ) -> Result, InternalPakeError> { + Ok(input.to_vec()) + } +} + +#[cfg(feature = "slow-hash")] +impl SlowHash for scrypt::ScryptParams { + fn hash( + input: GenericArray::OutputSize>, + ) -> Result, InternalPakeError> { + let params = scrypt::ScryptParams::new(15, 8, 1).unwrap(); + let mut output = [0u8; 32]; + scrypt::scrypt(&input, &[], ¶ms, &mut output) + .map_err(|_| InternalPakeError::SlowHashError)?; + Ok(output.to_vec()) + } +} diff --git a/src/tests/opaque_ke_test.rs b/src/tests/opaque_ke_test.rs index d01434d..2d42000 100644 --- a/src/tests/opaque_ke_test.rs +++ b/src/tests/opaque_ke_test.rs @@ -9,6 +9,7 @@ use crate::{ key_exchange::NONCE_LEN, keypair::{Key, KeyPair, SignalKeyPair}, opaque::*, + slow_hash::NoOpHash, tests::mock_rng::CycleRng, }; use aes_gcm::Aes256Gcm; @@ -257,7 +258,11 @@ fn generate_parameters() -> TestVectorParameters { let mut finish_registration_rng = CycleRng::new(client_s_sk_and_nonce); let (r3, opaque_key_registration) = client_registration - .finish::<_, SignalKeyPair>(r2, server_s_kp.public(), &mut finish_registration_rng) + .finish::<_, SignalKeyPair, NoOpHash>( + r2, + server_s_kp.public(), + &mut finish_registration_rng, + ) .unwrap(); let r3_bytes = r3.to_bytes().to_vec(); @@ -292,7 +297,7 @@ fn generate_parameters() -> TestVectorParameters { let mut client_e_sk_rng = CycleRng::new(client_e_kp.private().to_vec()); let (l3, client_shared_secret, _opaque_key_login) = client_login - .finish(l2, server_s_kp.public(), &mut client_e_sk_rng) + .finish::<_, NoOpHash>(l2, server_s_kp.public(), &mut client_e_sk_rng) .unwrap(); let l3_bytes = l3.to_bytes().to_vec(); @@ -382,7 +387,7 @@ fn test_r3() -> Result<(), PakeError> { ¶meters.client_registration_state[..], ) .unwrap() - .finish::( + .finish::( RegisterSecondMessage::try_from(¶meters.r2[..]).unwrap(), &Key::try_from(parameters.server_s_pk).unwrap(), &mut finish_registration_rng, @@ -474,7 +479,7 @@ fn test_l3() -> Result<(), PakeError> { ¶meters.client_login_state[..], ) .unwrap() - .finish( + .finish::<_, NoOpHash>( LoginSecondMessage::::try_from(¶meters.l2[..]).unwrap(), &Key::try_from(parameters.server_s_pk)?, &mut client_e_sk_rng, @@ -529,7 +534,7 @@ fn test_complete_flow( &mut server_rng, )?; let (register_m3, registration_opaque_key) = - client_state.finish(register_m2, server_kp.public(), &mut client_rng)?; + client_state.finish::<_, _, NoOpHash>(register_m2, server_kp.public(), &mut client_rng)?; let p_file = server_state.finish(register_m3)?; let (login_m1, client_login_state) = ClientLogin::::start( @@ -541,7 +546,7 @@ fn test_complete_flow( ServerLogin::start(p_file, &server_kp.private(), login_m1, &mut server_rng)?; let client_login_result = - client_login_state.finish(login_m2, &server_kp.public(), &mut client_rng); + client_login_state.finish::<_, NoOpHash>(login_m2, &server_kp.public(), &mut client_rng); if hex::encode(registration_password) == hex::encode(login_password) { let (login_m3, client_shared_secret, login_opaque_key) = client_login_result?;