diff --git a/.cargo/license.rs b/.cargo/license.rs new file mode 100644 index 0000000..3fa8d9f --- /dev/null +++ b/.cargo/license.rs @@ -0,0 +1,6 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under both the MIT license found in the +// LICENSE-MIT file in the root directory of this source tree and the Apache +// License, Version 2.0 found in the LICENSE-APACHE file in the root directory +// of this source tree. diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4ef4112..d4e28ce 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -227,11 +227,11 @@ jobs: - name: Checkout sources uses: actions/checkout@v2 - - name: Install stable toolchain + - name: Install nightly toolchain uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: stable + toolchain: nightly override: true components: rustfmt @@ -241,6 +241,28 @@ jobs: command: fmt args: --all -- --check + taplo: + name: Taplo + runs-on: ubuntu-latest + steps: + - name: Cache + uses: actions/cache@v2 + with: + path: | + ~/.cargo/.crates.toml + ~/.cargo/.crates2.json + ~/.cargo/bin/taplo + key: taplo + + - name: Install Taplo + run: cargo install taplo-cli + + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Run Taplo + run: taplo fmt --check + deny-check: name: cargo-deny check runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index bd0ac18..672707d 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,38 +1,64 @@ [package] -name = "opaque-ke" -version = "2.0.0-pre.1" -repository = "https://github.com/novifinancial/opaque-ke" -keywords = ["cryptography", "crypto", "opaque", "passwords", "authentication"] +authors = ["Kevin Lewi ", "François Garillot "] categories = ["no-std"] description = "An implementation of the OPAQUE password-authenticated key exchange protocol" -authors = ["Kevin Lewi ", "François Garillot "] -license = "Apache-2.0 OR MIT" edition = "2021" +keywords = ["cryptography", "crypto", "opaque", "passwords", "authentication"] +license = "Apache-2.0 OR MIT" +name = "opaque-ke" readme = "README.md" +repository = "https://github.com/novifinancial/opaque-ke" rust-version = "1.56" +version = "2.0.0-pre.1" [features] default = ["ristretto255_u64", "serde"] -slow-hash = ["argon2"] p256 = ["p256_", "voprf/p256"] ristretto255 = [] -ristretto255_u64 = ["curve25519-dalek/u64_backend", "ristretto255", "voprf/ristretto255_u64"] -ristretto255_u32 = ["curve25519-dalek/u32_backend", "ristretto255", "voprf/ristretto255_u32"] -ristretto255_fiat_u64 = ["curve25519-dalek/fiat_u64_backend", "ristretto255", "voprf/ristretto255_fiat_u64"] -ristretto255_fiat_u32 = ["curve25519-dalek/fiat_u32_backend", "ristretto255", "voprf/ristretto255_fiat_u32"] -ristretto255_simd = ["curve25519-dalek/simd_backend", "ristretto255", "voprf/ristretto255_simd"] -x25519 = [] -x25519_u64 = ["x25519", "x25519-dalek/u64_backend"] -x25519_u32 = ["x25519", "x25519-dalek/u32_backend"] -x25519_fiat_u64 = ["x25519", "x25519-dalek/fiat_u64_backend"] -x25519_fiat_u32 = ["x25519", "x25519-dalek/fiat_u32_backend"] -# x25519-dalek isn't properly re-exposing `simd_backend`. -x25519_simd = ["curve25519-dalek/simd_backend", "x25519", "x25519-dalek/nightly"] -std = ["getrandom", "rand/std", "rand/std_rng", "voprf/std"] +ristretto255_fiat_u32 = [ + "curve25519-dalek/fiat_u32_backend", + "ristretto255", + "voprf/ristretto255_fiat_u32", +] +ristretto255_fiat_u64 = [ + "curve25519-dalek/fiat_u64_backend", + "ristretto255", + "voprf/ristretto255_fiat_u64", +] +ristretto255_simd = [ + "curve25519-dalek/simd_backend", + "ristretto255", + "voprf/ristretto255_simd", +] +ristretto255_u32 = [ + "curve25519-dalek/u32_backend", + "ristretto255", + "voprf/ristretto255_u32", +] +ristretto255_u64 = [ + "curve25519-dalek/u64_backend", + "ristretto255", + "voprf/ristretto255_u64", +] serde = ["serde_", "generic-array/serde", "voprf/serde"] +slow-hash = ["argon2"] +std = ["getrandom", "rand/std", "rand/std_rng", "voprf/std"] +x25519 = [] +x25519_fiat_u32 = ["x25519", "x25519-dalek/fiat_u32_backend"] +x25519_fiat_u64 = ["x25519", "x25519-dalek/fiat_u64_backend"] +# x25519-dalek isn't properly re-exposing `simd_backend`. +x25519_simd = [ + "curve25519-dalek/simd_backend", + "x25519", + "x25519-dalek/nightly", +] +x25519_u32 = ["x25519", "x25519-dalek/u32_backend"] +x25519_u64 = ["x25519", "x25519-dalek/u64_backend"] [dependencies] -argon2 = { version = "0.3", default-features = false, features = ["alloc"], optional = true } +argon2 = { version = "0.3", default-features = false, features = [ + "alloc", +], optional = true } constant_time_eq = "0.1" curve25519-dalek = { version = "3", default-features = false, optional = true } derive-where = { version = "1.0.0-rc.1", features = ["zeroize"] } @@ -42,11 +68,17 @@ generic-array = "0.14" getrandom = { version = "0.2", optional = true } hkdf = "0.12" hmac = "0.12" -p256_ = { package = "p256", version = "0.10", default-features = false, features = ["arithmetic"], optional = true } +p256_ = { package = "p256", version = "0.10", default-features = false, features = [ + "arithmetic", +], optional = true } rand = { version = "0.8", default-features = false } -serde_ = { version = "1", package = "serde", default-features = false, features = ["derive"], optional = true } +serde_ = { version = "1", package = "serde", default-features = false, features = [ + "derive", +], optional = true } subtle = { version = "2.3", default-features = false } -voprf = { git = "https://github.com/novifinancial/voprf", rev = "55ef981a3f9a12eddd8c372ffdf51818011343ee", default-features = false, features = ["danger"] } +voprf = { git = "https://github.com/novifinancial/voprf", rev = "55ef981a3f9a12eddd8c372ffdf51818011343ee", default-features = false, features = [ + "danger", +] } x25519-dalek = { version = "1", default-features = false, optional = true } zeroize = { version = "1", features = ["zeroize_derive"] } @@ -61,12 +93,12 @@ criterion = "0.3" hex = "0.4" json = "0.12" lazy_static = "1" -serde_json = "1" -sha2 = "0.10" proptest = "1" regex = "1" rustyline = "9" +serde_json = "1" +sha2 = "0.10" [[bench]] -name = "opaque" harness = false +name = "opaque" diff --git a/deny.toml b/deny.toml index db35d7b..7412774 100644 --- a/deny.toml +++ b/deny.toml @@ -18,13 +18,13 @@ # dependencies not shared by any other crates, would be ignored, as the target # list here is effectively saying which targets you are building for. targets = [ - # The triple can be any string, but only the target triples built in to - # rustc (as of 1.40) can be checked against actual config expressions - #{ triple = "x86_64-unknown-linux-musl" }, - # You can also specify which target_features you promise are enabled for a - # particular target. target_features are currently not validated against - # the actual valid features supported by the target architecture. - #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, + # The triple can be any string, but only the target triples built in to + # rustc (as of 1.40) can be checked against actual config expressions + #{ triple = "x86_64-unknown-linux-musl" }, + # You can also specify which target_features you promise are enabled for a + # particular target. target_features are currently not validated against + # the actual valid features supported by the target architecture. + #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, ] # This section is considered when running `cargo deny check advisories` @@ -48,7 +48,7 @@ notice = "deny" # A list of advisory IDs to ignore. Note that ignored advisories will still # output a note when they are encountered. ignore = [ - #"RUSTSEC-0000-0000", + #"RUSTSEC-0000-0000", ] # Threshold for security vulnerabilities, any vulnerability with a CVSS score # lower than the range specified will be ignored. Note that ignored advisories @@ -70,15 +70,15 @@ unlicensed = "deny" # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.7 short identifier (+ optional exception)]. allow = [ - #"MIT", - #"Apache-2.0", - #"Apache-2.0 WITH LLVM-exception", + #"MIT", + #"Apache-2.0", + #"Apache-2.0 WITH LLVM-exception", ] # List of explictly disallowed licenses # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.7 short identifier (+ optional exception)]. deny = [ - #"Nokia", + #"Nokia", ] # Lint level for licenses considered copyleft copyleft = "warn" @@ -102,9 +102,9 @@ confidence-threshold = 0.8 # Allow 1 or more licenses on a per-crate basis, so that particular licenses # aren't accepted for every possible crate as with the normal allow list exceptions = [ - # Each entry is the crate and version constraint, and its specific allow - # list - #{ allow = ["Zlib"], name = "adler32", version = "*" }, + # Each entry is the crate and version constraint, and its specific allow + # list + #{ allow = ["Zlib"], name = "adler32", version = "*" }, ] # Some crates don't have (easily) machine readable licensing information, @@ -123,8 +123,8 @@ exceptions = [ # and the crate will be checked normally, which may produce warnings or errors # depending on the rest of your configuration #license-files = [ - # Each entry is a crate relative path, and the (opaque) hash of its contents - #{ path = "LICENSE", hash = 0xbd0eed23 } +# Each entry is a crate relative path, and the (opaque) hash of its contents +#{ path = "LICENSE", hash = 0xbd0eed23 } #] [licenses.private] @@ -135,7 +135,7 @@ ignore = false # is only published to private registries, and ignore is true, the crate will # not have its license(s) checked registries = [ - #"https://sekretz.com/registry + #"https://sekretz.com/registry ] # This section is considered when running `cargo deny check bans`. @@ -152,24 +152,24 @@ multiple-versions = "warn" highlight = "all" # List of crates that are allowed. Use with care! allow = [ - #{ name = "ansi_term", version = "=0.11.0" }, + #{ name = "ansi_term", version = "=0.11.0" }, ] # List of crates to deny deny = [ - # Each entry the name of a crate and a version range. If version is - # not specified, all versions will be matched. - #{ name = "ansi_term", version = "=0.11.0" }, + # Each entry the name of a crate and a version range. If version is + # not specified, all versions will be matched. + #{ name = "ansi_term", version = "=0.11.0" }, ] # Certain crates/versions that will be skipped when doing duplicate detection. skip = [ - #{ name = "ansi_term", version = "=0.11.0" }, + #{ name = "ansi_term", version = "=0.11.0" }, ] # Similarly to `skip` allows you to skip certain crates during duplicate # detection. Unlike skip, it also includes the entire tree of transitive # dependencies starting at the specified crate, up to a certain depth, which is # by default infinite skip-tree = [ - #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, + #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, ] # This section is considered when running `cargo deny check sources`. diff --git a/examples/digital_locker.rs b/examples/digital_locker.rs index 408f7a0..c04effc 100644 --- a/examples/digital_locker.rs +++ b/examples/digital_locker.rs @@ -8,43 +8,42 @@ //! Demonstrates an implementation of a server-side secured digital locker using //! the client's OPAQUE export key, over a command-line interface //! -//! A client can password-protect a secret message to be stored in a digital locker, -//! controlled by the server. The locker's contents are only revealed to the holder -//! of the password when attempting to open the locker. +//! A client can password-protect a secret message to be stored in a digital +//! locker, controlled by the server. The locker's contents are only revealed to +//! the holder of the password when attempting to open the locker. //! -//! The client-server interactions are executed in a three-step protocol -//! within the account_registration (for password registration) and -//! account_login (for password login) functions. These steps -//! must be performed in the specific sequence outlined in each of these -//! functions. +//! The client-server interactions are executed in a three-step protocol within +//! the account_registration (for password registration) and account_login (for +//! password login) functions. These steps must be performed in the specific +//! sequence outlined in each of these functions. //! -//! The CipherSuite trait allows the application to configure the -//! primitives used by OPAQUE, but must be kept consistent across the steps -//! of the protocol. +//! The CipherSuite trait allows the application to configure the primitives +//! used by OPAQUE, but must be kept consistent across the steps of the +//! protocol. //! -//! In a more realistic client-server interaction, the client must send -//! messages over "the wire" to the server. These bytes are serialized -//! and explicitly annotated in the below functions. +//! In a more realistic client-server interaction, the client must send messages +//! over "the wire" to the server. These bytes are serialized and explicitly +//! annotated in the below functions. + +use std::process::exit; use chacha20poly1305::aead::{Aead, NewAead}; use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce}; use generic_array::GenericArray; -use opaque_ke::ServerRegistrationLen; -use rustyline::error::ReadlineError; -use rustyline::Editor; -use std::process::exit; - +use opaque_ke::ciphersuite::CipherSuite; +use opaque_ke::rand::rngs::OsRng; +use opaque_ke::rand::RngCore; use opaque_ke::{ - ciphersuite::CipherSuite, - rand::{rngs::OsRng, RngCore}, ClientLogin, ClientLoginFinishParameters, ClientRegistration, ClientRegistrationFinishParameters, CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin, - ServerLoginStartParameters, ServerRegistration, ServerSetup, + ServerLoginStartParameters, ServerRegistration, ServerRegistrationLen, ServerSetup, }; +use rustyline::error::ReadlineError; +use rustyline::Editor; -// The ciphersuite trait allows to specify the underlying primitives -// that will be used in the OPAQUE protocol +// The ciphersuite trait allows to specify the underlying primitives that will +// be used in the OPAQUE protocol #[allow(dead_code)] struct Default; @@ -84,7 +83,8 @@ fn encrypt(key: &[u8], plaintext: &[u8]) -> Vec { [nonce_bytes.to_vec(), ciphertext].concat() } -// Decrypt using a key and a ciphertext (nonce included) to recover the original plaintext +// Decrypt using a key and a ciphertext (nonce included) to recover the original +// plaintext fn decrypt(key: &[u8], ciphertext: &[u8]) -> Vec { let cipher = ChaCha20Poly1305::new(Key::from_slice(&key[..32])); cipher @@ -95,7 +95,8 @@ fn decrypt(key: &[u8], ciphertext: &[u8]) -> Vec { .unwrap() } -// Password-based registration and encryption of client secret message between a client and server +// Password-based registration and encryption of client secret message between a +// client and server fn register_locker( server_setup: &ServerSetup, locker_id: usize, @@ -200,7 +201,7 @@ fn open_locker( let encrypted_locker_contents = encrypt(&server_login_finish_result.session_key, &locker.contents); - // Client decrypts contents of locker, first under the session key, and then under the export key + // Client decrypts contents of locker, first under the session key, and then let plaintext = decrypt( &client_login_finish_result.export_key, &decrypt( diff --git a/examples/simple_login.rs b/examples/simple_login.rs index 9e651fc..4cb5022 100644 --- a/examples/simple_login.rs +++ b/examples/simple_login.rs @@ -5,39 +5,39 @@ // License, Version 2.0 found in the LICENSE-APACHE file in the root directory // of this source tree. -//! Demonstrates a simple client-server password-based login protocol -//! using OPAQUE, over a command-line interface +//! Demonstrates a simple client-server password-based login protocol using +//! OPAQUE, over a command-line interface //! -//! The client-server interactions are executed in a three-step protocol -//! within the account_registration (for password registration) and -//! account_login (for password login) functions. These steps -//! must be performed in the specific sequence outlined in each of these -//! functions. +//! The client-server interactions are executed in a three-step protocol within +//! the account_registration (for password registration) and account_login (for +//! password login) functions. These steps must be performed in the specific +//! sequence outlined in each of these functions. //! -//! The CipherSuite trait allows the application to configure the -//! primitives used by OPAQUE, but must be kept consistent across the steps -//! of the protocol. +//! The CipherSuite trait allows the application to configure the primitives +//! used by OPAQUE, but must be kept consistent across the steps of the +//! protocol. //! -//! In a more realistic client-server interaction, the client must send -//! messages over "the wire" to the server. These bytes are serialized -//! and explicitly annotated in the below functions. +//! In a more realistic client-server interaction, the client must send messages +//! over "the wire" to the server. These bytes are serialized and explicitly +//! annotated in the below functions. -use generic_array::GenericArray; -use rustyline::error::ReadlineError; -use rustyline::Editor; use std::collections::HashMap; use std::process::exit; +use generic_array::GenericArray; +use opaque_ke::ciphersuite::CipherSuite; +use opaque_ke::rand::rngs::OsRng; use opaque_ke::{ - ciphersuite::CipherSuite, rand::rngs::OsRng, ClientLogin, ClientLoginFinishParameters, - ClientRegistration, ClientRegistrationFinishParameters, CredentialFinalization, - CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse, - RegistrationUpload, ServerLogin, ServerLoginStartParameters, ServerRegistration, - ServerRegistrationLen, ServerSetup, + ClientLogin, ClientLoginFinishParameters, ClientRegistration, + ClientRegistrationFinishParameters, CredentialFinalization, CredentialRequest, + CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin, + ServerLoginStartParameters, ServerRegistration, ServerRegistrationLen, ServerSetup, }; +use rustyline::error::ReadlineError; +use rustyline::Editor; -// The ciphersuite trait allows to specify the underlying primitives -// that will be used in the OPAQUE protocol +// The ciphersuite trait allows to specify the underlying primitives that will +// be used in the OPAQUE protocol #[allow(dead_code)] struct Default; @@ -191,11 +191,12 @@ fn main() { { println!("\nLogin success!"); } else { - // Note that at this point, the client knows whether or not the login - // succeeded. In this example, we simply rely on client-reported result - // of login, but in a real client-server implementation, the server may not - // know the outcome of login yet, and extra care must be taken to ensure - // that the server can learn the outcome as well. + // Note that at this point, the client knows whether or not the + // login succeeded. In this example, we simply rely on + // client-reported result of login, but in a real client-server + // implementation, the server may not know the outcome of login yet, + // and extra care must be taken to ensure that the server can learn + // the outcome as well. println!("\nIncorrect password, please try again."); } } diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..faa263a --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,8 @@ +format_code_in_doc_comments = true +format_strings = true +group_imports = "StdExternalCrate" +imports_granularity = "Module" +license_template_path = ".cargo/license.rs" +newline_style = "Unix" +unstable_features = true +wrap_comments = true diff --git a/src/ciphersuite.rs b/src/ciphersuite.rs index 4e2f60f..2bbe868 100644 --- a/src/ciphersuite.rs +++ b/src/ciphersuite.rs @@ -5,19 +5,23 @@ // License, Version 2.0 found in the LICENSE-APACHE file in the root directory // of this source tree. -//! Defines the CipherSuite trait to specify the underlying primitives for OPAQUE +//! Defines the CipherSuite trait to specify the underlying primitives for +//! OPAQUE -use crate::hash::ProxyHash; -use crate::key_exchange::group::KeGroup; -use crate::{hash::Hash, key_exchange::traits::KeyExchange, slow_hash::SlowHash}; use digest::core_api::{BlockSizeUser, CoreProxy}; use generic_array::typenum::{IsLess, Le, NonZero, U256}; use voprf::Group as OprfGroup; +use crate::hash::{Hash, ProxyHash}; +use crate::key_exchange::group::KeGroup; +use crate::key_exchange::traits::KeyExchange; +use crate::slow_hash::SlowHash; + /// Configures the underlying primitives used in OPAQUE -/// * `OprfGroup`: a finite cyclic group along with a point representation, along -/// with an extension trait PasswordToCurve that allows some customization on -/// how to hash a password to a curve point. See `group::Group`. +/// * `OprfGroup`: a finite cyclic group along with a point representation, +/// along with an extension trait PasswordToCurve that allows some +/// customization on how to hash a password to a curve point. See +/// `group::Group`. /// * `KeGroup`: A `Group` used for the `KeyExchange`. /// * `KeyExchange`: The key exchange protocol to use in the login step /// * `Hash`: The main hashing function to use @@ -28,15 +32,16 @@ where <::Core as BlockSizeUser>::BlockSize: IsLess, Le<<::Core as BlockSizeUser>::BlockSize, U256>: NonZero, { - /// A finite cyclic group along with a point representation along with - /// an extension trait PasswordToCurve that allows some customization on - /// how to hash a password to a curve point. See `group::Group`. + /// A finite cyclic group along with a point representation along with an + /// extension trait PasswordToCurve that allows some customization on how to + /// hash a password to a curve point. See `group::Group`. type OprfGroup: OprfGroup; /// A `Group` used for the `KeyExchange`. type KeGroup: KeGroup; /// A key exchange protocol type KeyExchange: KeyExchange; - /// The main hash function use (for HKDF computations and hashing transcripts) + /// The main hash function use (for HKDF computations and hashing + /// transcripts) type Hash: Hash; /// A slow hashing function, typically used for password hashing type SlowHash: SlowHash; diff --git a/src/envelope.rs b/src/envelope.rs index ddcf488..31e7c7e 100755 --- a/src/envelope.rs +++ b/src/envelope.rs @@ -5,31 +5,30 @@ // License, Version 2.0 found in the LICENSE-APACHE file in the root directory // of this source tree. -use crate::{ - ciphersuite::CipherSuite, - errors::{utils::check_slice_size, InternalError, ProtocolError}, - hash::{Hash, OutputSize, ProxyHash}, - key_exchange::group::KeGroup, - keypair::{KeyPair, PublicKey}, - opaque::{bytestrings_from_identifiers, Identifiers}, - serialization::{MacExt, Serialize}, -}; use core::convert::TryFrom; use core::ops::Add; + use derive_where::DeriveWhere; use digest::core_api::{BlockSizeUser, CoreProxy}; use digest::Output; -use generic_array::{ - sequence::Concat, - typenum::{IsLess, Le, NonZero, Sum, Unsigned, U2, U256, U32}, - ArrayLength, GenericArray, -}; +use generic_array::sequence::Concat; +use generic_array::typenum::{IsLess, Le, NonZero, Sum, Unsigned, U2, U256, U32}; +use generic_array::{ArrayLength, GenericArray}; use hkdf::Hkdf; use hmac::{Hmac, Mac}; use rand::{CryptoRng, RngCore}; use voprf::Group; use zeroize::Zeroize; +use crate::ciphersuite::CipherSuite; +use crate::errors::utils::check_slice_size; +use crate::errors::{InternalError, ProtocolError}; +use crate::hash::{Hash, OutputSize, ProxyHash}; +use crate::key_exchange::group::KeGroup; +use crate::keypair::{KeyPair, PublicKey}; +use crate::opaque::{bytestrings_from_identifiers, Identifiers}; +use crate::serialization::{MacExt, Serialize}; + // Constant string used as salt for HKDF computation const STR_AUTH_KEY: [u8; 7] = *b"AuthKey"; const STR_EXPORT_KEY: [u8; 9] = *b"ExportKey"; @@ -54,16 +53,14 @@ impl TryFrom for InnerEnvelopeMode { } } -/// This struct is an instantiation of the envelope as described in -/// +/// This struct is an instantiation of the envelope as described in /// -/// Note that earlier versions of this specification described an -/// implementation of this envelope using an encryption scheme that -/// satisfied random-key robustness -/// (). -/// The specification update has simplified this assumption by taking -/// an XOR-based approach without compromising on security, and to avoid -/// the confusion around the implementation of an RKR-secure encryption. +/// Note that earlier versions of this specification described an implementation +/// of this envelope using an encryption scheme that satisfied random-key +/// robustness (). +/// The specification update has simplified this assumption by taking an +/// XOR-based approach without compromising on security, and to avoid the +/// confusion around the implementation of an RKR-secure encryption. #[derive(DeriveWhere)] #[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))] pub(crate) struct Envelope @@ -77,9 +74,10 @@ where hmac: Output, } -// Note that this struct represents an envelope that has been "opened" with the asssociated -// key. This key is also used to derive the export_key parameter, which is technically -// unrelated to the envelope's encrypted and authenticated contents. +// Note that this struct represents an envelope that has been "opened" with the +// asssociated key. This key is also used to derive the export_key parameter, +// which is technically unrelated to the envelope's encrypted and authenticated +// contents. pub(crate) struct OpenedEnvelope<'a, CS: CipherSuite> where ::Core: ProxyHash, @@ -155,8 +153,8 @@ where )) } - /// Uses a key to convert the plaintext into an envelope, authenticated by the aad field. - /// Note that a new nonce is sampled for each call to seal. + /// Uses a key to convert the plaintext into an envelope, authenticated by + /// the aad field. Note that a new nonce is sampled for each call to seal. #[allow(clippy::type_complexity)] pub(crate) fn seal_raw<'a>( randomized_pwd_hasher: Hkdf, @@ -225,8 +223,8 @@ where }) } - /// Attempts to decrypt the envelope using a key, which is successful only if the key and - /// aad used to construct the envelope are the same. + /// Attempts to decrypt the envelope using a key, which is successful only + /// if the key and aad used to construct the envelope are the same. pub(crate) fn open_raw<'a>( &self, randomized_pwd_hasher: Hkdf, diff --git a/src/errors.rs b/src/errors.rs index 178ec48..e35cd38 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -140,7 +140,8 @@ pub enum ProtocolError { /** This error occurs when the client detects that the server has reflected the OPRF value (beta == alpha) */ ReflectedValueError, - /// Identity group element was encountered during deserialization, which is invalid + /** Identity group element was encountered during deserialization, which is + invalid */ IdentityGroupElementError, } @@ -169,9 +170,9 @@ impl From> for ProtocolError { } } -// See https://github.com/rust-lang/rust/issues/64715 and remove this when -// merged, and https://github.com/dtolnay/thiserror/issues/62 for why this -// comes up in our doc tests. +// See https://github.com/rust-lang/rust/issues/64715 and remove this when merged, +// and https://github.com/dtolnay/thiserror/issues/62 for why this comes up in our +// doc tests. impl From<::core::convert::Infallible> for ProtocolError { fn from(_: ::core::convert::Infallible) -> Self { unreachable!() diff --git a/src/hash.rs b/src/hash.rs index a33926c..f28e441 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -31,9 +31,9 @@ where { } -/// Trait inheriting the requirements from digest::Digest for compatibility with HKDF and HMAC -// Associated types could be simplified when they are made as defaults: -// https://github.com/rust-lang/rust/issues/29661 +/// Trait inheriting the requirements from digest::Digest for compatibility with +/// HKDF and HMAC Associated types could be simplified when they are made as +/// defaults: pub trait Hash: Digest + OutputSizeUser> diff --git a/src/key_exchange/group/mod.rs b/src/key_exchange/group/mod.rs index 3fcf5df..dad5163 100644 --- a/src/key_exchange/group/mod.rs +++ b/src/key_exchange/group/mod.rs @@ -5,13 +5,13 @@ // License, Version 2.0 found in the LICENSE-APACHE file in the root directory // of this source tree. -//! Includes the KeGroup trait and definitions for the -//! key exchange groups +//! Includes the KeGroup trait and definitions for the key exchange groups -use crate::errors::InternalError; use generic_array::{ArrayLength, GenericArray}; use rand::{CryptoRng, RngCore}; +use crate::errors::InternalError; + /// A group representation for use in the key exchange pub trait KeGroup: Sized + Clone { /// Length of the public key diff --git a/src/key_exchange/group/p256.rs b/src/key_exchange/group/p256.rs index 92b9d60..50eb9cb 100644 --- a/src/key_exchange/group/p256.rs +++ b/src/key_exchange/group/p256.rs @@ -7,8 +7,6 @@ //! Key Exchange group implementation for p256 -use super::KeGroup; -use crate::errors::InternalError; use generic_array::typenum::{U32, U33}; use generic_array::GenericArray; use p256_::elliptic_curve::group::GroupEncoding; @@ -17,6 +15,9 @@ use p256_::elliptic_curve::{PublicKey, SecretKey}; use p256_::NistP256; use rand::{CryptoRng, RngCore}; +use super::KeGroup; +use crate::errors::InternalError; + impl KeGroup for PublicKey { type PkLen = U33; type SkLen = U32; diff --git a/src/key_exchange/group/ristretto255.rs b/src/key_exchange/group/ristretto255.rs index 0121894..c4c6ebd 100644 --- a/src/key_exchange/group/ristretto255.rs +++ b/src/key_exchange/group/ristretto255.rs @@ -7,8 +7,6 @@ //! Key Exchange group implementation for ristretto255 -use super::KeGroup; -use crate::errors::InternalError; use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT; use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; use curve25519_dalek::scalar::Scalar; @@ -16,6 +14,9 @@ use generic_array::typenum::U32; use generic_array::GenericArray; use rand::{CryptoRng, RngCore}; +use super::KeGroup; +use crate::errors::InternalError; + impl KeGroup for RistrettoPoint { type PkLen = U32; type SkLen = U32; @@ -36,7 +37,8 @@ impl KeGroup for RistrettoPoint { Scalar::from_bytes_mod_order_wide(&scalar_bytes) } - // Tests need an exact conversion from bytes to scalar, sampling only 32 bytes from rng + // Tests need an exact conversion from bytes to scalar, sampling only 32 bytes + // from rng #[cfg(test)] { let mut scalar_bytes = [0u8; 32]; diff --git a/src/key_exchange/group/x25519.rs b/src/key_exchange/group/x25519.rs index 07070ab..18d2067 100644 --- a/src/key_exchange/group/x25519.rs +++ b/src/key_exchange/group/x25519.rs @@ -7,12 +7,14 @@ //! Key Exchange group implementation for X25519 -use super::KeGroup; -use crate::errors::InternalError; -use generic_array::{typenum::U32, GenericArray}; +use generic_array::typenum::U32; +use generic_array::GenericArray; use rand::{CryptoRng, RngCore}; use x25519_dalek::{PublicKey, StaticSecret}; +use super::KeGroup; +use crate::errors::InternalError; + /// The implementation of such a subgroup for Ristretto impl KeGroup for PublicKey { type PkLen = U32; diff --git a/src/key_exchange/mod.rs b/src/key_exchange/mod.rs index 47fcb30..a4cfd51 100644 --- a/src/key_exchange/mod.rs +++ b/src/key_exchange/mod.rs @@ -5,8 +5,8 @@ // License, Version 2.0 found in the LICENSE-APACHE file in the root directory // of this source tree. -//! Includes instantiations of key exchange protocols used in the -//! login step for OPAQUE +//! Includes instantiations of key exchange protocols used in the login step for +//! OPAQUE pub mod group; pub(crate) mod traits; diff --git a/src/key_exchange/traits.rs b/src/key_exchange/traits.rs index 7f33904..4e2bfe8 100755 --- a/src/key_exchange/traits.rs +++ b/src/key_exchange/traits.rs @@ -5,14 +5,6 @@ // License, Version 2.0 found in the LICENSE-APACHE file in the root directory // of this source tree. -use crate::hash::ProxyHash; -use crate::key_exchange::group::KeGroup; -use crate::{ - ciphersuite::CipherSuite, - errors::ProtocolError, - hash::Hash, - keypair::{PrivateKey, PublicKey, SecretKey}, -}; use digest::core_api::BlockSizeUser; use digest::Output; use generic_array::typenum::{IsLess, Le, NonZero, U256}; @@ -20,6 +12,12 @@ use generic_array::{ArrayLength, GenericArray}; use rand::{CryptoRng, RngCore}; use zeroize::Zeroize; +use crate::ciphersuite::CipherSuite; +use crate::errors::ProtocolError; +use crate::hash::{Hash, ProxyHash}; +use crate::key_exchange::group::KeGroup; +use crate::keypair::{PrivateKey, PublicKey, SecretKey}; + #[cfg(not(test))] pub type GenerateKe2Result = ( >::KE2State, diff --git a/src/key_exchange/tripledh.rs b/src/key_exchange/tripledh.rs index b5e7631..65935f1 100755 --- a/src/key_exchange/tripledh.rs +++ b/src/key_exchange/tripledh.rs @@ -6,34 +6,29 @@ // of this source tree. //! An implementation of the Triple Diffie-Hellman key exchange protocol -use crate::{ - errors::{ - utils::{check_slice_size, check_slice_size_atleast}, - InternalError, ProtocolError, - }, - hash::{Hash, OutputSize, ProxyHash}, - key_exchange::{ - group::KeGroup, - traits::{FromBytes, GenerateKe2Result, GenerateKe3Result, KeyExchange, ToBytes}, - }, - keypair::{KeyPair, PrivateKey, PublicKey, SecretKey}, - serialization::{Serialize, UpdateExt}, -}; use core::convert::TryFrom; use core::ops::Add; + use derive_where::DeriveWhere; use digest::core_api::BlockSizeUser; use digest::{Digest, Output}; use generic_array::sequence::Concat; -use generic_array::typenum::{IsLess, Le, NonZero, U256}; -use generic_array::{ - typenum::{Sum, Unsigned, U1, U2, U32}, - ArrayLength, GenericArray, -}; +use generic_array::typenum::{IsLess, Le, NonZero, Sum, Unsigned, U1, U2, U256, U32}; +use generic_array::{ArrayLength, GenericArray}; use hkdf::{Hkdf, HkdfExtract}; use hmac::{Hmac, Mac}; use rand::{CryptoRng, RngCore}; +use crate::errors::utils::{check_slice_size, check_slice_size_atleast}; +use crate::errors::{InternalError, ProtocolError}; +use crate::hash::{Hash, OutputSize, ProxyHash}; +use crate::key_exchange::group::KeGroup; +use crate::key_exchange::traits::{ + FromBytes, GenerateKe2Result, GenerateKe3Result, KeyExchange, ToBytes, +}; +use crate::keypair::{KeyPair, PrivateKey, PublicKey, SecretKey}; +use crate::serialization::{Serialize, UpdateExt}; + /////////////// // Constants // // ========= // @@ -363,8 +358,9 @@ type TripleDHDerivationResult = (Output, Output, Output, Output); // Helper functions -// Internal function which takes the public and private components of the client and server keypairs, along -// with some auxiliary metadata, to produce the session key and two MAC keys +// Internal function which takes the public and private components of the client +// and server keypairs, along with some auxiliary metadata, to produce the +// session key and two MAC keys fn derive_3dh_keys>( dh: TripleDHComponents, hashed_derivation_transcript: &[u8], diff --git a/src/keypair.rs b/src/keypair.rs index 064e852..40fa8c8 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -9,15 +9,17 @@ #![allow(unsafe_code)] -use crate::errors::{InternalError, ProtocolError}; -use crate::key_exchange::group::KeGroup; use core::ops::Deref; + use derive_where::DeriveWhere; use generic_array::typenum::Unsigned; use generic_array::{ArrayLength, GenericArray}; use rand::{CryptoRng, RngCore}; use zeroize::Zeroize; +use crate::errors::{InternalError, ProtocolError}; +use crate::key_exchange::group::KeGroup; + /// A Keypair trait with public-private verification #[cfg_attr( feature = "serde", @@ -50,9 +52,9 @@ impl> KeyPair { } /// Check whether a public key is valid. This is meant to be applied on - /// material provided through the network which fits the key - /// representation (i.e. can be mapped to a curve point), but presents - /// some risk - e.g. small subgroup check + /// material provided through the network which fits the key representation + /// (i.e. can be mapped to a curve point), but presents some risk - e.g. + /// small subgroup check pub(crate) fn check_public_key(key: PublicKey) -> Result, InternalError> { KG::from_pk_slice(GenericArray::from_slice(&key.0)).map(|_| key) } @@ -87,10 +89,11 @@ impl KeyPair { /// generate_random fn uniform_keypair_strategy() -> proptest::prelude::BoxedStrategy { use proptest::prelude::*; - use rand::{rngs::StdRng, SeedableRng}; + use rand::rngs::StdRng; + use rand::SeedableRng; - // The no_shrink is because keypairs should be fixed -- shrinking would cause a different - // keypair to be generated, which appears to not be very useful. + // The no_shrink is because keypairs should be fixed -- shrinking would cause a + // different keypair to be generated, which appears to not be very useful. any::<[u8; 32]>() .prop_filter_map("valid random keypair", |seed| { let mut rng = StdRng::from_seed(seed); @@ -119,7 +122,8 @@ impl> Deref for Key { } } -// Don't make it implement SizedBytes so that it's not constructible outside of this module. +// Don't make it implement SizedBytes so that it's not constructible outside of +// this module. impl> Key { /// Convert to bytes pub fn to_arr(&self) -> GenericArray { @@ -246,12 +250,14 @@ impl PublicKey { #[cfg(test)] mod tests { - use super::*; - use crate::errors::*; use core::slice::from_raw_parts; + use std::vec; + use generic_array::typenum::Unsigned; use rand::rngs::OsRng; - use std::vec; + + use super::*; + use crate::errors::*; #[test] fn test_zeroize_key() -> Result<(), ProtocolError> { @@ -304,10 +310,12 @@ mod tests { macro_rules! test { ($mod:ident, $point:ty) => { mod $mod { - use super::*; - use proptest::prelude::*; use std::format; + use proptest::prelude::*; + + use super::*; + proptest! { #[test] fn check(kp in KeyPair::<$point>::uniform_keypair_strategy()) { @@ -353,6 +361,12 @@ mod tests { #[test] fn remote_key() { + #[cfg(feature = "ristretto255")] + use curve25519_dalek::ristretto::RistrettoPoint as KeCurve; + #[cfg(not(feature = "ristretto255"))] + use p256_::PublicKey as KeCurve; + use rand::rngs::OsRng; + use crate::{ CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginFinishResult, ClientLoginStartResult, ClientRegistration, ClientRegistrationFinishParameters, @@ -360,11 +374,6 @@ mod tests { ServerLoginStartParameters, ServerLoginStartResult, ServerRegistration, ServerRegistrationStartResult, ServerSetup, }; - #[cfg(feature = "ristretto255")] - use curve25519_dalek::ristretto::RistrettoPoint as KeCurve; - #[cfg(not(feature = "ristretto255"))] - use p256_::PublicKey as KeCurve; - use rand::rngs::OsRng; struct Default; diff --git a/src/lib.rs b/src/lib.rs index 8deac88..3764512 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,10 +5,12 @@ // License, Version 2.0 found in the LICENSE-APACHE file in the root directory // of this source tree. -//! An implementation of the OPAQUE asymmetric password authentication key exchange protocol +//! An implementation of the OPAQUE asymmetric password authentication key +//! exchange protocol //! //! Note: This implementation is in sync with [draft-irtf-cfrg-opaque-05](https://www.ietf.org/archive/id/draft-irtf-cfrg-opaque-05.html), -//! but this specification is subject to change, until the final version published by the IETF. +//! but this specification is subject to change, until the final version +//! published by the IETF. //! //! ### Minimum Supported Rust Version //! @@ -16,8 +18,9 @@ //! //! # Overview //! -//! OPAQUE is a protocol between a client and a server. They must first agree on a collection of primitives -//! to be kept consistent throughout protocol execution. These include: +//! OPAQUE is a protocol between a client and a server. They must first agree on +//! a collection of primitives to be kept consistent throughout protocol +//! execution. These include: //! * a finite cyclic group along with a point representation //! * for the OPRF and //! * for the key exchange @@ -40,12 +43,15 @@ //! See [examples/simple_login.rs](https://github.com/novifinancial/opaque-ke/blob/main/examples/simple_login.rs) //! for a working example of a simple password-based login using OPAQUE. //! -//! 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 `Argon2`, -//! which can be enabled through the `slow-hash` feature. See more details in the [features](#features) section. +//! 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 `Argon2`, +//! which can be enabled through the `slow-hash` feature. See more details in +//! the [features](#features) section. //! //! ## Setup -//! To set up the protocol, the server begins by creating a `ServerSetup` object: +//! To set up the protocol, the server begins by creating a `ServerSetup` +//! object: //! ``` //! # use opaque_ke::errors::ProtocolError; //! # use opaque_ke::CipherSuite; @@ -67,24 +73,33 @@ //! # type Hash = sha2::Sha256; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } -//! use rand::{rngs::OsRng, RngCore}; +//! use rand::rngs::OsRng; +//! use rand::RngCore; //! let mut rng = OsRng; //! let server_setup = ServerSetup::::new(&mut rng); //! # Ok::<(), ProtocolError>(()) //! ``` -//! The server must persist an instance of [ServerSetup] for the registration and login steps. +//! The server must persist an instance of [ServerSetup] for the registration +//! and login steps. //! //! ## Registration -//! The registration protocol between the client and server consists of four steps along with three messages: -//! [RegistrationRequest], [RegistrationResponse], and [RegistrationUpload]. A successful execution of the registration protocol results in the -//! server producing a password file corresponding to a server-side identifier for the client, along with the password provided by -//! the client. This password file is typically stored in a key-value database, where the keys consist of these server-side identifiers for each client, -//! and the values consist of their corresponding password files, to be retrieved upon future login attempts made by the client. +//! The registration protocol between the client and server consists of four +//! steps along with three messages: [RegistrationRequest], +//! [RegistrationResponse], and [RegistrationUpload]. A successful execution of +//! the registration protocol results in the server producing a password file +//! corresponding to a server-side identifier for the client, along with the +//! password provided by the client. This password file is typically stored in a +//! key-value database, where the keys consist of these server-side identifiers +//! for each client, and the values consist of their corresponding password +//! files, to be retrieved upon future login attempts made by the client. //! //! ### Client Registration Start -//! In the first step of registration, the client chooses as input a registration password. The client runs [ClientRegistration::start] -//! to produce a [ClientRegistrationStartResult], which consists of a [RegistrationRequest] to be sent to the server and -//! a [ClientRegistration] which must be persisted on the client for the final step of client registration. +//! In the first step of registration, the client chooses as input a +//! registration password. The client runs [ClientRegistration::start] to +//! produce a [ClientRegistrationStartResult], which consists of a +//! [RegistrationRequest] to be sent to the server and a [ClientRegistration] +//! which must be persisted on the client for the final step of client +//! registration. //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, @@ -110,20 +125,20 @@ //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } //! use opaque_ke::ClientRegistration; -//! use rand::{rngs::OsRng, RngCore}; +//! use rand::rngs::OsRng; +//! use rand::RngCore; //! let mut client_rng = OsRng; -//! let client_registration_start_result = ClientRegistration::::start( -//! &mut client_rng, -//! b"password", -//! )?; +//! let client_registration_start_result = +//! ClientRegistration::::start(&mut client_rng, b"password")?; //! # Ok::<(), ProtocolError>(()) //! ``` //! //! ### Server Registration Start -//! In the second step of registration, the server takes as input a persisted instance of [ServerSetup], a [RegistrationRequest] from the client, and -//! a server-side identifier for the client. -//! The server runs [ServerRegistration::start] to produce a [ServerRegistrationStartResult], which consists of -//! a [RegistrationResponse] to be returned to the client. +//! In the second step of registration, the server takes as input a persisted +//! instance of [ServerSetup], a [RegistrationRequest] from the client, and a +//! server-side identifier for the client. The server runs +//! [ServerRegistration::start] to produce a [ServerRegistrationStartResult], +//! which consists of a [RegistrationResponse] to be returned to the client. //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, @@ -167,11 +182,13 @@ //! ``` //! //! ### Client Registration Finish -//! In the third step of registration, the client takes as input -//! a [RegistrationResponse] from the server, and -//! a [ClientRegistration] from the first step of registration. -//! The client runs [ClientRegistration::finish] to produce a [ClientRegistrationFinishResult], which consists of a [RegistrationUpload] -//! to be sent to the server and an `export_key` field which can be used optionally as described in the [Export Key](#export-key) section. +//! In the third step of registration, the client takes as input a +//! [RegistrationResponse] from the server, and a [ClientRegistration] from the +//! first step of registration. The client runs [ClientRegistration::finish] to +//! produce a [ClientRegistrationFinishResult], which consists of a +//! [RegistrationUpload] to be sent to the server and an `export_key` field +//! which can be used optionally as described in the [Export Key](#export-key) +//! section. //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, @@ -215,12 +232,13 @@ //! ``` //! //! ### Server Registration Finish -//! In the fourth step of registration, the server takes as input -//! a [RegistrationUpload] from the client, and -//! a [ServerRegistration] from the second step. -//! The server runs [ServerRegistration::finish] to produce a finalized [ServerRegistration]. -//! At this point, the client can be considered as successfully registered, and the server can invoke -//! [ServerRegistration::serialize] to store the password file for use during the login protocol. +//! In the fourth step of registration, the server takes as input a +//! [RegistrationUpload] from the client, and a [ServerRegistration] from the +//! second step. The server runs [ServerRegistration::finish] to produce a +//! finalized [ServerRegistration]. At this point, the client can be considered +//! as successfully registered, and the server can invoke +//! [ServerRegistration::serialize] to store the password file for use during +//! the login protocol. //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, @@ -262,17 +280,20 @@ //! ``` //! //! ## Login -//! The login protocol between a client and server also consists of four steps along with three messages: -//! [CredentialRequest], [CredentialResponse], [CredentialFinalization]. The server is expected to have access to the password file -//! corresponding to an output of the registration phase (see [Dummy Server Login](#dummy-server-login) for handling the scenario where -//! no password file is available). The login protocol will execute successfully only if the same password -//! was used in the registration phase that produced the password file that the server is testing against. +//! The login protocol between a client and server also consists of four steps +//! along with three messages: [CredentialRequest], [CredentialResponse], +//! [CredentialFinalization]. The server is expected to have access to the +//! password file corresponding to an output of the registration phase (see +//! [Dummy Server Login](#dummy-server-login) for handling the scenario where no +//! password file is available). The login protocol will execute successfully +//! only if the same password was used in the registration phase that produced +//! the password file that the server is testing against. //! //! ### Client Login Start //! In the first step of login, the client chooses as input a login password. -//! The client runs [ClientLogin::start] to produce an output consisting of -//! a [CredentialRequest] to be sent to the server, and -//! a [ClientLogin] which must be persisted on the client for the final step of client login. +//! The client runs [ClientLogin::start] to produce an output consisting of a +//! [CredentialRequest] to be sent to the server, and a [ClientLogin] which must +//! be persisted on the client for the final step of client login. //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, @@ -300,22 +321,18 @@ //! # use rand::{rngs::OsRng, RngCore}; //! use opaque_ke::ClientLogin; //! let mut client_rng = OsRng; -//! let client_login_start_result = ClientLogin::::start( -//! &mut client_rng, -//! b"password", -//! )?; +//! let client_login_start_result = ClientLogin::::start(&mut client_rng, b"password")?; //! # Ok::<(), ProtocolError>(()) //! ``` //! //! ### Server Login Start -//! In the second step of login, the server takes as input -//! a persisted instance of [ServerSetup], -//! the password file output from registration, -//! a [CredentialRequest] from the client, and -//! a server-side identifier for the client. -//! The server runs [ServerLogin::start] to produce an output consisting of -//! a [CredentialResponse] which is returned to the client, and -//! a [ServerLogin] which must be persisted on the server for the final step of login. +//! In the second step of login, the server takes as input a persisted instance +//! of [ServerSetup], the password file output from registration, a +//! [CredentialRequest] from the client, and a server-side identifier for the +//! client. The server runs [ServerLogin::start] to produce an output consisting +//! of a [CredentialResponse] which is returned to the client, and a +//! [ServerLogin] which must be persisted on the server for the final step of +//! login. //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, @@ -368,17 +385,19 @@ //! )?; //! # Ok::<(), ProtocolError>(()) //! ``` -//! Note that if there is no corresponding password file found for the user, -//! the server can use `None` in place of `Some(password_file)` in order to generate -//! a [CredentialResponse] that is indistinguishable from a valid [CredentialResponse] -//! returned for a registered client. This allows the server to prevent leaking information -//! about whether or not a client has previously registered with the server. +//! Note that if there is no corresponding password file found for the user, the +//! server can use `None` in place of `Some(password_file)` in order to generate +//! a [CredentialResponse] that is indistinguishable from a valid +//! [CredentialResponse] returned for a registered client. This allows the +//! server to prevent leaking information about whether or not a client has +//! previously registered with the server. //! //! ### Client Login Finish -//! In the third step of login, the client takes as input a [CredentialResponse] from the server. -//! The client runs [ClientLogin::finish] and produces an output consisting of -//! a [CredentialFinalization] to be sent to the server to complete the protocol, -//! the `session_key` sequence of bytes which will match the server's session key upon a successful login. +//! In the third step of login, the client takes as input a [CredentialResponse] +//! from the server. The client runs [ClientLogin::finish] and produces an +//! output consisting of a [CredentialFinalization] to be sent to the server to +//! complete the protocol, the `session_key` sequence of bytes which will match +//! the server's session key upon a successful login. //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, @@ -433,8 +452,10 @@ //! ``` //! //! ### Server Login Finish -//! In the fourth step of login, the server takes as input a [CredentialFinalization] from the client and runs [ServerLogin::finish] to -//! produce an output consisting of the `session_key` sequence of bytes which will match the client's session key upon a successful login. +//! In the fourth step of login, the server takes as input a +//! [CredentialFinalization] from the client and runs [ServerLogin::finish] to +//! produce an output consisting of the `session_key` sequence of bytes which +//! will match the client's session key upon a successful login. //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, @@ -495,31 +516,43 @@ //! ); //! # Ok::<(), ProtocolError>(()) //! ``` -//! If the protocol completes successfully, then the server obtains a `server_login_finish_result.session_key` which is guaranteed to -//! match `client_login_finish_result.session_key` (see the [Session Key](#session-key) section). -//! Otherwise, on failure, the [ServerLogin::finish] algorithm outputs the error [InvalidLoginError](errors::ProtocolError::InvalidLoginError). +//! If the protocol completes successfully, then the server obtains a +//! `server_login_finish_result.session_key` which is guaranteed to match +//! `client_login_finish_result.session_key` (see the [Session +//! Key](#session-key) section). Otherwise, on failure, the +//! [ServerLogin::finish] algorithm outputs the error +//! [InvalidLoginError](errors::ProtocolError::InvalidLoginError). //! //! # Advanced Usage //! -//! This implementation offers support for several optional features of OPAQUE, described below. They are not critical to the -//! execution of the main protocol, but can provide additional security benefits which can be suitable for various applications that rely on -//! OPAQUE for authentication. +//! This implementation offers support for several optional features of OPAQUE, +//! described below. They are not critical to the execution of the main +//! protocol, but can provide additional security benefits which can be suitable +//! for various applications that rely on OPAQUE for authentication. //! //! ## Session Key //! -//! Upon a successful completion of the OPAQUE protocol (the client runs login with the same password used during registration), -//! the client and server have access to a session key, which is a pseudorandomly distributed 32-byte string which only the client -//! and server know. Multiple login runs using the same password for the same client will produce different session keys, distributed -//! as uniformly random strings. Thus, the session key can be used to establish a secure channel between the client and server. +//! Upon a successful completion of the OPAQUE protocol (the client runs login +//! with the same password used during registration), the client and server have +//! access to a session key, which is a pseudorandomly distributed 32-byte +//! string which only the client and server know. Multiple login runs using the +//! same password for the same client will produce different session keys, +//! distributed as uniformly random strings. Thus, the session key can be used +//! to establish a secure channel between the client and server. //! -//! The session key can be accessed from the `session_key` field of [ClientLoginFinishResult] and [ServerLoginFinishResult]. See -//! the combination of [Client Login Finish](#client-login-finish) and [Server Login Finish](#server-login-finish) for example usage. +//! The session key can be accessed from the `session_key` field of +//! [ClientLoginFinishResult] and [ServerLoginFinishResult]. See the combination +//! of [Client Login Finish](#client-login-finish) and [Server Login +//! Finish](#server-login-finish) for example usage. //! //! ## Checking Server Consistency //! -//! A [ClientLoginFinishResult] contains the `server_s_pk` field, which is represents the static public key of the server that is established -//! during the setup phase. This can be used by the client to verify the authenticity of the server it engages with during the login phase. In particular, -//! the client can check that the static public key of the server supplied during registration (with the `server_s_pk` field of +//! A [ClientLoginFinishResult] contains the `server_s_pk` field, which is +//! represents the static public key of the server that is established during +//! the setup phase. This can be used by the client to verify the authenticity +//! of the server it engages with during the login phase. In particular, the +//! client can check that the static public key of the server supplied during +//! registration (with the `server_s_pk` field of //! [ClientRegistrationFinishResult]) matches this field during login. //! ``` //! # use opaque_ke::{ @@ -590,26 +623,36 @@ //! # Ok::<(), ProtocolError>(()) //! ``` //! -//! Note that without this check over the consistency of the server's static public key, a malicious actor could impersonate the registration server if it were able to copy the password -//! file output during registration! Therefore, it is recommended to perform the following check in the application layer if the client can obtain a copy of the server's static +//! Note that without this check over the consistency of the server's static +//! public key, a malicious actor could impersonate the registration server if +//! it were able to copy the password file output during registration! +//! Therefore, it is recommended to perform the following check in the +//! application layer if the client can obtain a copy of the server's static //! public key beforehand. //! //! //! ## Export Key //! -//! The export key is a pseudorandomly distributed 32-byte string output by both the -//! [Client Registration Finish](#client-registration-finish) and [Client Login Finish](#client-login-finish) steps. -//! The same export key string will be output by both functions only if the exact same password is passed to [ClientRegistration::start] and [ClientLogin::start]. +//! The export key is a pseudorandomly distributed 32-byte string output by both +//! the [Client Registration Finish](#client-registration-finish) and [Client +//! Login Finish](#client-login-finish) steps. The same export key string will +//! be output by both functions only if the exact same password is passed to +//! [ClientRegistration::start] and [ClientLogin::start]. //! -//! The export key retains as much secrecy as the password itself, and is similarly derived through an evaluation of the slow hashing function. Hence, only the parties which -//! know the password the client uses during registration and login can recover this secret, as it is never exposed to the server. As a result, the export key -//! can be used (separately from the OPAQUE protocol) to provide confidentiality and integrity to other data which only the client should be able to process. -//! For instance, if the server is expected to maintain any client-side secrets which require a password to access, then this export key can be used to encrypt -//! these secrets so that they remain hidden from the server (see [examples/digital_locker.rs](https://github.com/novifinancial/opaque-ke/blob/main/examples/digital_locker.rs) +//! The export key retains as much secrecy as the password itself, and is +//! similarly derived through an evaluation of the slow hashing function. Hence, +//! only the parties which know the password the client uses during registration +//! and login can recover this secret, as it is never exposed to the server. As +//! a result, the export key can be used (separately from the OPAQUE protocol) +//! to provide confidentiality and integrity to other data which only the client +//! should be able to process. For instance, if the server is expected to +//! maintain any client-side secrets which require a password to access, then +//! this export key can be used to encrypt these secrets so that they remain +//! hidden from the server (see [examples/digital_locker.rs](https://github.com/novifinancial/opaque-ke/blob/main/examples/digital_locker.rs) //! for a working example). //! -//! You can access the export key from the `export_key` field of [ClientRegistrationFinishResult] and [ClientLoginFinishResult]. -//! +//! You can access the export key from the `export_key` field of +//! [ClientRegistrationFinishResult] and [ClientLoginFinishResult]. //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, @@ -678,15 +721,21 @@ //! //! ## Custom Identifiers //! -//! Typically when applications use OPAQUE to authenticate a client to a server, the client has a registered username which is sent to the server to -//! identify the corresponding password file established during registration. This username may or may not coincide with the server-side identifier; -//! however, this username must be known to both the client and the server (whereas the server-side identifier does not need to be exposed to the client). -//! The server may also have an identifier corresponding to an entity (e.g. Facebook). -//! By default, neither of these public identifiers need to be supplied to the OPAQUE protocol. +//! Typically when applications use OPAQUE to authenticate a client to a server, +//! the client has a registered username which is sent to the server to identify +//! the corresponding password file established during registration. This +//! username may or may not coincide with the server-side identifier; however, +//! this username must be known to both the client and the server (whereas the +//! server-side identifier does not need to be exposed to the client). The +//! server may also have an identifier corresponding to an entity (e.g. +//! Facebook). By default, neither of these public identifiers need to be +//! supplied to the OPAQUE protocol. //! -//! But, for applications that wish to cryptographically bind these identities to -//! the registered password file as well as the session key output by the login phase, these custom identifiers can be specified through -//! [ClientRegistrationFinishParameters] in [Client Registration Finish](#client-registration-finish): +//! But, for applications that wish to cryptographically bind these identities +//! to the registered password file as well as the session key output by the +//! login phase, these custom identifiers can be specified through +//! [ClientRegistrationFinishParameters] in [Client Registration +//! Finish](#client-registration-finish): //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, @@ -735,7 +784,8 @@ //! # Ok::<(), ProtocolError>(()) //! ``` //! -//! The same identifiers must also be supplied using [ServerLoginStartParameters] in [Server Login Start](#server-login-start): +//! The same identifiers must also be supplied using +//! [ServerLoginStartParameters] in [Server Login Start](#server-login-start): //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, @@ -795,7 +845,8 @@ //! # Ok::<(), ProtocolError>(()) //! ``` //! -//! as well as [ClientLoginFinishParameters] in [Client Login Finish](#client-login-finish): +//! as well as [ClientLoginFinishParameters] in [Client Login +//! Finish](#client-login-finish): //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, @@ -856,36 +907,46 @@ //! //! # Ok::<(), ProtocolError>(()) //! ``` -//! Failing to supply the same pair of custom identifiers in any of the three steps above will result in an error in attempting to complete -//! the protocol! +//! Failing to supply the same pair of custom identifiers in any of the three +//! steps above will result in an error in attempting to complete the protocol! //! -//! Note that if only one of the client and server identifiers are present, then [Identifiers] can be -//! used to specify them individually. +//! Note that if only one of the client and server identifiers are present, then +//! [Identifiers] can be used to specify them individually. //! //! ## Key Exchange Context //! -//! A key exchange protocol typically allows for the specifying of shared "context" information between the two parties before the exchange is complete, -//! so as to bind the integrity of application-specific data or configuration parameters to the security of the key exchange. -//! During the login phase, the client and server can specify this context using: -//! - The second login message, where the server can populate [ServerLoginStartParameters], and -//! - The third login message, where the client can populate [ClientLoginFinishParameters]. +//! A key exchange protocol typically allows for the specifying of shared +//! "context" information between the two parties before the exchange is +//! complete, so as to bind the integrity of application-specific data or +//! configuration parameters to the security of the key exchange. During the +//! login phase, the client and server can specify this context using: +//! - The second login message, where the server can populate +//! [ServerLoginStartParameters], and +//! - The third login message, where the client can populate +//! [ClientLoginFinishParameters]. //! -//! For both of these messages, the `WithContextAndIdentifiers` variant can be used to specify these fields in addition to -//! [custom identifiers](#custom-identifiers), with the ordering of the fields as -//! `WithContextAndIdentifiers(context, Identifiers::ClientAndServerIdentifiers(username, server_name))`. +//! For both of these messages, the `WithContextAndIdentifiers` variant can be +//! used to specify these fields in addition to [custom +//! identifiers](#custom-identifiers), with the ordering of the fields as +//! `WithContextAndIdentifiers(context, +//! Identifiers::ClientAndServerIdentifiers(username, server_name))`. //! //! ## Dummy Server Login //! -//! For applications in which the server does not wish to reveal to the client whether an existing password file has been -//! registered, the server can return a "dummy" credential response message to the client for an unregistered client, -//! which is indistinguishable from the normal credential response message that the server would return for a registered client. -//! The dummy message is created by passing a `None` to the password_file parameter for [ServerLogin::start]. +//! For applications in which the server does not wish to reveal to the client +//! whether an existing password file has been registered, the server can return +//! a "dummy" credential response message to the client for an unregistered +//! client, which is indistinguishable from the normal credential response +//! message that the server would return for a registered client. The dummy +//! message is created by passing a `None` to the password_file parameter for +//! [ServerLogin::start]. //! //! ## Remote Private Keys //! -//! Servers that want to store their private key in an external location (e.g. in an HSM or vault) can do so with the -//! [`SecretKey`](keypair::SecretKey`) trait. This allows [`ServerSetup`] to be constructed using an existing keypair -//! without exposing the bytes of the private key to this library. +//! Servers that want to store their private key in an external location (e.g. +//! in an HSM or vault) can do so with the [`SecretKey`](keypair::SecretKey`) +//! trait. This allows [`ServerSetup`] to be constructed using an existing +//! keypair without exposing the bytes of the private key to this library. //! ``` //! # use generic_array::{GenericArray, typenum::U0}; //! # use opaque_ke::{CipherSuite, errors::{InternalError}, key_exchange::group::KeGroup, keypair::{KeyPair, PrivateKey, PublicKey, SecretKey}, ServerSetup}; @@ -952,36 +1013,44 @@ //! //! # Features //! -//! - The `slow-hash` feature, when enabled, introduces a dependency on `argon2` and implements the `SlowHash` trait for `Argon2` -//! 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 `slow-hash` feature, when enabled, introduces a dependency on `argon2` +//! and implements the `SlowHash` trait for `Argon2` 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 `serde` feature, enabled by default, provides convenience functions for serializing and deserializing with -//! [serde](https://serde.rs/). +//! - The `serde` feature, enabled by default, provides convenience functions for serializing and deserializing with [serde](https://serde.rs/). //! -//! - The backend features are re-exported from -//! [curve25519-dalek](https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) and allow for selecting -//! the corresponding backend for the curve arithmetic used. The `ristretto255_u64` feature is included as the default. -//! Other features are mapped as `ristretto255_u32`, `ristretto255_fiat_u64` and `ristretto255_fiat_u32`. Any `ristretto255_*` -//! backend feature will enable the `ristretto255` feature, which can be used too, but keep in mind that `curve25519-dalek` -//! will fail to compile without a selected backend. This enabled to use `curve25519_dalek::ristretto::RistrettoPoint` as a +//! - The backend features are re-exported from [curve25519-dalek](https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) +//! and allow for selecting the corresponding backend for the curve arithmetic +//! used. The `ristretto255_u64` feature is included as the default. Other +//! features are mapped as `ristretto255_u32`, `ristretto255_fiat_u64` and +//! `ristretto255_fiat_u32`. Any `ristretto255_*` backend feature will enable +//! the `ristretto255` feature, which can be used too, but keep in mind that +//! `curve25519-dalek` will fail to compile without a selected backend. This +//! enables the use of `curve25519_dalek::ristretto::RistrettoPoint` as a //! `KeGroup` and `OprfGroup`. //! -//! - The `x25519` feature is similar to the `ristretto255` feature and requires to select a backend like `x25519_u64`, other -//! backends are the same as in `ristretto255_*`. This enables `x25519_dalek::PublicKey` as a `KeGroup`. +//! - The `x25519` feature is similar to the `ristretto255` feature and requires +//! to select a backend like `x25519_u64`, other backends are the same as in +//! `ristretto255_*`. This enables `x25519_dalek::PublicKey` as a `KeGroup`. //! -//! - The `ristretto255_simd` feature is re-exported from -//! [curve25519-dalek](https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) and enables parallel formulas, -//! using either AVX2 or AVX512-IFMA. This will automatically enable the `ristretto255_u64` feature and requires Rust nightly. +//! - The `ristretto255_simd` feature is re-exported from [curve25519-dalek](https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) +//! and enables parallel formulas, using either AVX2 or AVX512-IFMA. This will +//! automatically enable the `ristretto255_u64` feature and requires Rust +//! nightly. //! -//! - The `p256` feature enables the use of `p256::PublicKey` as a `KeGroup` and `p256::ProjectivePoint` as a `OprfGroup` for -//! `CipherSuite`. Note that this is currently an experimental feature ⚠️, and is not yet ready for production use. -//! -//! - The `bench` feature is used only for running performance benchmarks for this implementation. +//! - The `p256` feature enables the use of `p256::PublicKey` as a `KeGroup` and +//! `p256::ProjectivePoint` as a `OprfGroup` for `CipherSuite`. Note that this +//! is currently an experimental feature ⚠️, and is not yet ready for +//! production use. //! +//! - The `bench` feature is used only for running performance benchmarks for +//! this implementation. #![deny(unsafe_code)] #![no_std] @@ -1011,27 +1080,18 @@ mod tests; // Exports +pub use ciphersuite::CipherSuite; pub use rand; -pub use ciphersuite::CipherSuite; - pub use crate::messages::{ - CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest, - RegistrationResponse, RegistrationUpload, -}; -pub use crate::messages::{ - CredentialFinalizationLen, CredentialRequestLen, CredentialResponseLen, RegistrationRequestLen, - RegistrationResponseLen, RegistrationUploadLen, -}; -pub use crate::opaque::ServerRegistrationLen; -pub use crate::opaque::{ - ClientLogin, ClientRegistration, ServerLogin, ServerRegistration, ServerSetup, + CredentialFinalization, CredentialFinalizationLen, CredentialRequest, CredentialRequestLen, + CredentialResponse, CredentialResponseLen, RegistrationRequest, RegistrationRequestLen, + RegistrationResponse, RegistrationResponseLen, RegistrationUpload, RegistrationUploadLen, }; pub use crate::opaque::{ - ClientLoginFinishParameters, ClientRegistrationFinishParameters, ServerLoginStartParameters, -}; -pub use crate::opaque::{ - ClientLoginFinishResult, ClientLoginStartResult, ClientRegistrationFinishResult, - ClientRegistrationStartResult, Identifiers, ServerLoginFinishResult, ServerLoginStartResult, - ServerRegistrationStartResult, + ClientLogin, ClientLoginFinishParameters, ClientLoginFinishResult, ClientLoginStartResult, + ClientRegistration, ClientRegistrationFinishParameters, ClientRegistrationFinishResult, + ClientRegistrationStartResult, Identifiers, ServerLogin, ServerLoginFinishResult, + ServerLoginStartParameters, ServerLoginStartResult, ServerRegistration, ServerRegistrationLen, + ServerRegistrationStartResult, ServerSetup, }; diff --git a/src/messages.rs b/src/messages.rs index ba58d9f..c071997 100755 --- a/src/messages.rs +++ b/src/messages.rs @@ -7,34 +7,30 @@ //! Contains the messages used for OPAQUE -use crate::{ - ciphersuite::CipherSuite, - envelope::{Envelope, EnvelopeLen}, - errors::{ - utils::{check_slice_size, check_slice_size_atleast}, - ProtocolError, - }, - hash::{OutputSize, ProxyHash}, - key_exchange::{ - group::KeGroup, - traits::{FromBytes, Ke1MessageLen, Ke2MessageLen, Ke3MessageLen, KeyExchange, ToBytes}, - tripledh::NonceLen, - }, - keypair::{KeyPair, PublicKey, SecretKey}, - opaque::{MaskedResponse, MaskedResponseLen, ServerSetup}, -}; use core::ops::Add; + use derive_where::DeriveWhere; use digest::core_api::{BlockSizeUser, CoreProxy}; use digest::Output; use generic_array::sequence::Concat; -use generic_array::{ - typenum::{IsLess, Le, NonZero, Sum, Unsigned, U256}, - ArrayLength, GenericArray, -}; +use generic_array::typenum::{IsLess, Le, NonZero, Sum, Unsigned, U256}; +use generic_array::{ArrayLength, GenericArray}; use rand::{CryptoRng, RngCore}; use voprf::Group; +use crate::ciphersuite::CipherSuite; +use crate::envelope::{Envelope, EnvelopeLen}; +use crate::errors::utils::{check_slice_size, check_slice_size_atleast}; +use crate::errors::ProtocolError; +use crate::hash::{OutputSize, ProxyHash}; +use crate::key_exchange::group::KeGroup; +use crate::key_exchange::traits::{ + FromBytes, Ke1MessageLen, Ke2MessageLen, Ke3MessageLen, KeyExchange, ToBytes, +}; +use crate::key_exchange::tripledh::NonceLen; +use crate::keypair::{KeyPair, PublicKey, SecretKey}; +use crate::opaque::{MaskedResponse, MaskedResponseLen, ServerSetup}; + //////////////////////////// // High-level API Structs // // ====================== // @@ -91,8 +87,8 @@ where <::Core as BlockSizeUser>::BlockSize: IsLess, Le<<::Core as BlockSizeUser>::BlockSize, U256>: NonZero, { - /// The "envelope" generated by the user, containing sealed - /// cryptographic identifiers + /// The "envelope" generated by the user, containing sealed cryptographic + /// identifiers pub(crate) envelope: Envelope, /// The masking key used to mask the envelope pub(crate) masking_key: Output, @@ -139,8 +135,8 @@ impl_serialize_and_deserialize_for!( CredentialRequestLen: ArrayLength, ); -/// The answer sent by the server to the user, upon reception of the -/// login attempt +/// The answer sent by the server to the user, upon reception of the login +/// attempt #[derive(DeriveWhere)] #[derive_where(Clone)] #[derive_where( @@ -179,8 +175,8 @@ impl_serialize_and_deserialize_for!( CredentialResponseLen: ArrayLength, ); -/// The answer sent by the client to the server, upon reception of the -/// sealed envelope +/// The answer sent by the client to the server, upon reception of the sealed +/// envelope #[derive(DeriveWhere)] #[derive_where(Clone)] #[derive_where( @@ -275,8 +271,8 @@ where } #[cfg(test)] - /// Only used for tests, where we can set the beta value to test for the reflection - /// error case + /// Only used for tests, where we can set the beta value to test for the + /// reflection error case pub fn set_evaluation_element_for_testing(&self, beta: CS::OprfGroup) -> Self { Self { evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta), @@ -383,8 +379,8 @@ where let checked_slice = check_slice_size_atleast(input, elem_len, "login_first_message_bytes")?; - // Check that the message is actually containing an element of the - // correct subgroup + // Check that the message is actually containing an element of the correct + // subgroup let blinded_element = voprf::BlindedElement::::deserialize( &checked_slice[..elem_len], )?; @@ -476,8 +472,8 @@ where "credential_response_bytes", )?; - // Check that the message is actually containing an element of the - // correct subgroup + // Check that the message is actually containing an element of the correct + // subgroup let beta_bytes = &checked_slice[..elem_len]; let evaluation_element = voprf::EvaluationElement::::deserialize(beta_bytes)?; @@ -506,8 +502,8 @@ where } #[cfg(test)] - /// Only used for tests, where we can set the beta value to test for the reflection - /// error case + /// Only used for tests, where we can set the beta value to test for the + /// reflection error case pub fn set_evaluation_element_for_testing(&self, beta: CS::OprfGroup) -> Self { Self { evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta), diff --git a/src/opaque.rs b/src/opaque.rs index 82fcbd6..d3f3911 100755 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -7,38 +7,39 @@ //! Provides the main OPAQUE API -use crate::{ - ciphersuite::CipherSuite, - envelope::{Envelope, EnvelopeLen}, - errors::{utils::check_slice_size, InternalError, ProtocolError}, - hash::{Hash, OutputSize, ProxyHash}, - key_exchange::{ - group::KeGroup, - traits::{FromBytes, Ke1MessageLen, Ke1StateLen, Ke2StateLen, KeyExchange, ToBytes}, - tripledh::NonceLen, - }, - keypair::{KeyPair, PrivateKey, PublicKey, SecretKey}, - messages::{CredentialRequestLen, RegistrationUploadLen}, - serialization::Serialize, - slow_hash::SlowHash, - CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest, - RegistrationResponse, RegistrationUpload, -}; use core::marker::PhantomData; use core::ops::Add; + use derive_where::DeriveWhere; use digest::core_api::{BlockSizeUser, CoreProxy}; use digest::Output; use generic_array::sequence::Concat; -use generic_array::{ - typenum::{IsLess, Le, NonZero, Sum, Unsigned, U2, U256}, - ArrayLength, GenericArray, -}; +use generic_array::typenum::{IsLess, Le, NonZero, Sum, Unsigned, U2, U256}; +use generic_array::{ArrayLength, GenericArray}; use hkdf::{Hkdf, HkdfExtract}; use rand::{CryptoRng, RngCore}; use subtle::ConstantTimeEq; use voprf::Group; +use crate::ciphersuite::CipherSuite; +use crate::envelope::{Envelope, EnvelopeLen}; +use crate::errors::utils::check_slice_size; +use crate::errors::{InternalError, ProtocolError}; +use crate::hash::{Hash, OutputSize, ProxyHash}; +use crate::key_exchange::group::KeGroup; +use crate::key_exchange::traits::{ + FromBytes, Ke1MessageLen, Ke1StateLen, Ke2StateLen, KeyExchange, ToBytes, +}; +use crate::key_exchange::tripledh::NonceLen; +use crate::keypair::{KeyPair, PrivateKey, PublicKey, SecretKey}; +use crate::messages::{CredentialRequestLen, RegistrationUploadLen}; +use crate::serialization::Serialize; +use crate::slow_hash::SlowHash; +use crate::{ + CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest, + RegistrationResponse, RegistrationUpload, +}; + /////////////// // Constants // // ========= // @@ -311,7 +312,8 @@ where .concat() } - /// Returns an initial "blinded" request to send to the server, as well as a ClientRegistration + /// Returns an initial "blinded" request to send to the server, as well as a + /// ClientRegistration pub fn start( blinding_factor_rng: &mut R, password: &[u8], @@ -330,7 +332,8 @@ where } /// "Unblinds" the server's answer and returns a final message containing - /// cryptographic identifiers, to be sent to the server on setup finalization + /// cryptographic identifiers, to be sent to the server on setup + /// finalization pub fn finish( self, rng: &mut R, @@ -416,8 +419,8 @@ where Ok(Self(RegistrationUpload::deserialize(input)?)) } - /// From the client's "blinded" password, returns a response to be - /// sent back to the client, as well as a ServerRegistration + /// From the client's "blinded" password, returns a response to be sent back + /// to the client, as well as a ServerRegistration pub fn start>( server_setup: &ServerSetup, message: RegistrationRequest, @@ -441,8 +444,8 @@ where }) } - /// From the client's cryptographic identifiers, fully populates and - /// returns a ServerRegistration + /// From the client's cryptographic identifiers, fully populates and returns + /// a ServerRegistration pub fn finish(message: RegistrationUpload) -> Self { Self(message) } @@ -530,7 +533,8 @@ where <::Core as BlockSizeUser>::BlockSize: IsLess, Le<<::Core as BlockSizeUser>::BlockSize, U256>: NonZero, { - /// Returns an initial "blinded" password request to send to the server, as well as a ClientLogin + /// Returns an initial "blinded" password request to send to the server, as + /// well as a ClientLogin pub fn start( rng: &mut R, password: &[u8], @@ -553,8 +557,8 @@ where }) } - /// "Unblinds" the server's answer and returns the opened assets from - /// the server + /// "Unblinds" the server's answer and returns the opened assets from the + /// server pub fn finish( self, password: &[u8], @@ -676,8 +680,8 @@ where }) } - /// From the client's "blinded" password, returns a challenge to be - /// sent back to the client, as well as a ServerLogin + /// From the client's "blinded" password, returns a challenge to be sent + /// back to the client, as well as a ServerLogin pub fn start>( rng: &mut R, server_setup: &ServerSetup, @@ -859,7 +863,8 @@ where { /// The registration request message to be sent to the server pub message: RegistrationRequest, - /// The client state that must be persisted in order to complete registration + /// The client state that must be persisted in order to complete + /// registration pub state: ClientRegistration, } @@ -878,7 +883,8 @@ where pub export_key: Output, /// The server's static public key pub server_s_pk: PublicKey, - /// Instance of the ClientRegistration, only used in tests for checking zeroize + /// Instance of the ClientRegistration, only used in tests for checking + /// zeroize #[cfg(test)] pub state: ClientRegistration, /// AuthKey, only used in tests @@ -889,8 +895,8 @@ where pub randomized_pwd: Output, } -/// Contains the fields that are returned by a server registration start. -/// Note that there is no state output in this step +/// Contains the fields that are returned by a server registration start. Note +/// that there is no state output in this step #[derive(DeriveWhere)] #[derive_where(Clone)] pub struct ServerRegistrationStartResult @@ -932,7 +938,8 @@ where { /// Specifying a context field that the server must agree on pub context: Option<&'c [u8]>, - /// Specifying a user identifier and server identifier that will be matched against the server + /// Specifying a user identifier and server identifier that will be matched + /// against the server pub identifiers: Identifiers<'i>, /// Specifying a configuration for the slow hash pub slow_hash: Option<&'h CS::SlowHash>, @@ -1000,7 +1007,8 @@ where /// The session key between client and server pub session_key: Output, _cs: PhantomData, - /// Instance of the ClientRegistration, only used in tests for checking zeroize + /// Instance of the ClientRegistration, only used in tests for checking + /// zeroize #[cfg(test)] pub state: ServerLogin, } @@ -1010,7 +1018,8 @@ where pub struct ServerLoginStartParameters<'c, 'i> { /// Specifying a context field that the client must agree on pub context: Option<&'c [u8]>, - /// Specifying a user identifier and server identifier that will be matched against the client + /// Specifying a user identifier and server identifier that will be matched + /// against the client pub identifiers: Identifiers<'i>, } @@ -1242,10 +1251,9 @@ pub(crate) fn bytestrings_from_identifiers( Ok((client_identity, server_identity)) } -/// Internal function for computing the blind result by calling the -/// voprf library. Note that for tests, we use the deterministic blinding -/// in order to be able to set the blinding factor directly from the passed-in -/// rng. +/// Internal function for computing the blind result by calling the voprf +/// library. Note that for tests, we use the deterministic blinding in order to +/// be able to set the blinding factor directly from the passed-in rng. fn blind( rng: &mut R, password: &[u8], diff --git a/src/serialization/mod.rs b/src/serialization/mod.rs index 1107d58..10f31fd 100644 --- a/src/serialization/mod.rs +++ b/src/serialization/mod.rs @@ -5,15 +5,15 @@ // License, Version 2.0 found in the LICENSE-APACHE file in the root directory // of this source tree. -use crate::errors::ProtocolError; use core::marker::PhantomData; + use digest::Update; -use generic_array::{ - typenum::{U0, U2}, - ArrayLength, GenericArray, -}; +use generic_array::typenum::{U0, U2}; +use generic_array::{ArrayLength, GenericArray}; use hmac::Mac; +use crate::errors::ProtocolError; + // Corresponds to the I2OSP() function from RFC8017 pub(crate) fn i2osp>( input: usize, @@ -43,7 +43,8 @@ pub(crate) fn os2ip(input: &[u8]) -> Result { Ok(usize::from_be_bytes(output_array)) } -/// Computes `I2OSP(len(input), max_bytes) || input` and helps hold output without allocation. +/// Computes `I2OSP(len(input), max_bytes) || input` and helps hold output +/// without allocation. pub(crate) struct Serialize< 'a, L1: ArrayLength, @@ -161,9 +162,10 @@ mod tests; #[cfg(test)] mod unit_tests { - use super::*; use generic_array::typenum::{U1, U2}; + use super::*; + // Test the error condition for I2OSP #[test] fn test_i2osp_err_check() { diff --git a/src/serialization/tests.rs b/src/serialization/tests.rs index 99fe58f..2a2e3e9 100755 --- a/src/serialization/tests.rs +++ b/src/serialization/tests.rs @@ -5,39 +5,35 @@ // License, Version 2.0 found in the LICENSE-APACHE file in the root directory // of this source tree. -use crate::{ - ciphersuite::CipherSuite, - envelope::{Envelope, EnvelopeLen, InnerEnvelopeMode}, - errors::*, - hash::{OutputSize, ProxyHash}, - key_exchange::{ - group::KeGroup, - traits::{Ke1MessageLen, Ke1StateLen, Ke2MessageLen}, - }, - key_exchange::{ - traits::{FromBytes, KeyExchange, ToBytes}, - tripledh::{NonceLen, TripleDH}, - }, - keypair::KeyPair, - messages::CredentialResponseWithoutKeLen, - opaque::{ClientLoginLen, ClientRegistrationLen, MaskedResponseLen}, - serialization::{i2osp, os2ip}, - *, -}; use core::ops::Add; use std::vec; use std::vec::Vec; use digest::core_api::{BlockSizeUser, CoreProxy}; use digest::Output; -use generic_array::{ - typenum::{IsLess, Le, NonZero, Sum, Unsigned, U256}, - ArrayLength, -}; -use proptest::{collection::vec, prelude::*}; -use rand::{rngs::OsRng, RngCore}; +use generic_array::typenum::{IsLess, Le, NonZero, Sum, Unsigned, U256}; +use generic_array::ArrayLength; +use proptest::collection::vec; +use proptest::prelude::*; +use rand::rngs::OsRng; +use rand::RngCore; use voprf::Group; +use crate::ciphersuite::CipherSuite; +use crate::envelope::{Envelope, EnvelopeLen, InnerEnvelopeMode}; +use crate::errors::*; +use crate::hash::{OutputSize, ProxyHash}; +use crate::key_exchange::group::KeGroup; +use crate::key_exchange::traits::{ + FromBytes, Ke1MessageLen, Ke1StateLen, Ke2MessageLen, KeyExchange, ToBytes, +}; +use crate::key_exchange::tripledh::{NonceLen, TripleDH}; +use crate::keypair::KeyPair; +use crate::messages::CredentialResponseWithoutKeLen; +use crate::opaque::{ClientLoginLen, ClientRegistrationLen, MaskedResponseLen}; +use crate::serialization::{i2osp, os2ip}; +use crate::*; + #[cfg(feature = "ristretto255")] struct Ristretto255; #[cfg(feature = "ristretto255")] @@ -128,16 +124,18 @@ fn server_registration_roundtrip() -> Result<(), ProtocolError> { // ServerRegistration = RegistrationUpload { // If we don't have envelope and client_pk, the server registration just - // contains the prf key let mut rng = OsRng; let mut masking_key = Output::::default(); rng.fill_bytes(&mut masking_key); // Construct a mock envelope let mut mock_envelope_bytes = Vec::new(); - mock_envelope_bytes.extend_from_slice(&[0; NonceLen::USIZE]); // empty nonce - // mock_envelope_bytes.extend_from_slice(&ciphertext); // ciphertext which is an encrypted private key - mock_envelope_bytes.extend_from_slice(&Output::::default()); // length-MAC_SIZE hmac + // empty nonce + mock_envelope_bytes.extend_from_slice(&[0; NonceLen::USIZE]); + // ciphertext which is an encrypted private key + //mock_envelope_bytes.extend_from_slice(&ciphertext); + // length-MAC_SIZE hmac + mock_envelope_bytes.extend_from_slice(&Output::::default()); let mock_client_kp = KeyPair::::generate_random(&mut rng); // serialization order: oprf_key, public key, envelope diff --git a/src/slow_hash.rs b/src/slow_hash.rs index fa9b083..522d923 100644 --- a/src/slow_hash.rs +++ b/src/slow_hash.rs @@ -7,14 +7,13 @@ //! Trait specifying a slow hashing function -use crate::{ - errors::InternalError, - hash::{Hash, ProxyHash}, -}; use digest::core_api::BlockSizeUser; use digest::Output; use generic_array::typenum::{IsLess, Le, NonZero, U256}; +use crate::errors::InternalError; +use crate::hash::{Hash, ProxyHash}; + /// Used for the slow hashing function in OPAQUE pub trait SlowHash: Default where diff --git a/src/tests/full_test.rs b/src/tests/full_test.rs index 26d03c6..5644f67 100755 --- a/src/tests/full_test.rs +++ b/src/tests/full_test.rs @@ -7,39 +7,37 @@ #![allow(unsafe_code)] -use crate::{ - ciphersuite::CipherSuite, - envelope::EnvelopeLen, - errors::*, - hash::{OutputSize, ProxyHash}, - key_exchange::{ - group::KeGroup, - traits::{Ke1MessageLen, Ke1StateLen, Ke2MessageLen}, - tripledh::{NonceLen, TripleDH}, - }, - messages::{ - CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen, - RegistrationResponseLen, RegistrationUploadLen, - }, - opaque::*, - slow_hash::NoOpHash, - tests::mock_rng::CycleRng, - *, -}; use core::ops::Add; +use std::string::{String, ToString}; +use std::vec::Vec; +use std::{format, println, vec}; + use digest::core_api::{BlockSizeUser, CoreProxy}; use digest::Output; use generic_array::typenum::{IsLess, Le, NonZero, Sum, Unsigned, U256}; use generic_array::ArrayLength; use rand::rngs::OsRng; use serde_json::Value; -use std::string::{String, ToString}; -use std::vec::Vec; -use std::{format, println, vec}; use subtle::ConstantTimeEq; use voprf::Group; use zeroize::Zeroize; +use crate::ciphersuite::CipherSuite; +use crate::envelope::EnvelopeLen; +use crate::errors::*; +use crate::hash::{OutputSize, ProxyHash}; +use crate::key_exchange::group::KeGroup; +use crate::key_exchange::traits::{Ke1MessageLen, Ke1StateLen, Ke2MessageLen}; +use crate::key_exchange::tripledh::{NonceLen, TripleDH}; +use crate::messages::{ + CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen, + RegistrationResponseLen, RegistrationUploadLen, +}; +use crate::opaque::*; +use crate::slow_hash::NoOpHash; +use crate::tests::mock_rng::CycleRng; +use crate::*; + // Tests // ===== @@ -491,9 +489,10 @@ where CredentialResponseWithoutKeLen: Add>, CredentialResponseLen: ArrayLength, { - use crate::keypair::KeyPair; use rand::RngCore; + use crate::keypair::KeyPair; + let mut rng = OsRng; // Inputs diff --git a/src/tests/mock_rng.rs b/src/tests/mock_rng.rs index 351a335..d675338 100644 --- a/src/tests/mock_rng.rs +++ b/src/tests/mock_rng.rs @@ -6,22 +6,21 @@ // of this source tree. use core::cmp::min; -use rand::{CryptoRng, Error, RngCore}; use std::vec::Vec; +use rand::{CryptoRng, Error, RngCore}; + /// A simple implementation of `RngCore` for testing purposes. /// /// This generates a cyclic sequence (i.e. cycles over an initial buffer) -/// -/// #[derive(Clone, Debug)] pub struct CycleRng { v: Vec, } impl CycleRng { - /// Create a `CycleRng`, yielding a sequence starting with - /// `initial` and looping thereafter + /// Create a `CycleRng`, yielding a sequence starting with `initial` and + /// looping thereafter pub fn new(initial: Vec) -> Self { CycleRng { v: initial } } diff --git a/src/tests/opaque_vectors.rs b/src/tests/opaque_vectors.rs index 4723b8d..78e6662 100644 --- a/src/tests/opaque_vectors.rs +++ b/src/tests/opaque_vectors.rs @@ -5,8 +5,7 @@ // License, Version 2.0 found in the LICENSE-APACHE file in the root directory // of this source tree. -//! The OPAQUE test vectors taken from: -//! https://github.com/cfrg/draft-irtf-cfrg-opaque/blob/master/draft-irtf-cfrg-opaque.md +//! The OPAQUE test vectors taken from: https://github.com/cfrg/draft-irtf-cfrg-opaque/blob/master/draft-irtf-cfrg-opaque.md pub(crate) static VECTORS: &str = r#" ## Real Test Vectors {#real-vectors} diff --git a/src/tests/test_opaque_vectors.rs b/src/tests/test_opaque_vectors.rs index ce13eeb..7c2e9a0 100755 --- a/src/tests/test_opaque_vectors.rs +++ b/src/tests/test_opaque_vectors.rs @@ -5,35 +5,33 @@ // License, Version 2.0 found in the LICENSE-APACHE file in the root directory // of this source tree. -use crate::{ - ciphersuite::CipherSuite, - envelope::EnvelopeLen, - errors::*, - hash::{OutputSize, ProxyHash}, - key_exchange::{ - group::KeGroup, - traits::{Ke1MessageLen, Ke2MessageLen}, - tripledh::{NonceLen, TripleDH}, - }, - messages::{ - CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen, - RegistrationResponseLen, RegistrationUploadLen, - }, - opaque::*, - slow_hash::NoOpHash, - tests::mock_rng::CycleRng, - *, -}; use core::ops::Add; +use std::string::ToString; +use std::vec::Vec; +use std::{println, vec}; + use digest::core_api::{BlockSizeUser, CoreProxy}; -use generic_array::{ - typenum::{IsLess, Le, NonZero, Sum, U256}, - ArrayLength, -}; +use generic_array::typenum::{IsLess, Le, NonZero, Sum, U256}; +use generic_array::ArrayLength; use json::JsonValue; -use std::{println, string::ToString, vec, vec::Vec}; use voprf::Group; +use crate::ciphersuite::CipherSuite; +use crate::envelope::EnvelopeLen; +use crate::errors::*; +use crate::hash::{OutputSize, ProxyHash}; +use crate::key_exchange::group::KeGroup; +use crate::key_exchange::traits::{Ke1MessageLen, Ke2MessageLen}; +use crate::key_exchange::tripledh::{NonceLen, TripleDH}; +use crate::messages::{ + CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen, + RegistrationResponseLen, RegistrationUploadLen, +}; +use crate::opaque::*; +use crate::slow_hash::NoOpHash; +use crate::tests::mock_rng::CycleRng; +use crate::*; + #[allow(non_snake_case)] #[derive(Debug)] pub struct OpaqueTestVectorParameters { @@ -95,7 +93,8 @@ macro_rules! parse_default { macro_rules! parse_default_random { ( $v:ident, $s:expr, $size:expr ) => { parse_default!($v, $s, { - use rand::{rngs::OsRng, RngCore}; + use rand::rngs::OsRng; + use rand::RngCore; let mut rng = OsRng; let mut v = vec![0u8; $size]; rng.fill_bytes(&mut v); diff --git a/taplo.toml b/taplo.toml new file mode 100644 index 0000000..7653be7 --- /dev/null +++ b/taplo.toml @@ -0,0 +1,3 @@ +[formatting] +reorder_keys = true +allowed_blank_lines = 1