From 29b2ebef1becabbb43c2966dd978b992612570d1 Mon Sep 17 00:00:00 2001 From: Kevin Lewi Date: Mon, 25 Oct 2021 02:54:32 -0700 Subject: [PATCH] Using voprf as a dependency (#248) * Using voprf as a dependency * Adding back x25519 and KeGroup * Addressing comments --- .github/workflows/main.yml | 9 +- Cargo.toml | 17 +- benches/opaque.rs | 345 +++++++++++++++ benches/oprf.rs | 68 --- examples/digital_locker.rs | 25 +- examples/simple_login.rs | 25 +- src/ciphersuite.rs | 8 +- src/envelope.rs | 82 ++-- src/errors.rs | 20 + src/group/expand.rs | 190 --------- src/group/mod.rs | 78 ---- src/group/p256.rs | 561 ------------------------- src/group/ristretto.rs | 122 ------ src/group/x25519.rs | 184 -------- src/impls.rs | 47 +-- src/key_exchange/group/mod.rs | 39 ++ src/key_exchange/group/p256.rs | 47 +++ src/key_exchange/group/ristretto255.rs | 63 +++ src/key_exchange/group/x25519.rs | 154 +++++++ src/key_exchange/mod.rs | 1 + src/key_exchange/traits.rs | 16 +- src/key_exchange/tripledh.rs | 114 +++-- src/keypair.rs | 152 ++++--- src/lib.rs | 42 +- src/messages.rs | 152 +++---- src/opaque.rs | 385 +++++++++-------- src/oprf.rs | 173 -------- src/serialization/tests.rs | 163 ++++--- src/tests/full_test.rs | 245 ++++++----- src/tests/mod.rs | 1 - src/tests/opaque_vectors.rs | 316 +++++++------- src/tests/parser.rs | 2 - src/tests/test_opaque_vectors.rs | 86 ++-- src/tests/voprf_test_vectors.rs | 158 ------- 34 files changed, 1638 insertions(+), 2452 deletions(-) create mode 100644 benches/opaque.rs delete mode 100644 benches/oprf.rs delete mode 100644 src/group/expand.rs delete mode 100644 src/group/mod.rs delete mode 100644 src/group/p256.rs delete mode 100644 src/group/ristretto.rs delete mode 100644 src/group/x25519.rs create mode 100644 src/key_exchange/group/mod.rs create mode 100644 src/key_exchange/group/p256.rs create mode 100644 src/key_exchange/group/ristretto255.rs create mode 100644 src/key_exchange/group/x25519.rs delete mode 100644 src/oprf.rs delete mode 100644 src/tests/voprf_test_vectors.rs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 334a9da..e0d8dab 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -163,6 +163,13 @@ jobs: benches: name: cargo bench compilation runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + backend_feature: + - u64_backend + - u32_backend + - p256,u64_backend steps: - name: Checkout sources uses: actions/checkout@v2 @@ -178,7 +185,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: bench - args: --features "bench" --no-run + args: --no-default-features --features bench --features ${{ matrix.backend_feature }} --no-run clippy: name: cargo clippy diff --git a/Cargo.toml b/Cargo.toml index ab3339f..2a7334e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,12 +14,12 @@ resolver = "2" [features] default = ["u64_backend", "serialize"] slow-hash = ["argon2"] -p256 = ["num-bigint", "num-integer", "num-traits", "once_cell", "p256_"] +p256 = ["p256_", "voprf/p256"] bench = [] -u64_backend = ["curve25519-dalek/u64_backend"] -u32_backend = ["curve25519-dalek/u32_backend"] -std = ["curve25519-dalek/std", "getrandom", "rand/std", "rand/std_rng", "num-bigint/std", "num-integer/std", "num-traits/std"] -serialize = ["serde", "base64", "generic-array/serde", "curve25519-dalek/serde"] +u64_backend = ["curve25519-dalek/u64_backend", "voprf/ristretto255_u64"] +u32_backend = ["curve25519-dalek/u32_backend", "voprf/ristretto255_u32"] +std = ["curve25519-dalek/std", "getrandom", "rand/std", "rand/std_rng", "voprf/std"] +serialize = ["serde", "base64", "generic-array/serde", "curve25519-dalek/serde", "voprf/serde"] [dependencies] argon2 = { version = "0.3", default-features = false, features = ["alloc"], optional = true } @@ -32,14 +32,11 @@ generic-array = "0.14" getrandom = { version = "0.2", optional = true } hkdf = "0.11" hmac = "0.11" -num-bigint = { version = "0.4", default-features = false, optional = true } -num-integer = { version = "0.1", default-features = false, optional = true } -num-traits = { version = "0.2", default-features = false, optional = true } -once_cell = { version = "1", default-features = false, optional = true } p256_ = { package = "p256", version = "0.9", default-features = false, features = ["arithmetic", "zeroize"], optional = true } rand = { version = "0.8", default-features = false } serde = { version = "1", default-features = false, features = ["alloc", "derive"], optional = true } subtle = { version = "2.3", default-features = false } +voprf = { version = "0.2", default-features = false, features = ["danger"] } zeroize = { version = "1", features = ["zeroize_derive"] } [target.'cfg(target_arch = "wasm32")'.dependencies] @@ -61,6 +58,6 @@ regex = "1" rustyline = "8" [[bench]] -name = "oprf" +name = "opaque" harness = false required-features = ["bench"] diff --git a/benches/opaque.rs b/benches/opaque.rs new file mode 100644 index 0000000..1b81af5 --- /dev/null +++ b/benches/opaque.rs @@ -0,0 +1,345 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +#[macro_use] +extern crate criterion; + +use criterion::Criterion; +use opaque_ke::*; +use rand::rngs::OsRng; + +#[cfg(all(not(feature = "p256"), feature = "u64_backend"))] +static SUFFIX: &str = "u64_backend"; +#[cfg(all(not(feature = "p256"), feature = "u32_backend"))] +static SUFFIX: &str = "u32_backend"; +#[cfg(feature = "p256")] +static SUFFIX: &str = "p256"; + +struct Default; + +#[cfg(not(feature = "p256"))] +impl CipherSuite for Default { + type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; + type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; + type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; + type Hash = sha2::Sha512; + type SlowHash = opaque_ke::slow_hash::NoOpHash; +} + +#[cfg(feature = "p256")] +impl CipherSuite for Default { + type OprfGroup = p256_::ProjectivePoint; + type KeGroup = p256_::ProjectivePoint; + type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; + type Hash = sha2::Sha256; + type SlowHash = opaque_ke::slow_hash::NoOpHash; +} + +fn server_setup(c: &mut Criterion) { + let mut rng = OsRng; + + c.bench_function(&format!("server setup ({})", SUFFIX), move |b| { + b.iter(|| { + ServerSetup::::new(&mut rng).unwrap(); + }) + }); +} + +fn client_registration_start(c: &mut Criterion) { + let mut rng = OsRng; + let password = b"password"; + + c.bench_function( + &format!("client registration start ({})", SUFFIX), + move |b| { + b.iter(|| { + ClientRegistration::::start(&mut rng, &password[..]).unwrap(); + }) + }, + ); +} + +fn server_registration_start(c: &mut Criterion) { + let mut rng = OsRng; + let username = b"username"; + let password = b"password"; + let server_setup = ServerSetup::::new(&mut rng).unwrap(); + let client_registration_start_result = + ClientRegistration::::start(&mut rng, &password[..]).unwrap(); + + c.bench_function( + &format!("server registration start ({})", SUFFIX), + move |b| { + b.iter(|| { + ServerRegistration::::start( + &server_setup, + client_registration_start_result.message.clone(), + &username[..], + ) + .unwrap(); + }) + }, + ); +} + +fn client_registration_finish(c: &mut Criterion) { + let mut rng = OsRng; + let username = b"username"; + let password = b"password"; + let server_setup = ServerSetup::::new(&mut rng).unwrap(); + let client_registration_start_result = + ClientRegistration::::start(&mut rng, &password[..]).unwrap(); + let server_registration_start_result = ServerRegistration::::start( + &server_setup, + client_registration_start_result.message.clone(), + &username[..], + ) + .unwrap(); + + c.bench_function( + &format!("client registration finish ({})", SUFFIX), + move |b| { + b.iter(|| { + client_registration_start_result + .clone() + .state + .finish( + &mut rng, + server_registration_start_result.message.clone(), + ClientRegistrationFinishParameters::default(), + ) + .unwrap(); + }) + }, + ); +} + +fn server_registration_finish(c: &mut Criterion) { + let mut rng = OsRng; + let username = b"username"; + let password = b"password"; + let server_setup = ServerSetup::::new(&mut rng).unwrap(); + let client_registration_start_result = + ClientRegistration::::start(&mut rng, &password[..]).unwrap(); + let server_registration_start_result = ServerRegistration::::start( + &server_setup, + client_registration_start_result.message.clone(), + &username[..], + ) + .unwrap(); + let client_registration_finish_result = client_registration_start_result + .clone() + .state + .finish( + &mut rng, + server_registration_start_result.message.clone(), + ClientRegistrationFinishParameters::default(), + ) + .unwrap(); + + c.bench_function( + &format!("server registration finish ({})", SUFFIX), + move |b| { + b.iter(|| { + ServerRegistration::finish(client_registration_finish_result.clone().message); + }) + }, + ); +} + +fn client_login_start(c: &mut Criterion) { + let mut rng = OsRng; + let password = b"password"; + + c.bench_function(&format!("client login start ({})", SUFFIX), move |b| { + b.iter(|| { + ClientLogin::::start(&mut rng, &password[..]).unwrap(); + }) + }); +} + +fn server_login_start_real(c: &mut Criterion) { + let mut rng = OsRng; + let username = b"username"; + let password = b"password"; + let server_setup = ServerSetup::::new(&mut rng).unwrap(); + let client_registration_start_result = + ClientRegistration::::start(&mut rng, &password[..]).unwrap(); + let server_registration_start_result = ServerRegistration::::start( + &server_setup, + client_registration_start_result.message.clone(), + &username[..], + ) + .unwrap(); + let client_registration_finish_result = client_registration_start_result + .clone() + .state + .finish( + &mut rng, + server_registration_start_result.message.clone(), + ClientRegistrationFinishParameters::default(), + ) + .unwrap(); + let password_file = ServerRegistration::finish(client_registration_finish_result.message); + let client_login_start_result = ClientLogin::::start(&mut rng, &password[..]).unwrap(); + + c.bench_function( + &format!("server login start (real) ({})", SUFFIX), + move |b| { + b.iter(|| { + ServerLogin::start( + &mut rng, + &server_setup, + Some(password_file.clone()), + client_login_start_result.clone().message, + &username[..], + ServerLoginStartParameters::default(), + ) + .unwrap(); + }) + }, + ); +} + +fn server_login_start_fake(c: &mut Criterion) { + let mut rng = OsRng; + let username = b"username"; + let password = b"password"; + let server_setup = ServerSetup::::new(&mut rng).unwrap(); + let client_login_start_result = ClientLogin::::start(&mut rng, &password[..]).unwrap(); + + c.bench_function( + &format!("server login start (fake) ({})", SUFFIX), + move |b| { + b.iter(|| { + ServerLogin::start( + &mut rng, + &server_setup, + None, + client_login_start_result.clone().message, + &username[..], + ServerLoginStartParameters::default(), + ) + .unwrap(); + }) + }, + ); +} + +fn client_login_finish(c: &mut Criterion) { + let mut rng = OsRng; + let username = b"username"; + let password = b"password"; + let server_setup = ServerSetup::::new(&mut rng).unwrap(); + let client_registration_start_result = + ClientRegistration::::start(&mut rng, &password[..]).unwrap(); + let server_registration_start_result = ServerRegistration::::start( + &server_setup, + client_registration_start_result.message.clone(), + &username[..], + ) + .unwrap(); + let client_registration_finish_result = client_registration_start_result + .clone() + .state + .finish( + &mut rng, + server_registration_start_result.message.clone(), + ClientRegistrationFinishParameters::default(), + ) + .unwrap(); + let password_file = ServerRegistration::finish(client_registration_finish_result.message); + let client_login_start_result = ClientLogin::::start(&mut rng, &password[..]).unwrap(); + let server_login_start = ServerLogin::start( + &mut rng, + &server_setup, + Some(password_file.clone()), + client_login_start_result.clone().message, + &username[..], + ServerLoginStartParameters::default(), + ) + .unwrap(); + + c.bench_function(&format!("client login finish ({})", SUFFIX), move |b| { + b.iter(|| { + client_login_start_result + .clone() + .state + .finish( + server_login_start.clone().message, + ClientLoginFinishParameters::default(), + ) + .unwrap(); + }) + }); +} + +fn server_login_finish(c: &mut Criterion) { + let mut rng = OsRng; + let username = b"username"; + let password = b"password"; + let server_setup = ServerSetup::::new(&mut rng).unwrap(); + let client_registration_start_result = + ClientRegistration::::start(&mut rng, &password[..]).unwrap(); + let server_registration_start_result = ServerRegistration::::start( + &server_setup, + client_registration_start_result.message.clone(), + &username[..], + ) + .unwrap(); + let client_registration_finish_result = client_registration_start_result + .clone() + .state + .finish( + &mut rng, + server_registration_start_result.message.clone(), + ClientRegistrationFinishParameters::default(), + ) + .unwrap(); + let password_file = ServerRegistration::finish(client_registration_finish_result.message); + let client_login_start_result = ClientLogin::::start(&mut rng, &password[..]).unwrap(); + let server_login_start_result = ServerLogin::start( + &mut rng, + &server_setup, + Some(password_file.clone()), + client_login_start_result.clone().message, + &username[..], + ServerLoginStartParameters::default(), + ) + .unwrap(); + let client_login_finish_result = client_login_start_result + .clone() + .state + .finish( + server_login_start_result.clone().message, + ClientLoginFinishParameters::default(), + ) + .unwrap(); + + c.bench_function(&format!("server login finish ({})", SUFFIX), move |b| { + b.iter(|| { + server_login_start_result + .clone() + .state + .finish(client_login_finish_result.clone().message) + .unwrap(); + }) + }); +} + +criterion_group!( + opaque_benches, + server_setup, + client_registration_start, + server_registration_start, + client_registration_finish, + server_registration_finish, + client_login_start, + server_login_start_real, + server_login_start_fake, + client_login_finish, + server_login_finish, +); +criterion_main!(opaque_benches); diff --git a/benches/oprf.rs b/benches/oprf.rs deleted file mode 100644 index 9e9fbc6..0000000 --- a/benches/oprf.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. -// -// This source code is licensed under the MIT license found in the -// LICENSE file in the root directory of this source tree. - -#[macro_use] -extern crate criterion; - -use criterion::Criterion; -use curve25519_dalek::ristretto::RistrettoPoint; -use generic_array::arr; -use opaque_ke::{ - group::Group, - oprf::{blind_shim, evaluate_shim, finalize_shim}, -}; -use rand::{prelude::ThreadRng, thread_rng}; -use sha2::Sha512; - -fn oprf1(c: &mut Criterion) { - let mut csprng: ThreadRng = thread_rng(); - let input = b"hunter2"; - - c.bench_function("blind with Ristretto", move |b| { - b.iter(|| { - blind_shim::<_, RistrettoPoint, Sha512>(&input[..], &mut csprng).unwrap(); - }) - }); -} - -fn oprf2(c: &mut Criterion) { - let mut csprng: ThreadRng = thread_rng(); - let input = b"hunter2"; - - let (_, alpha) = blind_shim::<_, RistrettoPoint, Sha512>(&input[..], &mut csprng).unwrap(); - let salt_bytes = arr![ - u8; 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, - ]; - let salt = RistrettoPoint::from_scalar_slice(&salt_bytes).unwrap(); - - c.bench_function("evaluate with Ristretto", move |b| { - b.iter(|| { - let _beta = evaluate_shim::(alpha, &salt); - }) - }); -} - -fn oprf3(c: &mut Criterion) { - let mut csprng: ThreadRng = thread_rng(); - let input = b"hunter2"; - - let (token, alpha) = blind_shim::<_, RistrettoPoint, Sha512>(&input[..], &mut csprng).unwrap(); - let salt_bytes = arr![ - u8; 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, - ]; - let salt = RistrettoPoint::from_scalar_slice(&salt_bytes).unwrap(); - let beta = evaluate_shim::(alpha, &salt); - - c.bench_function("finalize with Ristretto", move |b| { - b.iter(|| { - let _res = finalize_shim::(&token, beta).unwrap(); - }) - }); -} - -criterion_group!(oprf_benches, oprf1, oprf2, oprf3); -criterion_main!(oprf_benches); diff --git a/examples/digital_locker.rs b/examples/digital_locker.rs index 71ad641..85856dc 100644 --- a/examples/digital_locker.rs +++ b/examples/digital_locker.rs @@ -90,7 +90,10 @@ fn register_locker( let mut client_rng = OsRng; let client_registration_start_result = ClientRegistration::::start(&mut client_rng, password.as_bytes()).unwrap(); - let registration_request_bytes = client_registration_start_result.message.serialize(); + let registration_request_bytes = client_registration_start_result + .message + .serialize() + .unwrap(); // Client sends registration_request_bytes to server let server_registration_start_result = ServerRegistration::::start( @@ -99,7 +102,10 @@ fn register_locker( &locker_id.to_be_bytes(), ) .unwrap(); - let registration_response_bytes = server_registration_start_result.message.serialize(); + let registration_response_bytes = server_registration_start_result + .message + .serialize() + .unwrap(); // Server sends registration_response_bytes to client @@ -111,7 +117,10 @@ fn register_locker( ClientRegistrationFinishParameters::default(), ) .unwrap(); - let message_bytes = client_finish_registration_result.message.serialize(); + let message_bytes = client_finish_registration_result + .message + .serialize() + .unwrap(); // Client encrypts secret message using export key let ciphertext = encrypt( @@ -127,7 +136,7 @@ fn register_locker( Locker { contents: ciphertext, - password_file: password_file.serialize(), + password_file: password_file.serialize().unwrap(), } } @@ -141,7 +150,7 @@ fn open_locker( let mut client_rng = OsRng; let client_login_start_result = ClientLogin::::start(&mut client_rng, password.as_bytes()).unwrap(); - let credential_request_bytes = client_login_start_result.message.serialize(); + let credential_request_bytes = client_login_start_result.message.serialize().unwrap(); // Client sends credential_request_bytes to server @@ -157,7 +166,7 @@ fn open_locker( ServerLoginStartParameters::default(), ) .unwrap(); - let credential_response_bytes = server_login_start_result.message.serialize(); + let credential_response_bytes = server_login_start_result.message.serialize().unwrap(); // Server sends credential_response_bytes to client @@ -171,7 +180,7 @@ fn open_locker( return Err(String::from("Incorrect password, please try again.")); } let client_login_finish_result = result.unwrap(); - let credential_finalization_bytes = client_login_finish_result.message.serialize(); + let credential_finalization_bytes = client_login_finish_result.message.serialize().unwrap(); // Client sends credential_finalization_bytes to server @@ -197,7 +206,7 @@ fn open_locker( fn main() { let mut rng = OsRng; - let server_setup = ServerSetup::::new(&mut rng); + let server_setup = ServerSetup::::new(&mut rng).unwrap(); let mut rl = Editor::<()>::new(); let mut registered_lockers: Vec = vec![]; diff --git a/examples/simple_login.rs b/examples/simple_login.rs index c380ba1..365f29f 100644 --- a/examples/simple_login.rs +++ b/examples/simple_login.rs @@ -53,7 +53,10 @@ fn account_registration( let mut client_rng = OsRng; let client_registration_start_result = ClientRegistration::::start(&mut client_rng, password.as_bytes()).unwrap(); - let registration_request_bytes = client_registration_start_result.message.serialize(); + let registration_request_bytes = client_registration_start_result + .message + .serialize() + .unwrap(); // Client sends registration_request_bytes to server @@ -63,7 +66,10 @@ fn account_registration( username.as_bytes(), ) .unwrap(); - let registration_response_bytes = server_registration_start_result.message.serialize(); + let registration_response_bytes = server_registration_start_result + .message + .serialize() + .unwrap(); // Server sends registration_response_bytes to client @@ -75,14 +81,17 @@ fn account_registration( ClientRegistrationFinishParameters::default(), ) .unwrap(); - let message_bytes = client_finish_registration_result.message.serialize(); + let message_bytes = client_finish_registration_result + .message + .serialize() + .unwrap(); // Client sends message_bytes to server let password_file = ServerRegistration::finish( RegistrationUpload::::deserialize(&message_bytes[..]).unwrap(), ); - password_file.serialize() + password_file.serialize().unwrap() } // Password-based login between a client and server @@ -95,7 +104,7 @@ fn account_login( let mut client_rng = OsRng; let client_login_start_result = ClientLogin::::start(&mut client_rng, password.as_bytes()).unwrap(); - let credential_request_bytes = client_login_start_result.message.serialize(); + let credential_request_bytes = client_login_start_result.message.serialize().unwrap(); // Client sends credential_request_bytes to server @@ -110,7 +119,7 @@ fn account_login( ServerLoginStartParameters::default(), ) .unwrap(); - let credential_response_bytes = server_login_start_result.message.serialize(); + let credential_response_bytes = server_login_start_result.message.serialize().unwrap(); // Server sends credential_response_bytes to client @@ -124,7 +133,7 @@ fn account_login( return false; } let client_login_finish_result = result.unwrap(); - let credential_finalization_bytes = client_login_finish_result.message.serialize(); + let credential_finalization_bytes = client_login_finish_result.message.serialize().unwrap(); // Client sends credential_finalization_bytes to server @@ -138,7 +147,7 @@ fn account_login( fn main() { let mut rng = OsRng; - let server_setup = ServerSetup::::new(&mut rng); + let server_setup = ServerSetup::::new(&mut rng).unwrap(); let mut rl = Editor::<()>::new(); let mut registered_users = HashMap::>::new(); diff --git a/src/ciphersuite.rs b/src/ciphersuite.rs index c6be548..520d290 100644 --- a/src/ciphersuite.rs +++ b/src/ciphersuite.rs @@ -5,7 +5,9 @@ //! Defines the CipherSuite trait to specify the underlying primitives for OPAQUE -use crate::{group::Group, hash::Hash, key_exchange::traits::KeyExchange, slow_hash::SlowHash}; +use crate::key_exchange::group::KeGroup; +use crate::{hash::Hash, key_exchange::traits::KeyExchange, slow_hash::SlowHash}; +use voprf::group::Group as OprfGroup; /// Configures the underlying primitives used in OPAQUE /// * `OprfGroup`: a finite cyclic group along with a point representation, along @@ -19,9 +21,9 @@ pub trait CipherSuite { /// 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: Group; + type OprfGroup: OprfGroup; /// A `Group` used for the `KeyExchange`. - type KeGroup: Group; + type KeGroup: KeGroup; /// A key exchange protocol type KeyExchange: KeyExchange; /// The main hash function use (for HKDF computations and hashing transcripts) diff --git a/src/envelope.rs b/src/envelope.rs index ccf2d39..6812ee5 100644 --- a/src/envelope.rs +++ b/src/envelope.rs @@ -6,8 +6,8 @@ use crate::{ ciphersuite::CipherSuite, errors::{utils::check_slice_size, InternalError, ProtocolError}, - group::Group, hash::Hash, + key_exchange::group::KeGroup, keypair::{KeyPair, PublicKey}, opaque::{bytestrings_from_identifiers, Identifiers}, }; @@ -19,13 +19,14 @@ use generic_array::{typenum::Unsigned, GenericArray}; use hkdf::Hkdf; use hmac::{Hmac, Mac, NewMac}; use rand::{CryptoRng, RngCore}; +use voprf::group::Group; use zeroize::Zeroize; // Constant string used as salt for HKDF computation -const STR_AUTH_KEY: &[u8] = b"AuthKey"; -const STR_EXPORT_KEY: &[u8] = b"ExportKey"; -const STR_PRIVATE_KEY: &[u8] = b"PrivateKey"; -const STR_OPAQUE_DERIVE_AUTH_KEY_PAIR: &[u8] = b"OPAQUE-DeriveAuthKeyPair"; +const STR_AUTH_KEY: &[u8; 7] = b"AuthKey"; +const STR_EXPORT_KEY: &[u8; 9] = b"ExportKey"; +const STR_PRIVATE_KEY: &[u8; 10] = b"PrivateKey"; +const STR_OPAQUE_DERIVE_AUTH_KEY_PAIR: &[u8; 24] = b"OPAQUE-DeriveAuthKeyPair"; const NONCE_LEN: usize = 32; #[derive(Clone, Debug, Eq, Hash, PartialEq, Zeroize)] @@ -117,7 +118,7 @@ impl Envelope { #[allow(clippy::type_complexity)] pub(crate) fn seal( rng: &mut R, - key: &[u8], + randomized_pwd_hasher: Hkdf, server_s_pk: &[u8], optional_ids: Option, ) -> Result, ProtocolError> { @@ -126,14 +127,14 @@ impl Envelope { let (mode, client_s_pk) = ( InnerEnvelopeMode::Internal, - build_inner_envelope_internal::(key, &nonce)?, + build_inner_envelope_internal::(randomized_pwd_hasher.clone(), &nonce)?, ); let (id_u, id_s) = bytestrings_from_identifiers(&optional_ids, &client_s_pk.to_arr(), server_s_pk)?; let aad = construct_aad(&id_u, &id_s, server_s_pk); - let result = Self::seal_raw(key, &nonce, &aad, mode)?; + let result = Self::seal_raw(randomized_pwd_hasher, &nonce, &aad, mode)?; Ok(( result.0, client_s_pk, @@ -147,18 +148,19 @@ impl Envelope { /// Note that a new nonce is sampled for each call to seal. #[allow(clippy::type_complexity)] pub(crate) fn seal_raw( - key: &[u8], + randomized_pwd_hasher: Hkdf, nonce: &[u8], aad: &[u8], mode: InnerEnvelopeMode, ) -> Result, InternalError> { - let h = Hkdf::::new(None, key); let mut hmac_key = vec![0u8; Self::hmac_key_size()]; let mut export_key = vec![0u8; Self::export_key_size()]; - h.expand(&[nonce, STR_AUTH_KEY].concat(), &mut hmac_key) + randomized_pwd_hasher + .expand(&[nonce, STR_AUTH_KEY].concat(), &mut hmac_key) .map_err(|_| InternalError::HkdfError)?; - h.expand(&[nonce, STR_EXPORT_KEY].concat(), &mut export_key) + randomized_pwd_hasher + .expand(&[nonce, STR_EXPORT_KEY].concat(), &mut export_key) .map_err(|_| InternalError::HkdfError)?; let mut hmac = @@ -182,7 +184,7 @@ impl Envelope { pub(crate) fn open( &self, - key: &[u8], + randomized_pwd_hasher: Hkdf, server_s_pk: &[u8], optional_ids: &Option, ) -> Result, ProtocolError> { @@ -190,7 +192,9 @@ impl Envelope { InnerEnvelopeMode::Zero => { return Err(InternalError::IncompatibleEnvelopeModeError.into()) } - InnerEnvelopeMode::Internal => recover_keys_internal::(key, &self.nonce)?, + InnerEnvelopeMode::Internal => { + recover_keys_internal::(randomized_pwd_hasher.clone(), &self.nonce)? + } }; let (id_u, id_s) = bytestrings_from_identifiers( @@ -200,7 +204,7 @@ impl Envelope { )?; let aad = construct_aad(&id_u, &id_s, server_s_pk); - let opened = self.open_raw(key, &aad)?; + let opened = self.open_raw(randomized_pwd_hasher, &aad)?; Ok(OpenedEnvelope { client_static_keypair, @@ -214,16 +218,23 @@ impl Envelope { /// aad used to construct the envelope are the same. pub(crate) fn open_raw( &self, - key: &[u8], + randomized_pwd_hasher: Hkdf, aad: &[u8], ) -> Result, InternalError> { - let h = Hkdf::::new(None, key); let mut hmac_key = vec![0u8; Self::hmac_key_size()]; let mut export_key = vec![0u8; Self::export_key_size()]; - h.expand(&[&self.nonce, STR_AUTH_KEY].concat(), &mut hmac_key) + randomized_pwd_hasher + .expand( + &[self.nonce.clone(), STR_AUTH_KEY.to_vec()].concat(), + &mut hmac_key, + ) .map_err(|_| InternalError::HkdfError)?; - h.expand(&[&self.nonce, STR_EXPORT_KEY].concat(), &mut export_key) + randomized_pwd_hasher + .expand( + &[self.nonce.clone(), STR_EXPORT_KEY.to_vec()].concat(), + &mut export_key, + ) .map_err(|_| InternalError::HkdfError)?; let mut hmac = @@ -292,11 +303,6 @@ impl Envelope { hmac: GenericArray::clone_from_slice(hmac), }) } - - #[cfg(test)] - pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> { - vec![(self.hmac.as_ptr(), self.hmac.len())] - } } // This can't be derived because of the use of a phantom parameter @@ -317,17 +323,17 @@ impl Drop for Envelope { // Helper functions fn build_inner_envelope_internal( - random_pwd: &[u8], + randomized_pwd_hasher: Hkdf, nonce: &[u8], ) -> Result, ProtocolError> { - let h = Hkdf::::new(None, random_pwd); - let mut keypair_seed = vec![0u8; ::ScalarLen::USIZE]; - h.expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed) + let mut keypair_seed = vec![0u8; ::SkLen::USIZE]; + randomized_pwd_hasher + .expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed) .map_err(|_| InternalError::HkdfError)?; let client_static_keypair = KeyPair::::from_private_key_slice( - &CS::OprfGroup::scalar_as_bytes(CS::OprfGroup::hash_to_scalar::( - &keypair_seed[..], - STR_OPAQUE_DERIVE_AUTH_KEY_PAIR, + &CS::OprfGroup::scalar_as_bytes(CS::OprfGroup::hash_to_scalar::( + Some(&keypair_seed[..]), + GenericArray::from(*STR_OPAQUE_DERIVE_AUTH_KEY_PAIR), )?), )?; @@ -335,17 +341,17 @@ fn build_inner_envelope_internal( } fn recover_keys_internal( - random_pwd: &[u8], + randomized_pwd_hasher: Hkdf, nonce: &[u8], ) -> Result, ProtocolError> { - let h = Hkdf::::new(None, random_pwd); - let mut keypair_seed = vec![0u8; ::ScalarLen::USIZE]; - h.expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed) + let mut keypair_seed = vec![0u8; ::SkLen::USIZE]; + randomized_pwd_hasher + .expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed) .map_err(|_| InternalError::HkdfError)?; let client_static_keypair = KeyPair::::from_private_key_slice( - &CS::OprfGroup::scalar_as_bytes(CS::OprfGroup::hash_to_scalar::( - &keypair_seed[..], - STR_OPAQUE_DERIVE_AUTH_KEY_PAIR, + &CS::OprfGroup::scalar_as_bytes(CS::OprfGroup::hash_to_scalar::( + Some(&keypair_seed[..]), + GenericArray::from(*STR_OPAQUE_DERIVE_AUTH_KEY_PAIR), )?), )?; diff --git a/src/errors.rs b/src/errors.rs index e5a5ca6..d979294 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -45,6 +45,10 @@ pub enum InternalError { IncompatibleEnvelopeModeError, /// This error occurs when the inner envelope is malformed InvalidInnerEnvelopeError, + /// Error from the OPRF evaluation + OprfError(voprf::errors::InternalError), + /// Error encountered when attempting to produce a keypair + InvalidKeypairError, } impl Debug for InternalError { @@ -72,6 +76,8 @@ impl Debug for InternalError { f.debug_tuple("IncompatibleEnvelopeModeError").finish() } Self::InvalidInnerEnvelopeError => f.debug_tuple("InvalidInnerEnvelopeError").finish(), + Self::OprfError(error) => f.debug_tuple("OprfError").field(error).finish(), + Self::InvalidKeypairError => f.debug_tuple("InvalidKeypairError").finish(), } } } @@ -102,10 +108,24 @@ impl InternalError { Self::SealOpenHmacError => InternalError::SealOpenHmacError, Self::IncompatibleEnvelopeModeError => InternalError::IncompatibleEnvelopeModeError, Self::InvalidInnerEnvelopeError => InternalError::InvalidInnerEnvelopeError, + Self::OprfError(error) => InternalError::OprfError(error), + Self::InvalidKeypairError => InternalError::InvalidKeypairError, } } } +impl From for InternalError { + fn from(voprf_error: voprf::errors::InternalError) -> Self { + Self::OprfError(voprf_error) + } +} + +impl From for ProtocolError { + fn from(voprf_error: voprf::errors::InternalError) -> Self { + Self::LibraryError(InternalError::OprfError(voprf_error)) + } +} + /// Represents an error in protocol handling #[derive(Clone, Display, Eq, Hash, PartialEq)] pub enum ProtocolError { diff --git a/src/group/expand.rs b/src/group/expand.rs deleted file mode 100644 index 3095a18..0000000 --- a/src/group/expand.rs +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. -// -// This source code is licensed under the MIT license found in the -// LICENSE file in the root directory of this source tree. - -use crate::errors::{InternalError, ProtocolError}; -use crate::hash::Hash; -use crate::serialization::i2osp; -use alloc::vec::Vec; -use digest::{BlockInput, Digest}; -use generic_array::typenum::Unsigned; - -// Computes ceil(x / y) -fn div_ceil(x: usize, y: usize) -> usize { - let additive = (x % y != 0) as usize; - x / y + additive -} - -fn xor(x: &[u8], y: &[u8]) -> Result, InternalError> { - if x.len() != y.len() { - return Err(InternalError::HashToCurveError); - } - - Ok(x.iter().zip(y).map(|(&x1, &x2)| x1 ^ x2).collect()) -} - -/// Corresponds to the expand_message_xmd() function defined in -/// -pub fn expand_message_xmd( - msg: &[u8], - dst: &[u8], - len_in_bytes: usize, -) -> Result, ProtocolError> { - let b_in_bytes = ::OutputSize::USIZE; - let r_in_bytes = ::BlockSize::USIZE; - - let ell = div_ceil(len_in_bytes, b_in_bytes); - if ell > 255 { - return Err(InternalError::HashToCurveError.into()); - } - let dst_prime = [dst, &i2osp(dst.len(), 1)?].concat(); - let z_pad = i2osp(0, r_in_bytes)?; - let l_i_b_str = i2osp(len_in_bytes, 2)?; - let msg_prime = [&z_pad, msg, &l_i_b_str, &i2osp(0, 1)?, &dst_prime].concat(); - - let mut b: Vec> = alloc::vec![H::digest(&msg_prime).to_vec()]; // b[0] - - let mut h = H::new(); - h.update(&b[0]); - h.update(&i2osp(1, 1)?); - h.update(&dst_prime); - b.push(h.finalize_reset().to_vec()); // b[1] - - let mut uniform_bytes: Vec = Vec::new(); - uniform_bytes.extend_from_slice(&b[1]); - - for i in 2..(ell + 1) { - h.update(xor(&b[0], &b[i - 1])?); - h.update(&i2osp(i, 1)?); - h.update(&dst_prime); - b.push(h.finalize_reset().to_vec()); // b[i] - uniform_bytes.extend_from_slice(&b[i]); - } - - Ok(uniform_bytes[..len_in_bytes].to_vec()) -} - -#[cfg(test)] -mod tests { - - struct Params { - msg: &'static str, - len_in_bytes: usize, - uniform_bytes: &'static str, - } - - #[test] - fn test_expand_message_xmd() { - // Test vectors taken from Section K.1 of https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt - let test_vectors: alloc::vec::Vec = alloc::vec![ - Params { - msg: "", - len_in_bytes: 0x20, - uniform_bytes: "f659819a6473c1835b25ea59e3d38914c98b374f0970b7e4c\ - 92181df928fca88", - }, - Params { - msg: "abc", - len_in_bytes: 0x20, - uniform_bytes: "1c38f7c211ef233367b2420d04798fa4698080a8901021a79\ - 5a1151775fe4da7", - }, - Params { - msg: "abcdef0123456789", - len_in_bytes: 0x20, - uniform_bytes: "8f7e7b66791f0da0dbb5ec7c22ec637f79758c0a48170bfb7c4611bd304ece89", - }, - Params { - msg: "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ - qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ - qqqqqqqqqqqqqqqqqqqqqqqqq", - len_in_bytes: 0x20, - uniform_bytes: "72d5aa5ec810370d1f0013c0df2f1d65699494ee2a39f72e\ - 1716b1b964e1c642", - }, - Params { - msg: "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - len_in_bytes: 0x20, - uniform_bytes: "3b8e704fc48336aca4c2a12195b720882f2162a4b7b13a9c\ - 350db46f429b771b", - }, - Params { - msg: "", - len_in_bytes: 0x80, - uniform_bytes: "8bcffd1a3cae24cf9cd7ab85628fd111bb17e3739d3b53f8\ - 9580d217aa79526f1708354a76a402d3569d6a9d19ef3de4d0b991\ - e4f54b9f20dcde9b95a66824cbdf6c1a963a1913d43fd7ac443a02\ - fc5d9d8d77e2071b86ab114a9f34150954a7531da568a1ea8c7608\ - 61c0cde2005afc2c114042ee7b5848f5303f0611cf297f", - }, - Params { - msg: "abc", - len_in_bytes: 0x80, - uniform_bytes: "fe994ec51bdaa821598047b3121c149b364b178606d5e72b\ - fbb713933acc29c186f316baecf7ea22212f2496ef3f785a27e84a\ - 40d8b299cec56032763eceeff4c61bd1fe65ed81decafff4a31d01\ - 98619c0aa0c6c51fca15520789925e813dcfd318b542f879944127\ - 1f4db9ee3b8092a7a2e8d5b75b73e28fb1ab6b4573c192", - }, - Params { - msg: "abcdef0123456789", - len_in_bytes: 0x80, - uniform_bytes: "c9ec7941811b1e19ce98e21db28d22259354d4d0643e3011\ - 75e2f474e030d32694e9dd5520dde93f3600d8edad94e5c3649030\ - 88a7228cc9eff685d7eaac50d5a5a8229d083b51de4ccc3733917f\ - 4b9535a819b445814890b7029b5de805bf62b33a4dc7e24acdf2c9\ - 24e9fe50d55a6b832c8c84c7f82474b34e48c6d43867be", - }, - Params { - msg: "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ - qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ - qqqqqqqqqqqqqqqqqqqqqqqqq", - len_in_bytes: 0x80, - uniform_bytes: "48e256ddba722053ba462b2b93351fc966026e6d6db49318\ - 9798181c5f3feea377b5a6f1d8368d7453faef715f9aecb078cd40\ - 2cbd548c0e179c4ed1e4c7e5b048e0a39d31817b5b24f50db58bb3\ - 720fe96ba53db947842120a068816ac05c159bb5266c63658b4f00\ - 0cbf87b1209a225def8ef1dca917bcda79a1e42acd8069", - }, - Params { - msg: "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - len_in_bytes: 0x80, - uniform_bytes: "396962db47f749ec3b5042ce2452b619607f27fd3939ece2\ - 746a7614fb83a1d097f554df3927b084e55de92c7871430d6b95c2\ - a13896d8a33bc48587b1f66d21b128a1a8240d5b0c26dfe795a1a8\ - 42a0807bb148b77c2ef82ed4b6c9f7fcb732e7f94466c8b51e52bf\ - 378fba044a31f5cb44583a892f5969dcd73b3fa128816e", - }, - ]; - let dst = "QUUX-V01-CS02-with-expander"; - - for tv in test_vectors { - let uniform_bytes = super::expand_message_xmd::( - tv.msg.as_bytes(), - dst.as_bytes(), - tv.len_in_bytes, - ) - .unwrap(); - assert_eq!(tv.uniform_bytes, hex::encode(uniform_bytes)); - } - } -} diff --git a/src/group/mod.rs b/src/group/mod.rs deleted file mode 100644 index 32fd8f0..0000000 --- a/src/group/mod.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. -// -// This source code is licensed under the MIT license found in the -// LICENSE file in the root directory of this source tree. - -//! Defines the Group trait to specify the underlying prime order group used in -//! OPAQUE's OPRF - -mod expand; -#[cfg(feature = "p256")] -pub(crate) mod p256; -mod ristretto; -mod x25519; - -use crate::errors::{InternalError, ProtocolError}; -use crate::hash::Hash; -use core::ops::Mul; -use generic_array::{ArrayLength, GenericArray}; -use rand::{CryptoRng, RngCore}; -use zeroize::Zeroize; - -/// A prime-order subgroup of a base field (EC, prime-order field ...). This -/// subgroup is noted additively — as in the draft RFC — in this trait. -pub trait Group: Copy + Sized + for<'a> Mul<&'a ::Scalar, Output = Self> { - /// The ciphersuite identifier as dictated by - /// - const SUITE_ID: usize; - - /// transforms a password and domain separation tag (DST) into a curve point - fn map_to_curve(msg: &[u8], dst: &[u8]) -> Result; - - /// Hashes a slice of pseudo-random bytes to a scalar - fn hash_to_scalar(input: &[u8], dst: &[u8]) -> Result; - - /// Generates the contextString parameter as defined in - /// - fn get_context_string(mode: u8) -> Result, ProtocolError> { - use crate::serialization::i2osp; - - Ok([i2osp(mode as usize, 1)?, i2osp(Self::SUITE_ID, 2)?].concat()) - } - - /// The type of base field scalars - type Scalar: Zeroize + Copy; - /// The byte length necessary to represent scalars - type ScalarLen: ArrayLength + 'static; - /// Return a scalar from its fixed-length bytes representation - fn from_scalar_slice( - scalar_bits: &GenericArray, - ) -> Result; - /// picks a scalar at random - fn random_nonzero_scalar(rng: &mut R) -> Self::Scalar; - /// Serializes a scalar to bytes - fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray; - /// The multiplicative inverse of this scalar - fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar; - - /// The byte length necessary to represent group elements - type ElemLen: ArrayLength + 'static; - /// Return an element from its fixed-length bytes representation - fn from_element_slice( - element_bits: &GenericArray, - ) -> Result; - /// Serializes the `self` group element - fn to_arr(&self) -> GenericArray; - - /// Get the base point for the group - fn base_point() -> Self; - - /// Multiply the point by a scalar, represented as a slice - fn mult_by_slice(&self, scalar: &GenericArray) -> Self; - - /// Returns if the group element is equal to the identity (1) - fn is_identity(&self) -> bool; - - /// Compares in constant time if the group elements are equal - fn ct_equal(&self, other: &Self) -> bool; -} diff --git a/src/group/p256.rs b/src/group/p256.rs deleted file mode 100644 index 8d03b30..0000000 --- a/src/group/p256.rs +++ /dev/null @@ -1,561 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. -// -// This source code is licensed under the MIT license found in the -// LICENSE file in the root directory of this source tree. - -// Note: This group implementation of p256 is experimental for now, -// until hash-to-curve or crypto-bigint are fully supported. - -#![allow( - clippy::borrow_interior_mutable_const, - clippy::declare_interior_mutable_const -)] - -use super::Group; -use crate::errors::{InternalError, ProtocolError}; -use crate::hash::Hash; -use core::ops::{Add, Div, Mul, Neg}; -use core::str::FromStr; -use generic_array::typenum::{U32, U33}; -use generic_array::{ArrayLength, GenericArray}; -use num_bigint::{BigInt, Sign}; -use num_integer::Integer; -use num_traits::{One, ToPrimitive, Zero}; -use once_cell::unsync::Lazy; -use p256_::elliptic_curve::group::prime::PrimeCurveAffine; -use p256_::elliptic_curve::group::GroupEncoding; -use p256_::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint}; -use p256_::elliptic_curve::subtle::ConstantTimeEq; -use p256_::elliptic_curve::Field; -use p256_::{AffinePoint, EncodedPoint, ProjectivePoint}; -use rand::{CryptoRng, RngCore}; -use subtle::{Choice, ConditionallySelectable}; - -// `L: 48` -pub const L: usize = 48; - -impl Group for ProjectivePoint { - const SUITE_ID: usize = 0x0003; - - // Implements the `hash_to_curve()` function from - // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3 - fn map_to_curve(msg: &[u8], dst: &[u8]) -> Result { - // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.2 - // `p: 2^256 - 2^224 + 2^192 + 2^96 - 1` - const P: Lazy = Lazy::new(|| { - BigInt::from_str( - "115792089210356248762697446949407573530086143415290314195533631308867097853951", - ) - .unwrap() - }); - // `A: -3` - const A: Lazy = Lazy::new(|| BigInt::from(-3)); - // `B: 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b` - const B: Lazy = Lazy::new(|| { - BigInt::parse_bytes( - b"5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", - 16, - ) - .unwrap() - }); - // `Z: -10` - const Z: Lazy = Lazy::new(|| BigInt::from(-10)); - - // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3 - // `hash_to_curve` calls `hash_to_field` with a `count` of `2` - // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3 - // `hash_to_field` calls `expand_message` with a `len_in_bytes` of `count * L` - let uniform_bytes = super::expand::expand_message_xmd::(msg, dst, 2 * L)?; - - // map to curve - let (q0x, q0y) = map_to_curve_simple_swu(&uniform_bytes[..L], &A, &B, &P, &Z); - let (q1x, q1y) = map_to_curve_simple_swu(&uniform_bytes[L..], &A, &B, &P, &Z); - - // convert to `p256` types - let p0 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates( - &q0x, &q0y, false, - )) - .ok_or(InternalError::PointError)? - .to_curve(); - let p1 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates( - &q1x, &q1y, false, - )) - .ok_or(InternalError::PointError)?; - - Ok(p0 + p1) - } - - // Implements the `HashToScalar()` function from - // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.3 - fn hash_to_scalar(input: &[u8], dst: &[u8]) -> Result { - // https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf#[{%22num%22:211,%22gen%22:0},{%22name%22:%22XYZ%22},70,700,0] - // P-256 `n` is defined as `115792089210356248762697446949407573529996955224135760342 422259061068512044369` - const N: Lazy = Lazy::new(|| { - BigInt::from_str( - "115792089210356248762697446949407573529996955224135760342422259061068512044369", - ) - .unwrap() - }); - - // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3 - // `HashToScalar` is `hash_to_field` - let uniform_bytes = super::expand::expand_message_xmd::(input, dst, L)?; - let bytes = BigInt::from_bytes_be(Sign::Plus, &uniform_bytes) - .mod_floor(&N) - .to_bytes_be() - .1; - let mut result = GenericArray::default(); - result[..bytes.len()].copy_from_slice(&bytes); - - Ok(p256_::Scalar::from_bytes_reduced(&result)) - } - - type ElemLen = U33; - type Scalar = p256_::Scalar; - type ScalarLen = U32; - - fn from_scalar_slice( - scalar_bits: &GenericArray, - ) -> Result { - Ok(Self::Scalar::from_bytes_reduced(scalar_bits)) - } - - fn random_nonzero_scalar(rng: &mut R) -> Self::Scalar { - Self::Scalar::random(rng) - } - - fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray { - scalar.into() - } - - fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar { - scalar.invert().unwrap_or(Self::Scalar::zero()) - } - - fn from_element_slice( - element_bits: &GenericArray, - ) -> Result { - Option::from(Self::from_bytes(element_bits)).ok_or(InternalError::PointError) - } - - fn to_arr(&self) -> GenericArray { - let bytes = self.to_affine().to_encoded_point(true); - let bytes = bytes.as_bytes(); - let mut result = GenericArray::default(); - result[..bytes.len()].copy_from_slice(bytes); - result - } - - fn base_point() -> Self { - Self::generator() - } - - fn mult_by_slice(&self, scalar: &GenericArray) -> Self { - self * &Self::Scalar::from_bytes_reduced(scalar) - } - - fn is_identity(&self) -> bool { - self == &Self::identity() - } - fn ct_equal(&self, other: &Self) -> bool { - self.ct_eq(other).into() - } -} - -/// Corresponds to the map_to_curve_simple_swu() function defined in -/// -// `cmov`, `mod_floor` and `modpow` needs to be made constant-time, which -// will be supported after crypto-bigint is no longer experimental. See -// https://github.com/novifinancial/opaque-ke/issues/239 for more context. - -#[allow(clippy::many_single_char_names)] -fn map_to_curve_simple_swu>( - u: &[u8], - a: &BigInt, - b: &BigInt, - p: &BigInt, - z: &BigInt, -) -> (GenericArray, GenericArray) { - #[derive(Clone)] - struct Field<'a>(&'a BigInt); - - impl<'a> Field<'a> { - fn new(p: &'a BigInt) -> Self { - Self(p) - } - - fn element(&'a self, number: &BigInt) -> FieldElement<'a> { - FieldElement { - number: number.mod_floor(self.0), - f: self, - } - } - - fn one(&'a self) -> FieldElement<'a> { - self.element(&BigInt::one()) - } - } - - /// Finite field arithmetic - #[derive(Clone)] - struct FieldElement<'a> { - number: BigInt, - f: &'a Field<'a>, - } - - impl<'a> Add for FieldElement<'a> { - type Output = FieldElement<'a>; - - fn add(self, rhs: Self) -> Self::Output { - &self + &rhs - } - } - - impl<'a> Add for &FieldElement<'a> { - type Output = FieldElement<'a>; - - fn add(self, rhs: Self) -> Self::Output { - self.f.element(&(&self.number + &rhs.number)) - } - } - - impl<'a> Neg for FieldElement<'a> { - type Output = FieldElement<'a>; - - fn neg(self) -> Self::Output { - -&self - } - } - - impl<'a> Neg for &FieldElement<'a> { - type Output = FieldElement<'a>; - - fn neg(self) -> Self::Output { - self.f.element(&-&self.number) - } - } - - impl<'a> Mul for FieldElement<'a> { - type Output = FieldElement<'a>; - - fn mul(self, rhs: Self) -> Self::Output { - &self * &rhs - } - } - - impl<'a> Mul<&Self> for FieldElement<'a> { - type Output = FieldElement<'a>; - - fn mul(self, rhs: &Self) -> Self::Output { - &self * rhs - } - } - - impl<'a> Mul> for &FieldElement<'a> { - type Output = FieldElement<'a>; - - fn mul(self, rhs: FieldElement<'a>) -> Self::Output { - self * &rhs - } - } - - impl<'a> Mul for &FieldElement<'a> { - type Output = FieldElement<'a>; - - fn mul(self, rhs: Self) -> Self::Output { - self.f.element(&(&self.number * &rhs.number)) - } - } - - impl<'a> Div<&Self> for FieldElement<'a> { - type Output = FieldElement<'a>; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn div(self, rhs: &Self) -> Self::Output { - self * rhs.inv0() - } - } - - impl<'a> FieldElement<'a> { - fn square(&self) -> Self { - self * self - } - - fn pow_internal(&self, exponent: &BigInt) -> Self { - let exponent = exponent.mod_floor(&(self.f.0 - 1)); - Self { - number: self.number.modpow(&exponent, self.f.0), - f: self.f, - } - } - - /// Corresponds to the sqrt_3mod4() function defined in - /// - fn sqrt(&self) -> Self { - // constant - let c1 = (self.f.0 + 1) >> 2; - - self.pow_internal(&c1) - } - - /// Corresponds to the sgn0_m_eq_1() function defined in - /// - fn sgn0(&self) -> i32 { - (&self.number % 2_usize).to_i32().unwrap() - } - - /// See - fn inv0(&self) -> Self { - self.pow_internal(&(self.f.0 - 2)) - } - - fn is_zero(&self) -> bool { - self.number.is_zero() - } - - /// Corresponds to the is_square() function defined in - /// - fn is_square(&self) -> bool { - // constant - let exponent = (self.f.0 - 1) >> 1; - - let result = self.pow_internal(&exponent); - result.is_zero() || result.number.is_one() - } - - fn to_bytes>(&self) -> GenericArray { - let bytes = self.number.to_bytes_be().1; - let mut result = GenericArray::default(); - result[N::USIZE - bytes.len()..].copy_from_slice(&bytes); - result - } - } - - fn cmov<'a>(x: &FieldElement<'a>, y: &FieldElement<'a>, b: bool) -> FieldElement<'a> { - let f = x.f; - - let x_bytes = x.number.to_bytes_le().1; - let mut x = [0; 32]; - x[..x_bytes.len()].copy_from_slice(&x_bytes); - - let y_bytes = y.number.to_bytes_le().1; - let mut y = [0; 32]; - y[..y_bytes.len()].copy_from_slice(&y_bytes); - - let mut bytes = [0; 32]; - - let choice = Choice::from(u8::from(b)); - - for ((byte, x), y) in bytes.iter_mut().zip(&x).zip(&y) { - *byte = u8::conditional_select(x, y, choice); - } - - FieldElement { - f, - number: BigInt::from_bytes_le(Sign::Plus, &bytes), - } - } - - let f = Field::new(p); - let a = f.element(a); - let b = f.element(b); - let z = f.element(z); - let u = f.element(&BigInt::from_bytes_be(Sign::Plus, u)); - - // Constants: - // 1. c1 = -B / A - let c1 = -&b / &a; - // 2. c2 = -1 / Z - let c2 = -f.one() / &z; - - // Steps: - // 1. tv1 = Z * u^2 - let tv1 = z * u.square(); - // 2. tv2 = tv1^2 - let mut tv2 = tv1.square(); - // 3. x1 = tv1 + tv2 - let mut x1 = &tv1 + &tv2; - // 4. x1 = inv0(x1) - x1 = x1.inv0(); - // 5. e1 = x1 == 0 - let e1 = x1.is_zero(); - // 6. x1 = x1 + 1 - x1 = x1 + f.one(); - // 7. x1 = CMOV(x1, c2, e1) # If (tv1 + tv2) == 0, set x1 = -1 / Z - x1 = cmov(&x1, &c2, e1); - // 8. x1 = x1 * c1 # x1 = (-B / A) * (1 + (1 / (Z^2 * u^4 + Z * u^2))) - x1 = x1 * c1; - // 9. gx1 = x1^2 - let mut gx1 = x1.square(); - // 10. gx1 = gx1 + A - gx1 = gx1 + a; - // 11. gx1 = gx1 * x1 - gx1 = gx1 * &x1; - // 12. gx1 = gx1 + B # gx1 = g(x1) = x1^3 + A * x1 + B - gx1 = gx1 + b; - // 13. x2 = tv1 * x1 # x2 = Z * u^2 * x1 - let x2 = &tv1 * &x1; - // 14. tv2 = tv1 * tv2 - tv2 = tv1 * tv2; - // 15. gx2 = gx1 * tv2 # gx2 = (Z * u^2)^3 * gx1 - let gx2 = &gx1 * tv2; - // 16. e2 = is_square(gx1) - let e2 = gx1.is_square(); - // 17. x = CMOV(x2, x1, e2) # If is_square(gx1), x = x1, else x = x2 - let x = cmov(&x2, &x1, e2); - // 18. y2 = CMOV(gx2, gx1, e2) # If is_square(gx1), y2 = gx1, else y2 = gx2 - let y2 = cmov(&gx2, &gx1, e2); - // 19. y = sqrt(y2) - let mut y = y2.sqrt(); - // 20. e3 = sgn0(u) == sgn0(y) # Fix sign of y - let e3 = u.sgn0() == y.sgn0(); - // 21. y = CMOV(-y, y, e3) - y = cmov(&-&y, &y, e3); - // 22. return (x, y) - (x.to_bytes(), y.to_bytes()) -} - -#[cfg(test)] -mod tests { - use super::*; - - struct Params { - msg: &'static str, - px: &'static str, - py: &'static str, - u0: &'static str, - u1: &'static str, - q0x: &'static str, - q0y: &'static str, - q1x: &'static str, - q1y: &'static str, - } - - #[test] - fn map_to_curve_simple_swu() { - const P: Lazy = Lazy::new(|| { - BigInt::from_str( - "115792089210356248762697446949407573530086143415290314195533631308867097853951", - ) - .unwrap() - }); - const A: Lazy = Lazy::new(|| BigInt::from(-3)); - const B: Lazy = Lazy::new(|| { - BigInt::parse_bytes( - b"5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", - 16, - ) - .unwrap() - }); - const Z: Lazy = Lazy::new(|| BigInt::from(-10)); - - // Test vectors taken from https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-J.1.1 - let test_vectors = alloc::vec![ - Params { - msg: "", - px: "2c15230b26dbc6fc9a37051158c95b79656e17a1a920b11394ca91c44247d3e4", - py: "8a7a74985cc5c776cdfe4b1f19884970453912e9d31528c060be9ab5c43e8415", - u0: "ad5342c66a6dd0ff080df1da0ea1c04b96e0330dd89406465eeba11582515009", - u1: "8c0f1d43204bd6f6ea70ae8013070a1518b43873bcd850aafa0a9e220e2eea5a", - q0x: "ab640a12220d3ff283510ff3f4b1953d09fad35795140b1c5d64f313967934d5", - q0y: "dccb558863804a881d4fff3455716c836cef230e5209594ddd33d85c565b19b1", - q1x: "51cce63c50d972a6e51c61334f0f4875c9ac1cd2d3238412f84e31da7d980ef5", - q1y: "b45d1a36d00ad90e5ec7840a60a4de411917fbe7c82c3949a6e699e5a1b66aac", - }, - Params { - msg: "abc", - px: "0bb8b87485551aa43ed54f009230450b492fead5f1cc91658775dac4a3388a0f", - py: "5c41b3d0731a27a7b14bc0bf0ccded2d8751f83493404c84a88e71ffd424212e", - u0: "afe47f2ea2b10465cc26ac403194dfb68b7f5ee865cda61e9f3e07a537220af1", - u1: "379a27833b0bfe6f7bdca08e1e83c760bf9a338ab335542704edcd69ce9e46e0", - q0x: "5219ad0ddef3cc49b714145e91b2f7de6ce0a7a7dc7406c7726c7e373c58cb48", - q0y: "7950144e52d30acbec7b624c203b1996c99617d0b61c2442354301b191d93ecf", - q1x: "019b7cb4efcfeaf39f738fe638e31d375ad6837f58a852d032ff60c69ee3875f", - q1y: "589a62d2b22357fed5449bc38065b760095ebe6aeac84b01156ee4252715446e", - }, - Params { - msg: "abcdef0123456789", - px: "65038ac8f2b1def042a5df0b33b1f4eca6bff7cb0f9c6c1526811864e544ed80", - py: "cad44d40a656e7aff4002a8de287abc8ae0482b5ae825822bb870d6df9b56ca3", - u0: "0fad9d125a9477d55cf9357105b0eb3a5c4259809bf87180aa01d651f53d312c", - u1: "b68597377392cd3419d8fcc7d7660948c8403b19ea78bbca4b133c9d2196c0fb", - q0x: "a17bdf2965eb88074bc01157e644ed409dac97cfcf0c61c998ed0fa45e79e4a2", - q0y: "4f1bc80c70d411a3cc1d67aeae6e726f0f311639fee560c7f5a664554e3c9c2e", - q1x: "7da48bb67225c1a17d452c983798113f47e438e4202219dd0715f8419b274d66", - q1y: "b765696b2913e36db3016c47edb99e24b1da30e761a8a3215dc0ec4d8f96e6f9", - }, - Params { - msg: "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ - qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ - qqqqqqqqqqqqqqqqqqqqqqqqq", - px: "4be61ee205094282ba8a2042bcb48d88dfbb609301c49aa8b078533dc65a0b5d", - py: "98f8df449a072c4721d241a3b1236d3caccba603f916ca680f4539d2bfb3c29e", - u0: "3bbc30446f39a7befad080f4d5f32ed116b9534626993d2cc5033f6f8d805919", - u1: "76bb02db019ca9d3c1e02f0c17f8baf617bbdae5c393a81d9ce11e3be1bf1d33", - q0x: "c76aaa823aeadeb3f356909cb08f97eee46ecb157c1f56699b5efebddf0e6398", - q0y: "776a6f45f528a0e8d289a4be12c4fab80762386ec644abf2bffb9b627e4352b1", - q1x: "418ac3d85a5ccc4ea8dec14f750a3a9ec8b85176c95a7022f391826794eb5a75", - q1y: "fd6604f69e9d9d2b74b072d14ea13050db72c932815523305cb9e807cc900aff", - }, - Params { - msg: "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - px: "457ae2981f70ca85d8e24c308b14db22f3e3862c5ea0f652ca38b5e49cd64bc5", - py: "ecb9f0eadc9aeed232dabc53235368c1394c78de05dd96893eefa62b0f4757dc", - u0: "4ebc95a6e839b1ae3c63b847798e85cb3c12d3817ec6ebc10af6ee51adb29fec", - u1: "4e21af88e22ea80156aff790750121035b3eefaa96b425a8716e0d20b4e269ee", - q0x: "d88b989ee9d1295df413d4456c5c850b8b2fb0f5402cc5c4c7e815412e926db8", - q0y: "bb4a1edeff506cf16def96afff41b16fc74f6dbd55c2210e5b8f011ba32f4f40", - q1x: "a281e34e628f3a4d2a53fa87ff973537d68ad4fbc28d3be5e8d9f6a2571c5a4b", - q1y: "f6ed88a7aab56a488100e6f1174fa9810b47db13e86be999644922961206e184", - }, - ]; - let dst = "QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_"; - - for tv in test_vectors { - let uniform_bytes = super::super::expand::expand_message_xmd::( - tv.msg.as_bytes(), - dst.as_bytes(), - 96, - ) - .unwrap(); - - let u0 = BigInt::from_bytes_be(Sign::Plus, &uniform_bytes[..48]).mod_floor(&P); - let u1 = BigInt::from_bytes_be(Sign::Plus, &uniform_bytes[48..]).mod_floor(&P); - - assert_eq!(BigInt::parse_bytes(tv.u0.as_bytes(), 16).unwrap(), u0); - assert_eq!(BigInt::parse_bytes(tv.u1.as_bytes(), 16).unwrap(), u1); - - let (q0x, q0y) = super::map_to_curve_simple_swu(&u0.to_bytes_be().1, &A, &B, &P, &Z); - let (q1x, q1y) = super::map_to_curve_simple_swu(&u1.to_bytes_be().1, &A, &B, &P, &Z); - - assert_eq!(tv.q0x, hex::encode(q0x)); - assert_eq!(tv.q0y, hex::encode(q0y)); - assert_eq!(tv.q1x, hex::encode(q1x)); - assert_eq!(tv.q1y, hex::encode(q1y)); - - let p0 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates( - &q0x, &q0y, false, - )) - .unwrap() - .to_curve(); - let p1 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates( - &q1x, &q1y, false, - )) - .unwrap(); - - let p = (p0 + p1).to_encoded_point(false); - - assert_eq!(tv.px, hex::encode(p.x().unwrap())); - assert_eq!(tv.py, hex::encode(p.y().unwrap())); - } - } -} diff --git a/src/group/ristretto.rs b/src/group/ristretto.rs deleted file mode 100644 index 73267da..0000000 --- a/src/group/ristretto.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. -// -// This source code is licensed under the MIT license found in the -// LICENSE file in the root directory of this source tree. - -use super::Group; -use crate::errors::{InternalError, ProtocolError}; -use crate::hash::Hash; -use core::convert::TryInto; -use curve25519_dalek::{ - constants::RISTRETTO_BASEPOINT_POINT, - ristretto::{CompressedRistretto, RistrettoPoint}, - scalar::Scalar, - traits::Identity, -}; -use generic_array::{typenum::U32, GenericArray}; -use rand::{CryptoRng, RngCore}; -use subtle::ConstantTimeEq; - -/// The implementation of such a subgroup for Ristretto -impl Group for RistrettoPoint { - const SUITE_ID: usize = 0x0001; - - // Implements the `hash_to_ristretto255()` function from - // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt - fn map_to_curve(msg: &[u8], dst: &[u8]) -> Result { - let uniform_bytes = super::expand::expand_message_xmd::(msg, dst, 64)?; - - Ok(RistrettoPoint::from_uniform_bytes( - uniform_bytes - .as_slice() - .try_into() - .map_err(|_| InternalError::HashToCurveError)?, - )) - } - - // Implements the `HashToScalar()` function from - // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.1 - fn hash_to_scalar(input: &[u8], dst: &[u8]) -> Result { - let uniform_bytes = super::expand::expand_message_xmd::(input, dst, 64)?; - - Ok(Scalar::from_bytes_mod_order_wide( - uniform_bytes - .as_slice() - .try_into() - .map_err(|_| InternalError::HashToCurveError)?, - )) - } - - type Scalar = Scalar; - type ScalarLen = U32; - fn from_scalar_slice( - scalar_bits: &GenericArray, - ) -> Result { - Ok(Scalar::from_bytes_mod_order(*scalar_bits.as_ref())) - } - - fn random_nonzero_scalar(rng: &mut R) -> Self::Scalar { - loop { - let scalar = { - #[cfg(not(test))] - { - let mut scalar_bytes = [0u8; 64]; - rng.fill_bytes(&mut scalar_bytes); - Scalar::from_bytes_mod_order_wide(&scalar_bytes) - } - - // Tests need an exact conversion from bytes to scalar, sampling only 32 bytes from rng - #[cfg(test)] - { - let mut scalar_bytes = [0u8; 32]; - rng.fill_bytes(&mut scalar_bytes); - Scalar::from_bytes_mod_order(scalar_bytes) - } - }; - - if scalar != Scalar::zero() { - break scalar; - } - } - } - - fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray { - scalar.to_bytes().into() - } - - fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar { - scalar.invert() - } - - // The byte length necessary to represent group elements - type ElemLen = U32; - fn from_element_slice( - element_bits: &GenericArray, - ) -> Result { - CompressedRistretto::from_slice(element_bits) - .decompress() - .ok_or(InternalError::PointError) - } - - // serialization of a group element - fn to_arr(&self) -> GenericArray { - self.compress().to_bytes().into() - } - - fn base_point() -> Self { - RISTRETTO_BASEPOINT_POINT - } - - fn mult_by_slice(&self, scalar: &GenericArray) -> Self { - self * Scalar::from_bits(*scalar.as_ref()) - } - - /// Returns if the group element is equal to the identity (1) - fn is_identity(&self) -> bool { - self == &Self::identity() - } - - fn ct_equal(&self, other: &Self) -> bool { - ConstantTimeEq::ct_eq(self, other).into() - } -} diff --git a/src/group/x25519.rs b/src/group/x25519.rs deleted file mode 100644 index fddfe46..0000000 --- a/src/group/x25519.rs +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. -// -// This source code is licensed under the MIT license found in the -// LICENSE file in the root directory of this source tree. - -use super::Group; -use crate::errors::{InternalError, ProtocolError}; -use crate::hash::Hash; -use curve25519_dalek::{constants::X25519_BASEPOINT, montgomery::MontgomeryPoint, scalar::Scalar}; -use generic_array::{typenum::U32, GenericArray}; -use rand::{CryptoRng, RngCore}; - -/// The implementation of such a subgroup for Ristretto -impl Group for MontgomeryPoint { - const SUITE_ID: usize = 0xFFFF; - - fn map_to_curve(_msg: &[u8], _dst: &[u8]) -> Result { - unreachable!("this algorithm should only be used as the `KeGroup`") - } - - fn hash_to_scalar(_input: &[u8], _dst: &[u8]) -> Result { - unreachable!("this algorithm should only be used as the `KeGroup`") - } - - type Scalar = Scalar; - type ScalarLen = U32; - fn from_scalar_slice( - scalar_bits: &GenericArray, - ) -> Result { - Ok(Scalar::from_bytes_mod_order(*scalar_bits.as_ref())) - } - - fn random_nonzero_scalar(rng: &mut R) -> Self::Scalar { - loop { - let scalar = { - #[cfg(not(test))] - { - let mut scalar_bytes = [0u8; 64]; - rng.fill_bytes(&mut scalar_bytes); - Scalar::from_bytes_mod_order_wide(&scalar_bytes) - } - - // Tests need an exact conversion from bytes to scalar, sampling only 32 bytes from rng - #[cfg(test)] - { - let mut scalar_bytes = [0u8; 32]; - rng.fill_bytes(&mut scalar_bytes); - Scalar::from_bytes_mod_order(scalar_bytes) - } - }; - - if scalar != Scalar::zero() { - break scalar; - } - } - } - - fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray { - scalar.to_bytes().into() - } - - fn scalar_invert(_scalar: &Self::Scalar) -> Self::Scalar { - unreachable!("this algorithm should only be used as the `KeGroup`") - } - - // The byte length necessary to represent group elements - type ElemLen = U32; - fn from_element_slice( - element_bits: &GenericArray, - ) -> Result { - Ok(Self(*element_bits.as_ref())) - } - - // serialization of a group element - fn to_arr(&self) -> GenericArray { - self.to_bytes().into() - } - - fn base_point() -> Self { - X25519_BASEPOINT - } - - fn mult_by_slice(&self, scalar: &GenericArray) -> Self { - self * Scalar::from_bits(*scalar.as_ref()) - } - - /// Returns if the group element is equal to the identity (1) - fn is_identity(&self) -> bool { - unreachable!("this algorithm should only be used as the `KeGroup`") - } - - fn ct_equal(&self, _other: &Self) -> bool { - unreachable!("this algorithm should only be used as the `KeGroup`") - } -} - -#[test] -fn test() -> Result<(), ProtocolError> { - use crate::{ - key_exchange::tripledh::TripleDH, slow_hash::NoOpHash, CipherSuite, ClientLogin, - ClientLoginFinishParameters, ClientLoginFinishResult, ClientLoginStartResult, - ClientRegistration, ClientRegistrationFinishParameters, ClientRegistrationFinishResult, - ClientRegistrationStartResult, ServerLogin, ServerLoginStartParameters, - ServerLoginStartResult, ServerRegistration, ServerSetup, - }; - use curve25519_dalek::ristretto::RistrettoPoint; - use rand::rngs::OsRng; - - struct X25519Sha512NoSlowHash; - impl CipherSuite for X25519Sha512NoSlowHash { - type OprfGroup = RistrettoPoint; - type KeGroup = MontgomeryPoint; - type KeyExchange = TripleDH; - type Hash = sha2::Sha512; - type SlowHash = NoOpHash; - } - - const PASSWORD: &[u8] = b"1234"; - - let server_setup = ServerSetup::::new(&mut OsRng); - - let ClientRegistrationStartResult { - message, - state: client, - } = ClientRegistration::start(&mut OsRng, PASSWORD)?; - let message = ServerRegistration::start(&server_setup, message, &[])?.message; - let ClientRegistrationFinishResult { - message, - export_key: register_export_key, - .. - } = client.finish( - &mut OsRng, - message, - ClientRegistrationFinishParameters::default(), - )?; - let server_registration = ServerRegistration::finish(message); - - let ClientLoginStartResult { - message, - state: client, - } = ClientLogin::start(&mut OsRng, PASSWORD)?; - let ServerLoginStartResult { - message, - state: server, - .. - } = ServerLogin::start( - &mut OsRng, - &server_setup, - Some(server_registration), - message, - &[], - ServerLoginStartParameters::default(), - )?; - let ClientLoginFinishResult { - message, - session_key: client_session_key, - export_key: login_export_key, - .. - } = client.finish(message, ClientLoginFinishParameters::default())?; - let server_session_key = server.finish(message)?.session_key; - - assert_eq!(register_export_key, login_export_key); - assert_eq!(client_session_key, server_session_key); - - let ClientLoginStartResult { - message, - state: client, - } = ClientLogin::start(&mut OsRng, PASSWORD)?; - let ServerLoginStartResult { message, .. } = ServerLogin::start( - &mut OsRng, - &server_setup, - None, - message, - &[], - ServerLoginStartParameters::default(), - )?; - - assert!(matches!( - client.finish(message, ClientLoginFinishParameters::default()), - Err(ProtocolError::InvalidLoginError) - )); - - Ok(()) -} diff --git a/src/impls.rs b/src/impls.rs index 48fbde6..585bd76 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -110,10 +110,13 @@ macro_rules! impl_serialize_and_deserialize_for { where S: serde::Serializer, { + use serde::ser::Error; + if serializer.is_human_readable() { - serializer.serialize_str(&base64::encode(&self.serialize())) + serializer + .serialize_str(&base64::encode(&self.serialize().map_err(Error::custom)?)) } else { - serializer.serialize_bytes(&self.serialize()) + serializer.serialize_bytes(&self.serialize().map_err(Error::custom)?) } } } @@ -124,45 +127,15 @@ macro_rules! impl_serialize_and_deserialize_for { where D: serde::Deserializer<'de>, { + use serde::de::Error; + if deserializer.is_human_readable() { let s = <&str>::deserialize(deserializer)?; - $t::::deserialize(&base64::decode(s).map_err(serde::de::Error::custom)?) - .map_err(serde::de::Error::custom) + Self::deserialize(&base64::decode(s).map_err(Error::custom)?) } else { - struct ByteVisitor { - marker: core::marker::PhantomData, - } - impl<'de, CS: CipherSuite> serde::de::Visitor<'de> for ByteVisitor { - type Value = $t; - fn expecting( - &self, - formatter: &mut core::fmt::Formatter, - ) -> core::fmt::Result { - formatter.write_str(core::concat!( - "the byte representation of a ", - core::stringify!($t) - )) - } - - fn visit_bytes(self, value: &[u8]) -> Result - where - E: serde::de::Error, - { - $t::::deserialize(value).map_err(|_| { - serde::de::Error::invalid_value( - serde::de::Unexpected::Bytes(value), - &core::concat!( - "invalid byte sequence for ", - core::stringify!($t) - ), - ) - }) - } - } - deserializer.deserialize_bytes(ByteVisitor:: { - marker: core::marker::PhantomData, - }) + Self::deserialize(<&[u8]>::deserialize(deserializer)?) } + .map_err(Error::custom) } } }; diff --git a/src/key_exchange/group/mod.rs b/src/key_exchange/group/mod.rs new file mode 100644 index 0000000..383597e --- /dev/null +++ b/src/key_exchange/group/mod.rs @@ -0,0 +1,39 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +//! Includes the KeGroup trait and definitions for the +//! key exchange groups + +use crate::errors::InternalError; +use generic_array::{ArrayLength, GenericArray}; +use rand::{CryptoRng, RngCore}; + +/// A group representation for use in the key exchange +pub trait KeGroup: Sized + Clone { + /// Length of the public key + type PkLen: ArrayLength + 'static; + /// Length of the secret key + type SkLen: ArrayLength + 'static; + + /// Return a public key from its fixed-length bytes representation + fn from_pk_slice(element_bits: &GenericArray) -> Result; + + /// Generate a random secret key + fn random_sk(rng: &mut R) -> GenericArray; + + /// Return a public key from its secret key + fn public_key(sk: &GenericArray) -> Self; + + /// Serializes `self` + fn to_arr(&self) -> GenericArray; + + /// Diffie-Hellman key exchange + fn diffie_hellman(&self, sk: &GenericArray) -> GenericArray; +} + +#[cfg(feature = "p256")] +pub mod p256; +pub mod ristretto255; +pub mod x25519; diff --git a/src/key_exchange/group/p256.rs b/src/key_exchange/group/p256.rs new file mode 100644 index 0000000..57c260b --- /dev/null +++ b/src/key_exchange/group/p256.rs @@ -0,0 +1,47 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +//! Key Exchange group implementation for p256 + +use super::KeGroup; +use crate::errors::InternalError; +use generic_array::typenum::{U32, U33}; +use generic_array::GenericArray; +use rand::{CryptoRng, RngCore}; + +impl KeGroup for p256_::ProjectivePoint { + type PkLen = U33; + type SkLen = U32; + + fn from_pk_slice(element_bits: &GenericArray) -> Result { + use p256_::elliptic_curve::group::GroupEncoding; + + Option::from(Self::from_bytes(element_bits)).ok_or(InternalError::PointError) + } + + fn random_sk(rng: &mut R) -> GenericArray { + use p256_::elliptic_curve::Field; + + p256_::Scalar::random(rng).into() + } + + fn public_key(sk: &GenericArray) -> Self { + Self::generator() * p256_::Scalar::from_bytes_reduced(sk) + } + + fn to_arr(&self) -> GenericArray { + use p256_::elliptic_curve::sec1::ToEncodedPoint; + + let bytes = self.to_affine().to_encoded_point(true); + let bytes = bytes.as_bytes(); + let mut result = GenericArray::default(); + result[..bytes.len()].copy_from_slice(bytes); + result + } + + fn diffie_hellman(&self, sk: &GenericArray) -> GenericArray { + (self * &p256_::Scalar::from_bytes_reduced(sk)).to_arr() + } +} diff --git a/src/key_exchange/group/ristretto255.rs b/src/key_exchange/group/ristretto255.rs new file mode 100644 index 0000000..5ee55d4 --- /dev/null +++ b/src/key_exchange/group/ristretto255.rs @@ -0,0 +1,63 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +//! 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; +use generic_array::typenum::U32; +use generic_array::GenericArray; +use rand::{CryptoRng, RngCore}; + +impl KeGroup for RistrettoPoint { + type PkLen = U32; + type SkLen = U32; + + fn from_pk_slice(element_bits: &GenericArray) -> Result { + CompressedRistretto::from_slice(element_bits) + .decompress() + .ok_or(InternalError::PointError) + } + + fn random_sk(rng: &mut R) -> GenericArray { + loop { + let scalar = { + #[cfg(not(test))] + { + let mut scalar_bytes = [0u8; 64]; + rng.fill_bytes(&mut scalar_bytes); + Scalar::from_bytes_mod_order_wide(&scalar_bytes) + } + + // Tests need an exact conversion from bytes to scalar, sampling only 32 bytes from rng + #[cfg(test)] + { + let mut scalar_bytes = [0u8; 32]; + rng.fill_bytes(&mut scalar_bytes); + Scalar::from_bytes_mod_order(scalar_bytes) + } + }; + + if scalar != Scalar::zero() { + break scalar.to_bytes().into(); + } + } + } + + fn public_key(sk: &GenericArray) -> Self { + RISTRETTO_BASEPOINT_POINT * Scalar::from_bits(*sk.as_ref()) + } + + fn to_arr(&self) -> GenericArray { + self.compress().to_bytes().into() + } + + fn diffie_hellman(&self, sk: &GenericArray) -> GenericArray { + (self * Scalar::from_bits(*sk.as_ref())).to_arr() + } +} diff --git a/src/key_exchange/group/x25519.rs b/src/key_exchange/group/x25519.rs new file mode 100644 index 0000000..b97851c --- /dev/null +++ b/src/key_exchange/group/x25519.rs @@ -0,0 +1,154 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +//! Key Exchange group implementation for x25519 + +use super::KeGroup; +use crate::errors::InternalError; +use curve25519_dalek::{constants::X25519_BASEPOINT, montgomery::MontgomeryPoint, scalar::Scalar}; +use generic_array::{typenum::U32, GenericArray}; +use rand::{CryptoRng, RngCore}; + +/// The implementation of such a subgroup for Ristretto +impl KeGroup for MontgomeryPoint { + type PkLen = U32; + type SkLen = U32; + + fn from_pk_slice(element_bits: &GenericArray) -> Result { + Ok(Self(*element_bits.as_ref())) + } + + fn random_sk(rng: &mut R) -> GenericArray { + loop { + let scalar = { + #[cfg(not(test))] + { + let mut scalar_bytes = [0u8; 64]; + rng.fill_bytes(&mut scalar_bytes); + Scalar::from_bytes_mod_order_wide(&scalar_bytes) + } + + // Tests need an exact conversion from bytes to scalar, sampling only 32 bytes from rng + #[cfg(test)] + { + let mut scalar_bytes = [0u8; 32]; + rng.fill_bytes(&mut scalar_bytes); + Scalar::from_bytes_mod_order(scalar_bytes) + } + }; + + if scalar != Scalar::zero() { + break GenericArray::clone_from_slice(&scalar.to_bytes()); + } + } + } + + fn public_key(sk: &GenericArray) -> Self { + X25519_BASEPOINT * Scalar::from_bits(*sk.as_ref()) + } + + fn to_arr(&self) -> GenericArray { + self.to_bytes().into() + } + + fn diffie_hellman(&self, sk: &GenericArray) -> GenericArray { + (self * Scalar::from_bits(*sk.as_ref())).to_arr() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::errors::ProtocolError; + + #[test] + fn test_x25519() -> Result<(), ProtocolError> { + use crate::{ + key_exchange::tripledh::TripleDH, slow_hash::NoOpHash, CipherSuite, ClientLogin, + ClientLoginFinishParameters, ClientLoginFinishResult, ClientLoginStartResult, + ClientRegistration, ClientRegistrationFinishParameters, ClientRegistrationFinishResult, + ClientRegistrationStartResult, ServerLogin, ServerLoginStartParameters, + ServerLoginStartResult, ServerRegistration, ServerSetup, + }; + use curve25519_dalek::ristretto::RistrettoPoint; + use rand::rngs::OsRng; + + struct X25519Sha512NoSlowHash; + impl CipherSuite for X25519Sha512NoSlowHash { + type OprfGroup = RistrettoPoint; + type KeGroup = MontgomeryPoint; + type KeyExchange = TripleDH; + type Hash = sha2::Sha512; + type SlowHash = NoOpHash; + } + + const PASSWORD: &[u8] = b"1234"; + + let server_setup = ServerSetup::::new(&mut OsRng)?; + + let ClientRegistrationStartResult { + message, + state: client, + } = ClientRegistration::start(&mut OsRng, PASSWORD)?; + let message = ServerRegistration::start(&server_setup, message, &[])?.message; + let ClientRegistrationFinishResult { + message, + export_key: register_export_key, + .. + } = client.finish( + &mut OsRng, + message, + ClientRegistrationFinishParameters::default(), + )?; + let server_registration = ServerRegistration::finish(message); + + let ClientLoginStartResult { + message, + state: client, + } = ClientLogin::start(&mut OsRng, PASSWORD)?; + let ServerLoginStartResult { + message, + state: server, + .. + } = ServerLogin::start( + &mut OsRng, + &server_setup, + Some(server_registration), + message, + &[], + ServerLoginStartParameters::default(), + )?; + let ClientLoginFinishResult { + message, + session_key: client_session_key, + export_key: login_export_key, + .. + } = client.finish(message, ClientLoginFinishParameters::default())?; + let server_session_key = server.finish(message)?.session_key; + + assert_eq!(register_export_key, login_export_key); + assert_eq!(client_session_key, server_session_key); + + let ClientLoginStartResult { + message, + state: client, + } = ClientLogin::start(&mut OsRng, PASSWORD)?; + let ServerLoginStartResult { message, .. } = ServerLogin::start( + &mut OsRng, + &server_setup, + None, + message, + &[], + ServerLoginStartParameters::default(), + )?; + + assert!(matches!( + client.finish(message, ClientLoginFinishParameters::default()), + Err(ProtocolError::InvalidLoginError) + )); + + Ok(()) + } +} diff --git a/src/key_exchange/mod.rs b/src/key_exchange/mod.rs index 3f1daa7..1dae6a8 100644 --- a/src/key_exchange/mod.rs +++ b/src/key_exchange/mod.rs @@ -6,5 +6,6 @@ //! Includes instantiations of key exchange protocols used in the //! login step for OPAQUE +pub mod group; pub(crate) mod traits; pub mod tripledh; diff --git a/src/key_exchange/traits.rs b/src/key_exchange/traits.rs index 70a67ef..b6a3c9d 100644 --- a/src/key_exchange/traits.rs +++ b/src/key_exchange/traits.rs @@ -3,10 +3,10 @@ // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree. +use crate::key_exchange::group::KeGroup; use crate::{ ciphersuite::CipherSuite, errors::ProtocolError, - group::Group, hash::Hash, keypair::{PrivateKey, PublicKey, SecretKey}, }; @@ -36,9 +36,9 @@ pub type GenerateKe3Result = ( generic_array::GenericArray::OutputSize>, ); -pub trait KeyExchange { - type KE1State: FromBytes + ToBytesWithPointers + Zeroize + Clone; - type KE2State: FromBytes + ToBytesWithPointers + Zeroize + Clone; +pub trait KeyExchange { + type KE1State: FromBytes + ToBytes + Zeroize + Clone; + type KE2State: FromBytes + ToBytes + Zeroize + Clone; type KE1Message: FromBytes + ToBytes + Clone; type KE2Message: FromBytes + ToBytes + Clone; type KE3Message: FromBytes + ToBytes + Clone; @@ -89,11 +89,3 @@ pub trait FromBytes: Sized { pub trait ToBytes { fn to_bytes(&self) -> Vec; } - -pub trait ToBytesWithPointers { - fn to_bytes(&self) -> Vec; - - // Only used for tests to grab raw pointers to data - #[cfg(test)] - fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)>; -} diff --git a/src/key_exchange/tripledh.rs b/src/key_exchange/tripledh.rs index f85c889..85afbf4 100644 --- a/src/key_exchange/tripledh.rs +++ b/src/key_exchange/tripledh.rs @@ -10,10 +10,10 @@ use crate::{ utils::{check_slice_size, check_slice_size_atleast}, InternalError, ProtocolError, }, - group::Group, hash::Hash, - key_exchange::traits::{ - FromBytes, GenerateKe2Result, GenerateKe3Result, KeyExchange, ToBytes, ToBytesWithPointers, + key_exchange::{ + group::KeGroup, + traits::{FromBytes, GenerateKe2Result, GenerateKe3Result, KeyExchange, ToBytes}, }, keypair::{KeyPair, PrivateKey, PublicKey, SecretKey}, serialization::serialize, @@ -55,26 +55,26 @@ pub struct TripleDH; /// The client state produced after the first key exchange message #[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] -pub struct Ke1State { - client_e_sk: PrivateKey, +pub struct Ke1State { + client_e_sk: PrivateKey, client_nonce: GenericArray, } impl_clone_for!( - struct Ke1State, + struct Ke1State, [client_e_sk, client_nonce], ); impl_debug_eq_hash_for!( - struct Ke1State, + struct Ke1State, [client_e_sk, client_nonce], ); /// The first key exchange message #[derive(PartialEq, Eq, Debug, Hash, Clone)] #[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] -pub struct Ke1Message { +pub struct Ke1Message { pub(crate) client_nonce: GenericArray, - pub(crate) client_e_pk: PublicKey, + pub(crate) client_e_pk: PublicKey, } /// The server state produced after the second key exchange message @@ -91,9 +91,9 @@ pub struct Ke2State> { #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serialize", serde(bound = ""))] -pub struct Ke2Message> { +pub struct Ke2Message> { server_nonce: GenericArray, - server_e_pk: PublicKey, + server_e_pk: PublicKey, mac: GenericArray, } @@ -110,17 +110,17 @@ pub struct Ke3Message> { // ========================== // //////////////////////////////// -impl KeyExchange for TripleDH { - type KE1State = Ke1State; +impl KeyExchange for TripleDH { + type KE1State = Ke1State; type KE2State = Ke2State<::OutputSize>; - type KE1Message = Ke1Message; - type KE2Message = Ke2Message::OutputSize>; + type KE1Message = Ke1Message; + type KE2Message = Ke2Message::OutputSize>; type KE3Message = Ke3Message<::OutputSize>; fn generate_ke1( rng: &mut R, ) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> { - let client_e_kp = KeyPair::::generate_random(rng); + let client_e_kp = KeyPair::::generate_random(rng)?; let client_nonce = generate_nonce::(rng); let ke1_message = Ke1Message { @@ -138,18 +138,19 @@ impl KeyExchange for TripleDH { } #[allow(clippy::type_complexity)] - fn generate_ke2>( + fn generate_ke2>( rng: &mut R, serialized_credential_request: Vec, l2_bytes: Vec, ke1_message: Self::KE1Message, - client_s_pk: PublicKey, + client_s_pk: PublicKey, server_s_sk: S, id_u: Vec, id_s: Vec, context: Vec, - ) -> Result, ProtocolError> { - let server_e_kp = KeyPair::::generate_random(rng); + ) -> Result, ProtocolError> { + let server_e_kp = + KeyPair::::generate_random(rng).map_err(|_| InternalError::InvalidKeypairError)?; let server_nonce = generate_nonce::(rng); let mut transcript_hasher = D::new() @@ -162,7 +163,7 @@ impl KeyExchange for TripleDH { .chain(&server_nonce[..]) .chain(&server_e_kp.public().to_arr()); - let result = derive_3dh_keys::( + let result = derive_3dh_keys::( TripleDHComponents { pk1: ke1_message.client_e_pk.clone(), sk1: server_e_kp.private().clone(), @@ -205,12 +206,12 @@ impl KeyExchange for TripleDH { ke2_message: Self::KE2Message, ke1_state: &Self::KE1State, serialized_credential_request: &[u8], - server_s_pk: PublicKey, - client_s_sk: PrivateKey, + server_s_pk: PublicKey, + client_s_sk: PrivateKey, id_u: Vec, id_s: Vec, context: Vec, - ) -> Result, ProtocolError> { + ) -> Result, ProtocolError> { let mut transcript_hasher = D::new() .chain(STR_RFC) .chain(&serialize(&context, 2)?) @@ -220,7 +221,7 @@ impl KeyExchange for TripleDH { .chain(&l2_component[..]) .chain(&ke2_message.to_bytes_without_info_or_mac()); - let result = derive_3dh_keys::>( + let result = derive_3dh_keys::>( TripleDHComponents { pk1: ke2_message.server_e_pk.clone(), sk1: ke1_state.client_e_sk.clone(), @@ -275,7 +276,7 @@ impl KeyExchange for TripleDH { } fn ke2_message_size() -> usize { - NonceLen::USIZE + ::ElemLen::USIZE + ::OutputSize::USIZE + NonceLen::USIZE + ::PkLen::USIZE + ::OutputSize::USIZE } } @@ -286,13 +287,13 @@ impl KeyExchange for TripleDH { #[allow(clippy::upper_case_acronyms)] // The triple of public and private components used in the 3DH computation -struct TripleDHComponents> { - pk1: PublicKey, - sk1: PrivateKey, - pk2: PublicKey, +struct TripleDHComponents> { + pk1: PublicKey, + sk1: PrivateKey, + pk2: PublicKey, sk2: S, - pk3: PublicKey, - sk3: PrivateKey, + pk3: PublicKey, + sk3: PrivateKey, } // Consists of a session key, followed by two mac keys: (session_key, km2, km3) @@ -320,8 +321,8 @@ type TripleDHDerivationResult = ( // 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, +fn derive_3dh_keys>( + dh: TripleDHComponents, hashed_derivation_transcript: &[u8], ) -> Result, ProtocolError> { let ikm: Vec = [ @@ -430,9 +431,9 @@ fn generate_nonce(rng: &mut R) -> GenericArray FromBytes for Ke1State { +impl FromBytes for Ke1State { fn from_bytes(bytes: &[u8]) -> Result { - let key_len = ::ElemLen::USIZE; + let key_len = ::PkLen::USIZE; let nonce_len = NonceLen::USIZE; let checked_bytes = check_slice_size_atleast(bytes, key_len + nonce_len, "ke1_state")?; @@ -446,27 +447,19 @@ impl FromBytes for Ke1State { } } -impl ToBytesWithPointers for Ke1State { +impl ToBytes for Ke1State { fn to_bytes(&self) -> Vec { let output: Vec = [&self.client_e_sk.to_arr(), &self.client_nonce[..]].concat(); output } - - #[cfg(test)] - fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> { - vec![ - (self.client_e_sk.as_ptr(), G::ScalarLen::USIZE), - (self.client_nonce.as_ptr(), NonceLen::USIZE), - ] - } } -impl FromBytes for Ke1Message { +impl FromBytes for Ke1Message { fn from_bytes(ke1_message_bytes: &[u8]) -> Result { let nonce_len = NonceLen::USIZE; let checked_nonce = check_slice_size( ke1_message_bytes, - nonce_len + ::ElemLen::USIZE, + nonce_len + ::PkLen::USIZE, "ke1_message nonce", )?; @@ -477,7 +470,7 @@ impl FromBytes for Ke1Message { } } -impl ToBytes for Ke1Message { +impl ToBytes for Ke1Message { fn to_bytes(&self) -> Vec { [&self.client_nonce[..], &self.client_e_pk.to_arr()].concat() } @@ -498,7 +491,7 @@ impl> FromBytes for Ke2State { } } -impl> ToBytesWithPointers for Ke2State { +impl> ToBytes for Ke2State { fn to_bytes(&self) -> Vec { [ &self.km3[..], @@ -507,20 +500,11 @@ impl> ToBytesWithPointers for Ke2State { ] .concat() } - - #[cfg(test)] - fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> { - vec![ - (self.km3.as_ptr(), HashLen::USIZE), - (self.hashed_transcript.as_ptr(), HashLen::USIZE), - (self.session_key.as_ptr(), HashLen::USIZE), - ] - } } -impl> FromBytes for Ke2Message { +impl> FromBytes for Ke2Message { fn from_bytes(input: &[u8]) -> Result { - let key_len = ::ElemLen::USIZE; + let key_len = ::PkLen::USIZE; let nonce_len = NonceLen::USIZE; let checked_nonce = check_slice_size_atleast(input, nonce_len, "ke2_message nonce")?; @@ -536,7 +520,7 @@ impl> FromBytes for Ke2Message { )?; // Check the public key bytes - let server_e_pk = KeyPair::::check_public_key(PublicKey::from_bytes( + let server_e_pk = KeyPair::::check_public_key(PublicKey::from_bytes( &unchecked_server_e_pk[..key_len], )?)?; @@ -548,13 +532,13 @@ impl> FromBytes for Ke2Message { } } -impl> ToBytes for Ke2Message { +impl> ToBytes for Ke2Message { fn to_bytes(&self) -> Vec { [&self.to_bytes_without_info_or_mac(), &self.mac[..]].concat() } } -impl> Ke2Message { +impl> Ke2Message { fn to_bytes_without_info_or_mac(&self) -> Vec { [&self.server_nonce[..], &self.server_e_pk.to_arr()].concat() } @@ -579,14 +563,14 @@ impl> ToBytes for Ke3Message { // Zeroize on drop implementations // This can't be derived because of the use of a generic parameter -impl Zeroize for Ke1State { +impl Zeroize for Ke1State { fn zeroize(&mut self) { self.client_e_sk.zeroize(); self.client_nonce.zeroize(); } } -impl Drop for Ke1State { +impl Drop for Ke1State { fn drop(&mut self) { self.zeroize(); } diff --git a/src/keypair.rs b/src/keypair.rs index caf4b72..88aeb60 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -8,7 +8,7 @@ #![allow(unsafe_code)] use crate::errors::{InternalError, ProtocolError}; -use crate::group::Group; +use crate::key_exchange::group::KeGroup; use alloc::vec::Vec; use core::fmt::Debug; use core::ops::Deref; @@ -26,12 +26,12 @@ use zeroize::Zeroize; serialize = "S: serde::Serialize" )) )] -pub struct KeyPair = PrivateKey> { - pk: PublicKey, +pub struct KeyPair = PrivateKey> { + pk: PublicKey, sk: S, } -impl> Clone for KeyPair { +impl> Clone for KeyPair { fn clone(&self) -> Self { Self { pk: self.pk.clone(), @@ -40,7 +40,7 @@ impl> Clone for KeyPair { } } -impl + Debug> Debug for KeyPair { +impl + Debug> Debug for KeyPair { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("KeyPair") .field("pk", &self.pk) @@ -49,15 +49,15 @@ impl + Debug> Debug for KeyPair { } } -impl + PartialEq> PartialEq for KeyPair { +impl + PartialEq> PartialEq for KeyPair { fn eq(&self, other: &Self) -> bool { self.pk.eq(&other.pk) && self.sk.eq(&other.sk) } } -impl + Eq> Eq for KeyPair {} +impl + Eq> Eq for KeyPair {} -impl + core::hash::Hash> core::hash::Hash for KeyPair { +impl + core::hash::Hash> core::hash::Hash for KeyPair { fn hash(&self, state: &mut H) { self.pk.hash(state); self.sk.hash(state); @@ -65,22 +65,22 @@ impl + core::hash::Hash> core::hash::Hash for KeyPair< } // This can't be derived because of the use of a generic parameter -impl> Zeroize for KeyPair { +impl> Zeroize for KeyPair { fn zeroize(&mut self) { self.pk.zeroize(); self.sk.zeroize(); } } -impl> Drop for KeyPair { +impl> Drop for KeyPair { fn drop(&mut self) { self.zeroize(); } } -impl> KeyPair { +impl> KeyPair { /// The public key component - pub fn public(&self) -> &PublicKey { + pub fn public(&self) -> &PublicKey { &self.pk } @@ -93,8 +93,8 @@ impl> KeyPair { /// 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> { - G::from_element_slice(GenericArray::from_slice(&key.0)).map(|_| key) + pub(crate) fn check_public_key(key: PublicKey) -> Result, InternalError> { + KG::from_pk_slice(GenericArray::from_slice(&key.0)).map(|_| key) } /// Obtains a KeyPair from a slice representing the private key @@ -109,29 +109,22 @@ impl> KeyPair { } } -impl KeyPair { +impl KeyPair { /// Generating a random key pair given a cryptographic rng - pub(crate) fn generate_random(rng: &mut R) -> Self { - let sk = G::random_nonzero_scalar(rng); - let sk_bytes = G::scalar_as_bytes(sk); - let pk = G::base_point().mult_by_slice(&sk_bytes); - Self { + pub(crate) fn generate_random( + rng: &mut R, + ) -> Result { + let sk = KG::random_sk(rng); + let pk = KG::public_key(&sk); + Ok(Self { pk: PublicKey(Key(pk.to_arr())), - sk: PrivateKey(Key(sk_bytes)), - } - } - - #[cfg(test)] - pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> { - alloc::vec![ - (self.pk.as_ptr(), G::ElemLen::USIZE), - (self.sk.as_ptr(), G::ScalarLen::USIZE), - ] + sk: PrivateKey(Key(sk)), + }) } } #[cfg(test)] -impl KeyPair { +impl KeyPair { /// Test-only strategy returning a proptest Strategy based on /// generate_random fn uniform_keypair_strategy() -> proptest::prelude::BoxedStrategy { @@ -143,7 +136,7 @@ impl KeyPair { any::<[u8; 32]>() .prop_filter_map("valid random keypair", |seed| { let mut rng = StdRng::from_seed(seed); - Some(Self::generate_random(&mut rng)) + Some(Self::generate_random(&mut rng).unwrap()) }) .no_shrink() .boxed() @@ -217,47 +210,47 @@ impl> Key { /// Wrapper around a Key to enforce that it's a private one. #[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] #[repr(transparent)] -pub struct PrivateKey(Key); +pub struct PrivateKey(Key); impl_clone_for!( - tuple PrivateKey, + tuple PrivateKey, [0], ); impl_debug_eq_hash_for!( - tuple PrivateKey, + tuple PrivateKey, [0], ); // This can't be derived because of the use of a generic parameter -impl Zeroize for PrivateKey { +impl Zeroize for PrivateKey { fn zeroize(&mut self) { self.0.zeroize(); } } -impl Drop for PrivateKey { +impl Drop for PrivateKey { fn drop(&mut self) { self.zeroize(); } } -impl Deref for PrivateKey { - type Target = Key; +impl Deref for PrivateKey { + type Target = Key; fn deref(&self) -> &Self::Target { &self.0 } } -impl PrivateKey { +impl PrivateKey { /// Convert from bytes - pub fn from_arr(key_bytes: GenericArray) -> Self { + pub fn from_arr(key_bytes: GenericArray) -> Self { PrivateKey(Key(key_bytes)) } /// Convert from slice pub fn from_bytes(key_bytes: &[u8]) -> Result { - if key_bytes.len() == G::ScalarLen::USIZE { + if key_bytes.len() == KG::SkLen::USIZE { Ok(Self::from_arr(GenericArray::from_slice(key_bytes).clone())) } else { Err(InternalError::InvalidByteSequence) @@ -266,15 +259,15 @@ impl PrivateKey { } /// A trait specifying the requirements for a private key container -pub trait SecretKey: Clone + Sized + Zeroize { +pub trait SecretKey: Clone + Sized + Zeroize { /// Custom error type that can be passed down to `InternalError::Custom` type Error; /// Diffie-Hellman key exchange implementation - fn diffie_hellman(&self, pk: PublicKey) -> Result, InternalError>; + fn diffie_hellman(&self, pk: PublicKey) -> Result, InternalError>; /// Returns public key from private key - fn public_key(&self) -> Result, InternalError>; + fn public_key(&self) -> Result, InternalError>; /// Serialization into bytes fn serialize(&self) -> Vec; @@ -283,21 +276,16 @@ pub trait SecretKey: Clone + Sized + Zeroize { fn deserialize(input: &[u8]) -> Result>; } -impl SecretKey for PrivateKey { +impl SecretKey for PrivateKey { type Error = core::convert::Infallible; - fn diffie_hellman(&self, pk: PublicKey) -> Result, InternalError> { - let pk_data = GenericArray::::from_slice(&pk.0[..]); - let point = G::from_element_slice(pk_data)?; - let secret_data = GenericArray::::from_slice(&self.0[..]); - Ok(G::mult_by_slice(&point, secret_data).to_arr().to_vec()) + fn diffie_hellman(&self, pk: PublicKey) -> Result, InternalError> { + let pk = KG::from_pk_slice(&pk)?; + Ok(pk.diffie_hellman(self).to_vec()) } - fn public_key(&self) -> Result, InternalError> { - let bytes_data = GenericArray::::from_slice(&self.0[..]); - Ok(PublicKey(Key(G::base_point() - .mult_by_slice(bytes_data) - .to_arr()))) + fn public_key(&self) -> Result, InternalError> { + Ok(PublicKey(Key(KG::public_key(&self.0).to_arr()))) } fn serialize(&self) -> Vec { @@ -312,47 +300,47 @@ impl SecretKey for PrivateKey { /// Wrapper around a Key to enforce that it's a public one. #[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] #[repr(transparent)] -pub struct PublicKey(Key); +pub struct PublicKey(Key); impl_clone_for!( - tuple PublicKey, + tuple PublicKey, [0], ); impl_debug_eq_hash_for!( - tuple PublicKey, + tuple PublicKey, [0], ); // This can't be derived because of the use of a generic parameter -impl Zeroize for PublicKey { +impl Zeroize for PublicKey { fn zeroize(&mut self) { self.0.zeroize(); } } -impl Drop for PublicKey { +impl Drop for PublicKey { fn drop(&mut self) { self.zeroize(); } } -impl Deref for PublicKey { - type Target = Key; +impl Deref for PublicKey { + type Target = Key; fn deref(&self) -> &Self::Target { &self.0 } } -impl PublicKey { +impl PublicKey { /// Convert from bytes - pub fn from_arr(key_bytes: GenericArray) -> Self { + pub fn from_arr(key_bytes: GenericArray) -> Self { Self(Key(key_bytes)) } /// Convert from slice pub fn from_bytes(key_bytes: &[u8]) -> Result { - if key_bytes.len() == G::ElemLen::USIZE { + if key_bytes.len() == KG::PkLen::USIZE { Ok(Self::from_arr(GenericArray::from_slice(key_bytes).clone())) } else { Err(InternalError::InvalidByteSequence) @@ -372,8 +360,8 @@ mod tests { #[test] fn test_zeroize_key() -> Result<(), ProtocolError> { - let key_len = ::ElemLen::USIZE; - let mut key = Key::<::ElemLen>(GenericArray::clone_from_slice( + let key_len = ::PkLen::USIZE; + let mut key = Key::<::PkLen>(GenericArray::clone_from_slice( &alloc::vec![ 1u8; key_len @@ -381,7 +369,7 @@ mod tests { )); let ptr = key.as_ptr(); - key.zeroize(); + Zeroize::zeroize(&mut key); let bytes = unsafe { from_raw_parts(ptr, key_len) }; assert!(bytes.iter().all(|&x| x == 0)); @@ -392,15 +380,19 @@ mod tests { #[test] fn test_zeroize_keypair() -> Result<(), ProtocolError> { let mut rng = OsRng; - let mut keypair = KeyPair::::generate_random(&mut rng); - let ptrs = keypair.as_byte_ptrs(); + let mut keypair = KeyPair::::generate_random(&mut rng)?; + let pk_ptr = keypair.pk.as_ptr(); + let sk_ptr = keypair.sk.as_ptr(); + let pk_len = ::PkLen::USIZE; + let sk_len = ::SkLen::USIZE; - keypair.zeroize(); + Zeroize::zeroize(&mut keypair); - for (ptr, len) in ptrs { - let bytes = unsafe { from_raw_parts(ptr, len) }; - assert!(bytes.iter().all(|&x| x == 0)); - } + let pk_bytes = unsafe { from_raw_parts(pk_ptr, pk_len) }; + let sk_bytes = unsafe { from_raw_parts(sk_ptr, sk_len) }; + + assert!(pk_bytes.iter().all(|&x| x == 0)); + assert!(sk_bytes.iter().all(|&x| x == 0)); Ok(()) } @@ -490,12 +482,12 @@ mod tests { const PASSWORD: &str = "password"; - let sk = RistrettoPoint::random_nonzero_scalar(&mut OsRng); - let sk_bytes = RistrettoPoint::scalar_as_bytes(sk); - let sk = RemoteKey(PrivateKey::from_arr(sk_bytes)); + let sk = RistrettoPoint::random_sk(&mut OsRng); + let sk = RemoteKey(PrivateKey(Key(sk))); let keypair = KeyPair::from_private_key(sk).unwrap(); - let server_setup = ServerSetup::::new_with_key(&mut OsRng, keypair); + let server_setup = + ServerSetup::::new_with_key(&mut OsRng, keypair).unwrap(); let ClientRegistrationStartResult { message, diff --git a/src/lib.rs b/src/lib.rs index e926621..20db38a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -128,7 +128,7 @@ //! # )?; //! use opaque_ke::ServerRegistration; //! # let mut server_rng = OsRng; -//! # let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_setup = ServerSetup::::new(&mut server_rng)?; //! let server_registration_start_result = ServerRegistration::::start( //! &server_setup, //! client_registration_start_result.message, @@ -165,7 +165,7 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_setup = ServerSetup::::new(&mut server_rng)?; //! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; //! let client_registration_finish_result = client_registration_start_result.state.finish( //! &mut client_rng, @@ -204,7 +204,7 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_setup = ServerSetup::::new(&mut server_rng)?; //! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; //! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?; //! let password_file = ServerRegistration::::finish( @@ -281,10 +281,10 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_setup = ServerSetup::::new(&mut server_rng)?; //! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; //! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?; -//! # let password_file_bytes = ServerRegistration::::finish(client_registration_finish_result.message).serialize(); +//! # let password_file_bytes = ServerRegistration::::finish(client_registration_finish_result.message).serialize()?; //! # let client_login_start_result = ClientLogin::::start( //! # &mut client_rng, //! # b"password", @@ -335,10 +335,10 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_setup = ServerSetup::::new(&mut server_rng)?; //! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; //! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?; -//! # let password_file_bytes = ServerRegistration::::finish(client_registration_finish_result.message).serialize(); +//! # let password_file_bytes = ServerRegistration::::finish(client_registration_finish_result.message).serialize()?; //! # let client_login_start_result = ClientLogin::::start( //! # &mut client_rng, //! # b"password", @@ -381,10 +381,10 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_setup = ServerSetup::::new(&mut server_rng)?; //! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; //! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?; -//! # let password_file_bytes = ServerRegistration::::finish(client_registration_finish_result.message).serialize(); +//! # let password_file_bytes = ServerRegistration::::finish(client_registration_finish_result.message).serialize()?; //! # let client_login_start_result = ClientLogin::::start( //! # &mut client_rng, //! # b"password", @@ -457,7 +457,7 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_setup = ServerSetup::::new(&mut server_rng)?; //! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; //! // During registration, the client obtains a ClientRegistrationFinishResult with //! // a server_s_pk field @@ -466,7 +466,7 @@ //! server_registration_start_result.message, //! ClientRegistrationFinishParameters::default(), //! )?; -//! # let password_file_bytes = ServerRegistration::::finish(client_registration_finish_result.message).serialize(); +//! # let password_file_bytes = ServerRegistration::::finish(client_registration_finish_result.message).serialize()?; //! # let client_login_start_result = ClientLogin::::start( //! # &mut client_rng, //! # b"password", @@ -535,7 +535,7 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_setup = ServerSetup::::new(&mut server_rng)?; //! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; //! // During registration... //! let client_registration_finish_result = client_registration_start_result.state.finish( @@ -543,7 +543,7 @@ //! server_registration_start_result.message, //! ClientRegistrationFinishParameters::default() //! )?; -//! # let password_file_bytes = ServerRegistration::::finish(client_registration_finish_result.message).serialize(); +//! # let password_file_bytes = ServerRegistration::::finish(client_registration_finish_result.message).serialize()?; //! # let client_login_start_result = ClientLogin::::start( //! # &mut client_rng, //! # b"password", @@ -601,7 +601,7 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_setup = ServerSetup::::new(&mut server_rng)?; //! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; //! let client_registration_finish_result = client_registration_start_result.state.finish( //! &mut client_rng, @@ -640,10 +640,10 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_setup = ServerSetup::::new(&mut server_rng)?; //! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; //! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::new(Some(Identifiers::ClientAndServerIdentifiers(b"Alice_the_Cryptographer".to_vec(), b"Facebook".to_vec())), None))?; -//! # let password_file_bytes = ServerRegistration::::finish(client_registration_finish_result.message).serialize(); +//! # let password_file_bytes = ServerRegistration::::finish(client_registration_finish_result.message).serialize()?; //! # let client_login_start_result = ClientLogin::::start( //! # &mut client_rng, //! # b"password", @@ -690,10 +690,10 @@ //! # b"password", //! # )?; //! # let mut server_rng = OsRng; -//! # let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_setup = ServerSetup::::new(&mut server_rng)?; //! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; //! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::new(Some(Identifiers::ClientAndServerIdentifiers(b"Alice_the_Cryptographer".to_vec(), b"Facebook".to_vec())), None))?; -//! # let password_file_bytes = ServerRegistration::::finish(client_registration_finish_result.message).serialize(); +//! # let password_file_bytes = ServerRegistration::::finish(client_registration_finish_result.message).serialize()?; //! # let client_login_start_result = ClientLogin::::start( //! # &mut client_rng, //! # b"password", @@ -846,7 +846,6 @@ mod impls; mod serialization; pub mod ciphersuite; mod envelope; -pub mod group; pub mod hash; pub mod key_exchange; pub mod keypair; @@ -854,11 +853,6 @@ mod messages; mod opaque; pub mod slow_hash; -#[cfg(feature = "bench")] -pub mod oprf; -#[cfg(not(feature = "bench"))] -mod oprf; - #[cfg(test)] mod tests; diff --git a/src/messages.rs b/src/messages.rs index 4f79cbd..600ae2a 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -12,8 +12,10 @@ use crate::{ utils::{check_slice_size, check_slice_size_atleast}, ProtocolError, }, - group::Group, - key_exchange::traits::{FromBytes, KeyExchange, ToBytes}, + key_exchange::{ + group::KeGroup, + traits::{FromBytes, KeyExchange, ToBytes}, + }, keypair::{KeyPair, PublicKey, SecretKey}, opaque::ServerSetup, }; @@ -21,6 +23,7 @@ use alloc::vec::Vec; use digest::Digest; use generic_array::{typenum::Unsigned, GenericArray}; use rand::{CryptoRng, RngCore}; +use voprf::group::Group; //////////////////////////// // High-level API Structs // @@ -30,14 +33,14 @@ use rand::{CryptoRng, RngCore}; /// The message sent by the client to the server, to initiate registration pub struct RegistrationRequest { /// blinded password information - pub(crate) alpha: CS::OprfGroup, + pub(crate) blinded_element: voprf::BlindedElement, } /// The answer sent by the server to the user, upon reception of the /// registration attempt pub struct RegistrationResponse { /// The server's oprf output - pub(crate) beta: CS::OprfGroup, + pub(crate) evaluation_element: voprf::EvaluationElement, /// Server's static public key pub(crate) server_s_pk: PublicKey, } @@ -56,8 +59,7 @@ pub struct RegistrationUpload { /// The message sent by the user to the server, to initiate registration pub struct CredentialRequest { - /// blinded password information - pub(crate) alpha: CS::OprfGroup, + pub(crate) blinded_element: voprf::BlindedElement, pub(crate) ke1_message: >::KE1Message, } @@ -65,7 +67,7 @@ pub struct CredentialRequest { /// login attempt pub struct CredentialResponse { /// the server's oprf output - pub(crate) beta: CS::OprfGroup, + pub(crate) evaluation_element: voprf::EvaluationElement, pub(crate) masking_nonce: Vec, pub(crate) masked_response: Vec, pub(crate) ke2_message: >::KE2Message, @@ -85,69 +87,59 @@ pub struct CredentialFinalization { impl RegistrationRequest { /// Only used for testing purposes #[cfg(test)] - pub fn get_alpha_for_testing(&self) -> CS::OprfGroup { - self.alpha + pub fn get_blinded_element_for_testing( + &self, + ) -> voprf::BlindedElement { + self.blinded_element.clone() } /// Serialization into bytes - pub fn serialize(&self) -> Vec { - self.alpha.to_arr().to_vec() + pub fn serialize(&self) -> Result, ProtocolError> { + Ok(self.blinded_element.serialize()) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let elem_len = ::ElemLen::USIZE; - let checked_slice = check_slice_size(input, elem_len, "first_message_bytes")?; - // Check that the message is actually containing an element of the - // correct subgroup - let arr = GenericArray::from_slice(checked_slice); - let alpha = CS::OprfGroup::from_element_slice(arr)?; - - // Throw an error if the identity group element is encountered - if alpha.is_identity() { - return Err(ProtocolError::IdentityGroupElementError); - } - Ok(Self { alpha }) + Ok(Self { + blinded_element: voprf::BlindedElement::deserialize(input)?, + }) } } impl RegistrationResponse { /// Serialization into bytes - pub fn serialize(&self) -> Vec { - [self.beta.to_arr().to_vec(), self.server_s_pk.to_vec()].concat() + pub fn serialize(&self) -> Result, ProtocolError> { + Ok([ + self.evaluation_element.serialize(), + self.server_s_pk.to_vec(), + ] + .concat()) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let elem_len = ::ElemLen::USIZE; - let key_len = ::ElemLen::USIZE; + let key_len = ::PkLen::USIZE; let checked_slice = check_slice_size(input, elem_len + key_len, "registration_response_bytes")?; - // Check that the message is actually containing an element of the - // correct subgroup - let arr = GenericArray::from_slice(&checked_slice[..elem_len]); - let beta = CS::OprfGroup::from_element_slice(arr)?; - - // Throw an error if the identity group element is encountered - if beta.is_identity() { - return Err(ProtocolError::IdentityGroupElementError); - } - // Ensure that public key is valid let server_s_pk = KeyPair::::check_public_key(PublicKey::from_bytes( &checked_slice[elem_len..], )?)?; - Ok(Self { beta, server_s_pk }) + Ok(Self { + evaluation_element: voprf::EvaluationElement::deserialize(&checked_slice[..elem_len])?, + server_s_pk, + }) } #[cfg(test)] /// Only used for tests, where we can set the beta value to test for the reflection /// error case - pub fn set_beta_for_testing(&self, new_beta: CS::OprfGroup) -> Self { + pub fn set_evaluation_element_for_testing(&self, beta: CS::OprfGroup) -> Self { Self { - beta: new_beta, + evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta), server_s_pk: self.server_s_pk.clone(), } } @@ -155,18 +147,18 @@ impl RegistrationResponse { impl RegistrationUpload { /// Serialization into bytes - pub fn serialize(&self) -> Vec { - [ + pub fn serialize(&self) -> Result, ProtocolError> { + Ok([ self.client_s_pk.to_arr().to_vec(), self.masking_key.to_vec(), self.envelope.serialize(), ] - .concat() + .concat()) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let key_len = ::ElemLen::USIZE; + let key_len = ::PkLen::USIZE; let hash_len = ::OutputSize::USIZE; let checked_slice = check_slice_size_atleast(input, key_len + hash_len, "registration_upload_bytes")?; @@ -200,8 +192,12 @@ impl RegistrationUpload { impl CredentialRequest { /// Serialization into bytes - pub fn serialize(&self) -> Vec { - [self.alpha.to_arr().to_vec(), self.ke1_message.to_bytes()].concat() + pub fn serialize(&self) -> Result, ProtocolError> { + Ok([ + self.blinded_element.serialize(), + self.ke1_message.to_bytes(), + ] + .concat()) } /// Deserialization from bytes @@ -212,11 +208,12 @@ impl CredentialRequest { // Check that the message is actually containing an element of the // correct subgroup - let arr = GenericArray::from_slice(&checked_slice[..elem_len]); - let alpha = CS::OprfGroup::from_element_slice(arr)?; + let blinded_element = voprf::BlindedElement::::deserialize( + &checked_slice[..elem_len], + )?; // Throw an error if the identity group element is encountered - if alpha.is_identity() { + if blinded_element.value().is_identity() { return Err(ProtocolError::IdentityGroupElementError); } @@ -225,24 +222,33 @@ impl CredentialRequest { &checked_slice[elem_len..], )?; - Ok(Self { alpha, ke1_message }) + Ok(Self { + blinded_element, + ke1_message, + }) } /// Only used for testing purposes #[cfg(test)] - pub fn get_alpha_for_testing(&self) -> CS::OprfGroup { - self.alpha + pub fn get_blinded_element_for_testing( + &self, + ) -> voprf::BlindedElement { + self.blinded_element.clone() } } impl CredentialResponse { /// Serialization into bytes - pub fn serialize(&self) -> Vec { - [ - Self::serialize_without_ke(&self.beta, &self.masking_nonce, &self.masked_response), + pub fn serialize(&self) -> Result, ProtocolError> { + Ok([ + Self::serialize_without_ke( + &self.evaluation_element.value(), + &self.masking_nonce, + &self.masked_response, + ), self.ke2_message.to_bytes(), ] - .concat() + .concat()) } pub(crate) fn serialize_without_ke( @@ -256,7 +262,7 @@ impl CredentialResponse { /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let elem_len = ::ElemLen::USIZE; - let key_len = ::ElemLen::USIZE; + let key_len = ::PkLen::USIZE; let nonce_len: usize = 32; let envelope_len = Envelope::::len(); let masked_response_len = key_len + envelope_len; @@ -271,11 +277,11 @@ impl CredentialResponse { // Check that the message is actually containing an element of the // correct subgroup let beta_bytes = &checked_slice[..elem_len]; - let arr = GenericArray::from_slice(beta_bytes); - let beta = CS::OprfGroup::from_element_slice(arr)?; + let evaluation_element = + voprf::EvaluationElement::::deserialize(beta_bytes)?; // Throw an error if the identity group element is encountered - if beta.is_identity() { + if evaluation_element.value().is_identity() { return Err(ProtocolError::IdentityGroupElementError); } @@ -289,7 +295,7 @@ impl CredentialResponse { )?; Ok(Self { - beta, + evaluation_element, masking_nonce, masked_response, ke2_message, @@ -299,9 +305,9 @@ impl CredentialResponse { #[cfg(test)] /// Only used for tests, where we can set the beta value to test for the reflection /// error case - pub fn set_beta_for_testing(&self, new_beta: CS::OprfGroup) -> Self { + pub fn set_evaluation_element_for_testing(&self, beta: CS::OprfGroup) -> Self { Self { - beta: new_beta, + evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta), masking_nonce: self.masking_nonce.clone(), masked_response: self.masked_response.clone(), ke2_message: self.ke2_message.clone(), @@ -311,8 +317,8 @@ impl CredentialResponse { impl CredentialFinalization { /// Serialization into bytes - pub fn serialize(&self) -> Vec { - self.ke3_message.to_bytes() + pub fn serialize(&self) -> Result, ProtocolError> { + Ok(self.ke3_message.to_bytes()) } /// Deserialization from bytes @@ -332,19 +338,19 @@ impl CredentialFinalization { impl_clone_for!( struct RegistrationRequest, - [alpha], + [blinded_element], ); -impl_debug_eq_hash_for!(struct RegistrationRequest, [alpha], [CS::OprfGroup]); +impl_debug_eq_hash_for!(struct RegistrationRequest, [blinded_element], [CS::OprfGroup, CS::Hash]); impl_serialize_and_deserialize_for!(RegistrationRequest); impl_clone_for!( struct RegistrationResponse, - [beta, server_s_pk], + [evaluation_element, server_s_pk], ); impl_debug_eq_hash_for!( struct RegistrationResponse, - [beta, server_s_pk], - [CS::OprfGroup], + [evaluation_element, server_s_pk], + [CS::OprfGroup, CS::Hash], ); impl_serialize_and_deserialize_for!(RegistrationResponse); @@ -360,11 +366,11 @@ impl_serialize_and_deserialize_for!(RegistrationUpload); impl_clone_for!( struct CredentialRequest, - [alpha, ke1_message], + [blinded_element, ke1_message], ); impl_debug_eq_hash_for!( struct CredentialRequest, - [alpha, ke1_message], + [blinded_element, ke1_message], [ CS::OprfGroup, >::KE1Message @@ -374,11 +380,11 @@ impl_serialize_and_deserialize_for!(CredentialRequest); impl_clone_for!( struct CredentialResponse, - [beta, masking_nonce, masked_response, ke2_message], + [evaluation_element, masking_nonce, masked_response, ke2_message], ); impl_debug_eq_hash_for!( struct CredentialResponse, - [beta, masking_nonce, masked_response, ke2_message], + [evaluation_element, masking_nonce, masked_response, ke2_message], [ CS::OprfGroup, >::KE2Message, diff --git a/src/opaque.rs b/src/opaque.rs index b18f178..1f8e91f 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -9,11 +9,12 @@ use crate::{ ciphersuite::CipherSuite, envelope::Envelope, errors::{utils::check_slice_size, InternalError, ProtocolError}, - group::Group, hash::Hash, - key_exchange::traits::{FromBytes, KeyExchange, ToBytesWithPointers}, + key_exchange::{ + group::KeGroup, + traits::{FromBytes, KeyExchange, ToBytes}, + }, keypair::{KeyPair, PrivateKey, PublicKey, SecretKey}, - oprf, serialization::{serialize, tokenize}, slow_hash::SlowHash, CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest, @@ -26,6 +27,8 @@ use digest::Digest; use generic_array::{typenum::Unsigned, GenericArray}; use hkdf::Hkdf; use rand::{CryptoRng, RngCore}; +use subtle::ConstantTimeEq; +use voprf::group::Group; use zeroize::Zeroize; /////////////// @@ -33,10 +36,10 @@ use zeroize::Zeroize; // ========= // /////////////// -const STR_CREDENTIAL_RESPONSE_PAD: &[u8] = b"CredentialResponsePad"; -const STR_MASKING_KEY: &[u8] = b"MaskingKey"; -const STR_OPRF_KEY: &[u8] = b"OprfKey"; -const STR_OPAQUE_DERIVE_KEY_PAIR: &[u8] = b"OPAQUE-DeriveKeyPair"; +const STR_CREDENTIAL_RESPONSE_PAD: &[u8; 21] = b"CredentialResponsePad"; +const STR_MASKING_KEY: &[u8; 10] = b"MaskingKey"; +const STR_OPRF_KEY: &[u8; 7] = b"OprfKey"; +const STR_OPAQUE_DERIVE_KEY_PAIR: &[u8; 20] = b"OPAQUE-DeriveKeyPair"; //////////////////////////// // High-level API Structs // @@ -73,16 +76,15 @@ impl_debug_eq_hash_for!( /// The state elements the client holds to register itself pub struct ClientRegistration { - alpha: CS::OprfGroup, - /// token containing the client's password and the blinding factor - pub(crate) token: oprf::Token, + pub(crate) oprf_client: voprf::NonVerifiableClient, + pub(crate) blinded_element: voprf::BlindedElement, } -impl_clone_for!(struct ClientRegistration, [token, alpha]); +impl_clone_for!(struct ClientRegistration, [oprf_client, blinded_element]); impl_debug_eq_hash_for!( struct ClientRegistration, - [token], - [oprf::Token], + [oprf_client], + [voprf::NonVerifiableClient], ); impl_serialize_and_deserialize_for!(ClientRegistration); @@ -97,27 +99,19 @@ impl_debug_eq_hash_for!( impl_serialize_and_deserialize_for!(ServerRegistration); /// The state elements the client holds to perform a login -#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] -#[cfg_attr( - feature = "serialize", - serde(bound( - deserialize = "oprf::Token: serde::Deserialize<'de>, >::KE1State: serde::Deserialize<'de>", - serialize = "oprf::Token: serde::Serialize, >::KE1State: serde::Serialize" - )) -)] pub struct ClientLogin { - /// token containing the client's password and the blinding factor - token: oprf::Token, + oprf_client: voprf::NonVerifiableClient, ke1_state: >::KE1State, serialized_credential_request: Vec, } -impl_clone_for!(struct ClientLogin, [token, ke1_state, serialized_credential_request]); +impl_clone_for!(struct ClientLogin, [oprf_client, ke1_state, serialized_credential_request]); impl_debug_eq_hash_for!( struct ClientLogin, - [token, ke1_state, serialized_credential_request], - [oprf::Token, >::KE1State], + [oprf_client, ke1_state, serialized_credential_request], + [voprf::NonVerifiableClient, >::KE1State], ); +impl_serialize_and_deserialize_for!(ClientLogin); /// The state elements the server holds to record a login pub struct ServerLogin { @@ -143,8 +137,8 @@ impl_serialize_and_deserialize_for!(ServerLogin); impl ServerSetup> { /// Generate a new instance of server setup - pub fn new(rng: &mut R) -> Self { - let keypair = KeyPair::::generate_random(rng); + pub fn new(rng: &mut R) -> Result { + let keypair = KeyPair::::generate_random(rng)?; Self::new_with_key(rng, keypair) } } @@ -154,31 +148,31 @@ impl> ServerSetup { pub fn new_with_key( rng: &mut R, keypair: KeyPair, - ) -> Self { + ) -> Result { let mut seed = vec![0u8; ::OutputSize::USIZE]; rng.fill_bytes(&mut seed); - Self { + Ok(Self { oprf_seed: GenericArray::clone_from_slice(&seed[..]), keypair, - fake_keypair: KeyPair::::generate_random(rng), - } + fake_keypair: KeyPair::::generate_random(rng)?, + }) } /// Serialization into bytes - pub fn serialize(&self) -> Vec { - [ + pub fn serialize(&self) -> Result, ProtocolError> { + Ok([ self.oprf_seed.to_vec(), self.keypair.private().serialize(), self.fake_keypair.private().serialize(), ] - .concat() + .concat()) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result> { let seed_len = ::OutputSize::USIZE; - let key_len = ::ScalarLen::USIZE; + let key_len = ::SkLen::USIZE; let checked_slice = check_slice_size(input, seed_len + key_len + key_len, "server_setup")?; Ok(Self { @@ -200,56 +194,37 @@ impl> ServerSetup { impl ClientRegistration { /// Serialization into bytes - pub fn serialize(&self) -> Vec { - [ - &self.alpha.to_arr().to_vec(), - &CS::OprfGroup::scalar_as_bytes(self.token.blind)[..], - &self.token.data, + pub fn serialize(&self) -> Result, ProtocolError> { + Ok([ + serialize(&self.oprf_client.serialize(), 2)?, + serialize(&self.blinded_element.serialize(), 2)?, ] - .concat() + .concat()) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let elem_len = ::ElemLen::USIZE; - let scalar_len = ::ScalarLen::USIZE; - let min_expected_len = elem_len + scalar_len; - let checked_slice = (if input.len() <= min_expected_len { - Err(InternalError::SizeError { - name: "client_registration_bytes", - len: min_expected_len, - actual_len: input.len(), - }) - } else { - Ok(input) - })?; + let (serialized_oprf_client, remainder) = tokenize(input, 2)?; + let (serialized_blinded_element, remainder) = tokenize(&remainder, 2)?; - let alpha = CS::OprfGroup::from_element_slice(GenericArray::from_slice( - &checked_slice[..elem_len], - ))?; + if !remainder.is_empty() { + return Err(ProtocolError::SerializationError); + } - // Check that the message is actually containing an element of the - // correct subgroup - let blinding_factor_bytes = - GenericArray::from_slice(&checked_slice[elem_len..elem_len + scalar_len]); - let blinding_factor = CS::OprfGroup::from_scalar_slice(blinding_factor_bytes)?; - - let password = checked_slice[elem_len + scalar_len..].to_vec(); Ok(Self { - alpha, - token: oprf::Token { - data: password, - blind: blinding_factor, - }, + oprf_client: voprf::NonVerifiableClient::deserialize(&serialized_oprf_client)?, + blinded_element: voprf::BlindedElement::deserialize(&serialized_blinded_element)?, }) } #[cfg(test)] - pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> { - vec![ - (self.token.data.as_ptr(), self.token.data.len()), - /* cannot provide raw pointer to self.token.blind until this is exposed in curve25519_dalek::scalar::Scalar */ + /// Only used for testing zeroize + pub(crate) fn to_vec(&self) -> Result, ProtocolError> { + Ok([ + self.oprf_client.serialize(), + self.blinded_element.serialize(), ] + .concat()) } /// Returns an initial "blinded" request to send to the server, as well as a ClientRegistration @@ -257,12 +232,16 @@ impl ClientRegistration { blinding_factor_rng: &mut R, password: &[u8], ) -> Result, ProtocolError> { - let (token, alpha) = - oprf::blind::(password, blinding_factor_rng)?; + let blind_result = blind::(blinding_factor_rng, password)?; Ok(ClientRegistrationStartResult { - message: RegistrationRequest:: { alpha }, - state: Self { alpha, token }, + message: RegistrationRequest:: { + blinded_element: blind_result.message.clone(), + }, + state: Self { + oprf_client: blind_result.state, + blinded_element: blind_result.message, + }, }) } @@ -271,27 +250,35 @@ impl ClientRegistration { pub fn finish( self, rng: &mut R, - r2: RegistrationResponse, + registration_response: RegistrationResponse, params: ClientRegistrationFinishParameters, ) -> Result, ProtocolError> { // Check for reflected value from server and halt if detected - if self.alpha.ct_equal(&r2.beta) { + if self + .blinded_element + .value() + .ct_eq(®istration_response.evaluation_element.value()) + .into() + { return Err(ProtocolError::ReflectedValueError); } - let password_derived_key = - get_password_derived_key::(&self.token, r2.beta, params.slow_hash)?; - #[cfg_attr(not(test), allow(unused_variables))] - let (randomized_pwd, h) = Hkdf::::extract(None, &password_derived_key); + let (randomized_pwd, randomized_pwd_hasher) = get_password_derived_key::( + self.oprf_client.clone(), + registration_response.evaluation_element, + params.slow_hash, + )?; + let mut masking_key = vec![0u8; ::OutputSize::USIZE]; - h.expand(STR_MASKING_KEY, &mut masking_key) + randomized_pwd_hasher + .expand(STR_MASKING_KEY, &mut masking_key) .map_err(|_| InternalError::HkdfError)?; let result = Envelope::::seal( rng, - &password_derived_key, - &r2.server_s_pk, + randomized_pwd_hasher, + ®istration_response.server_s_pk, params.identifiers, )?; @@ -302,7 +289,7 @@ impl ClientRegistration { client_s_pk: result.1, }, export_key: result.2, - server_s_pk: r2.server_s_pk, + server_s_pk: registration_response.server_s_pk, #[cfg(test)] state: self, #[cfg(test)] @@ -315,7 +302,7 @@ impl ClientRegistration { impl ServerRegistration { /// Serialization into bytes - pub fn serialize(&self) -> Vec { + pub fn serialize(&self) -> Result, ProtocolError> { self.0.serialize() } @@ -324,15 +311,6 @@ impl ServerRegistration { Ok(Self(RegistrationUpload::deserialize(input)?)) } - #[cfg(test)] - pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> { - [ - self.0.envelope.as_byte_ptrs(), - vec![(self.0.client_s_pk.as_ptr(), self.0.client_s_pk.len())], - /* cannot provide raw pointer to self.oprf_key until this is exposed in curve25519_dalek::scalar::Scalar */ - ].concat() - } - /// From the client's "blinded" password, returns a response to be /// sent back to the client, as well as a ServerRegistration pub fn start>( @@ -345,16 +323,16 @@ impl ServerRegistration { credential_identifier, )?; - // Compute beta = alpha^oprf_key - let beta = oprf::evaluate::(message.alpha, &oprf_key); + let server = voprf::NonVerifiableServer::new_with_key(&oprf_key)?; + let evaluate_result = server.evaluate(message.blinded_element, None)?; Ok(ServerRegistrationStartResult { message: RegistrationResponse { - beta, + evaluation_element: evaluate_result.message, server_s_pk: server_setup.keypair.public().clone(), }, #[cfg(test)] - oprf_key: CS::OprfGroup::scalar_as_bytes(oprf_key), + oprf_key: GenericArray::clone_from_slice(&oprf_key), }) } @@ -380,10 +358,9 @@ impl ClientLogin { /// Serialization into bytes pub fn serialize(&self) -> Result, ProtocolError> { let output: Vec = [ - &CS::OprfGroup::scalar_as_bytes(self.token.blind)[..], - &serialize(&self.serialized_credential_request, 2)?, - &serialize(&self.ke1_state.to_bytes(), 2)?, - &self.token.data, + serialize(&self.oprf_client.serialize(), 2)?, + serialize(&self.serialized_credential_request, 2)?, + serialize(&self.ke1_state.to_bytes(), 2)?, ] .concat(); Ok(output) @@ -391,47 +368,34 @@ impl ClientLogin { /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let scalar_len = ::ScalarLen::USIZE; - let checked_slice = (if input.len() <= scalar_len { - Err(InternalError::SizeError { - name: "client_login_bytes", - len: scalar_len, - actual_len: input.len(), - }) - } else { - Ok(input) - })?; + let (serialized_oprf_client, remainder) = tokenize(input, 2)?; + let (serialized_credential_request, remainder) = tokenize(&remainder, 2)?; + let (ke1_state_bytes, remainder) = tokenize(&remainder, 2)?; - let blinding_factor_bytes = GenericArray::from_slice(&checked_slice[..scalar_len]); - let blinding_factor = CS::OprfGroup::from_scalar_slice(blinding_factor_bytes)?; - - let (serialized_credential_request, remainder) = tokenize(&checked_slice[scalar_len..], 2)?; - let (ke1_state_bytes, password) = tokenize(&remainder, 2)?; + if !remainder.is_empty() { + return Err(ProtocolError::SerializationError); + } let ke1_state = >::KE1State::from_bytes::( &ke1_state_bytes[..], )?; Ok(Self { - token: oprf::Token { - data: password, - blind: blinding_factor, - }, + oprf_client: voprf::NonVerifiableClient::deserialize(&serialized_oprf_client)?, ke1_state, serialized_credential_request, }) } #[cfg(test)] - pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> { - [ - vec![ - (self.token.data.as_ptr(), self.token.data.len()), - /* cannot provide raw pointer to self.token.blind until this is exposed in curve25519_dalek::scalar::Scalar */ - ], - self.ke1_state.as_byte_ptrs(), - vec![ (self.serialized_credential_request.as_ptr(), self.serialized_credential_request.len()) ], - ].concat() + /// Only used for testing zeroize + pub(crate) fn to_vec(&self) -> Result, ProtocolError> { + Ok([ + self.oprf_client.serialize(), + self.serialized_credential_request.clone(), + self.ke1_state.to_bytes(), + ] + .concat()) } } @@ -441,17 +405,19 @@ impl ClientLogin { rng: &mut R, password: &[u8], ) -> Result, ProtocolError> { - let (token, alpha) = oprf::blind::(password, rng)?; - + let blind_result = blind::(rng, password)?; let (ke1_state, ke1_message) = CS::KeyExchange::generate_ke1(rng)?; - let credential_request = CredentialRequest { alpha, ke1_message }; - let serialized_credential_request = credential_request.serialize(); + let credential_request = CredentialRequest { + blinded_element: blind_result.message, + ke1_message, + }; + let serialized_credential_request = credential_request.serialize()?; Ok(ClientLoginStartResult { message: credential_request, state: Self { - token, + oprf_client: blind_result.state, ke1_state, serialized_credential_request, }, @@ -468,19 +434,24 @@ impl ClientLogin { // Check if beta value from server is equal to alpha value from client let credential_request = CredentialRequest::::deserialize(&self.serialized_credential_request[..])?; - if credential_request.alpha.ct_equal(&credential_response.beta) { + if credential_request + .blinded_element + .value() + .ct_eq(&credential_response.evaluation_element.value()) + .into() + { return Err(ProtocolError::ReflectedValueError); } - let password_derived_key = get_password_derived_key::( - &self.token, - credential_response.beta, + let (_, randomized_pwd_hasher) = get_password_derived_key::( + self.oprf_client.clone(), + credential_response.evaluation_element.clone(), params.slow_hash, )?; - let h = Hkdf::::new(None, &password_derived_key); let mut masking_key = vec![0u8; ::OutputSize::USIZE]; - h.expand(STR_MASKING_KEY, &mut masking_key) + randomized_pwd_hasher + .expand(STR_MASKING_KEY, &mut masking_key) .map_err(|_| InternalError::HkdfError)?; let (server_s_pk, envelope) = unmask_response::( @@ -496,7 +467,7 @@ impl ClientLogin { let opened_envelope = &envelope .open( - &password_derived_key, + randomized_pwd_hasher, &server_s_pk_bytes, ¶ms.identifiers, ) @@ -508,7 +479,7 @@ impl ClientLogin { })?; let credential_response_component = CredentialResponse::::serialize_without_ke( - &credential_response.beta, + &credential_response.evaluation_element.value(), &credential_response.masking_nonce, &credential_response.masked_response, ); @@ -544,8 +515,8 @@ impl ClientLogin { impl ServerLogin { /// Serialization into bytes - pub fn serialize(&self) -> Vec { - self.ke2_state.to_bytes() + pub fn serialize(&self) -> Result, ProtocolError> { + Ok(self.ke2_state.to_bytes()) } /// Deserialization from bytes @@ -565,7 +536,7 @@ impl ServerLogin { rng: &mut R, server_setup: &ServerSetup, password_file: Option>, - l1: CredentialRequest, + credential_request: CredentialRequest, credential_identifier: &[u8], params: ServerLoginStartParameters, ) -> Result, ProtocolError> { @@ -605,23 +576,33 @@ impl ServerLogin { ) .map_err(ProtocolError::into_custom)?; - let l1_bytes = &l1.serialize(); + let credential_request_bytes = credential_request + .serialize() + .map_err(ProtocolError::into_custom)?; let oprf_key = oprf_key_from_seed::( &server_setup.oprf_seed, credential_identifier, ) .map_err(ProtocolError::into_custom)?; - let beta = oprf::evaluate(l1.alpha, &oprf_key); + let server = voprf::NonVerifiableServer::new_with_key(&oprf_key) + .map_err(|e| ProtocolError::into_custom(e.into()))?; + let evaluate_result = server + .evaluate(credential_request.blinded_element, None) + .map_err(|e| ProtocolError::into_custom(e.into()))?; + let evaluation_element = evaluate_result.message; - let credential_response_component = - CredentialResponse::::serialize_without_ke(&beta, &masking_nonce, &masked_response); + let credential_response_component = CredentialResponse::::serialize_without_ke( + &evaluation_element.value(), + &masking_nonce, + &masked_response, + ); let result = CS::KeyExchange::generate_ke2( rng, - l1_bytes.to_vec(), + credential_request_bytes, credential_response_component, - l1.ke1_message, + credential_request.ke1_message, client_s_pk, server_s_sk.clone(), id_u, @@ -630,7 +611,7 @@ impl ServerLogin { )?; let credential_response = CredentialResponse { - beta, + evaluation_element, masking_nonce, masked_response, ke2_message: result.1, @@ -647,7 +628,7 @@ impl ServerLogin { #[cfg(test)] server_mac_key: result.3, #[cfg(test)] - oprf_key: CS::OprfGroup::scalar_as_bytes(oprf_key), + oprf_key: GenericArray::clone_from_slice(&oprf_key), }) } @@ -669,11 +650,6 @@ impl ServerLogin { state: self, }) } - - #[cfg(test)] - pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> { - self.ke2_state.as_byte_ptrs() - } } ///////////////////////// @@ -969,31 +945,47 @@ impl Clone for ServerLoginStartResult { // Helper functions +#[allow(clippy::type_complexity)] fn get_password_derived_key( - token: &oprf::Token, - beta: CS::OprfGroup, + oprf_client: voprf::NonVerifiableClient, + evaluation_element: voprf::EvaluationElement, slow_hash: Option<&CS::SlowHash>, -) -> Result, ProtocolError> { - let oprf_output = oprf::finalize::(&token.data, &token.blind, beta)?; +) -> Result< + ( + GenericArray::OutputSize>, + Hkdf, + ), + ProtocolError, +> { + let oprf_output = oprf_client.finalize(evaluation_element, None)?; - if let Some(slow_hash) = slow_hash { - slow_hash.hash(oprf_output) + let hardened_output = if let Some(slow_hash) = slow_hash { + slow_hash.hash(oprf_output.clone()) } else { - CS::SlowHash::default().hash(oprf_output) + CS::SlowHash::default().hash(oprf_output.clone()) } - .map_err(ProtocolError::from) + .map_err(ProtocolError::from)?; + + Ok(Hkdf::::extract( + None, + &[oprf_output.to_vec(), hardened_output].concat(), + )) } fn oprf_key_from_seed( oprf_seed: &GenericArray, credential_identifier: &[u8], -) -> Result { +) -> Result, ProtocolError> { let mut ikm = vec![0u8; G::ScalarLen::USIZE]; Hkdf::::from_prk(oprf_seed) .map_err(|_| InternalError::HkdfError)? .expand(&[credential_identifier, STR_OPRF_KEY].concat(), &mut ikm) .map_err(|_| InternalError::HkdfError)?; - G::hash_to_scalar::(&ikm[..], STR_OPAQUE_DERIVE_KEY_PAIR) + Ok(G::scalar_as_bytes(G::hash_to_scalar::( + Some(&ikm[..]), + GenericArray::from(*STR_OPAQUE_DERIVE_KEY_PAIR), + )?) + .to_vec()) } fn mask_response( @@ -1002,7 +994,7 @@ fn mask_response( server_s_pk: &PublicKey, envelope: &Envelope, ) -> Result, ProtocolError> { - let mut xor_pad = vec![0u8; ::ElemLen::USIZE + Envelope::::len()]; + let mut xor_pad = vec![0u8; ::PkLen::USIZE + Envelope::::len()]; Hkdf::::from_prk(masking_key) .map_err(|_| InternalError::HkdfError)? .expand( @@ -1025,7 +1017,7 @@ fn unmask_response( masking_nonce: &[u8], masked_response: &[u8], ) -> Result<(PublicKey, Envelope), ProtocolError> { - let mut xor_pad = vec![0u8; ::ElemLen::USIZE + Envelope::::len()]; + let mut xor_pad = vec![0u8; ::PkLen::USIZE + Envelope::::len()]; Hkdf::::from_prk(masking_key) .map_err(|_| InternalError::HkdfError)? .expand( @@ -1038,7 +1030,7 @@ fn unmask_response( .zip(masked_response.iter()) .map(|(&x1, &x2)| x1 ^ x2) .collect(); - let key_len = ::ElemLen::USIZE; + let key_len = ::PkLen::USIZE; let unchecked_server_s_pk = PublicKey::from_bytes(&plaintext[..key_len])?; let envelope = Envelope::deserialize(&plaintext[key_len..])?; @@ -1066,13 +1058,49 @@ pub(crate) fn bytestrings_from_identifiers( )) } +/// 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], +) -> Result< + voprf::NonVerifiableClientBlindResult, + voprf::errors::InternalError, +> { + #[cfg(not(test))] + let result = voprf::NonVerifiableClient::blind(password.to_vec(), rng)?; + + #[cfg(test)] + let result = { + let mut blind_bytes = vec![0u8; ::ScalarLen::USIZE]; + let blind = loop { + rng.fill_bytes(&mut blind_bytes); + let scalar = ::from_scalar_slice_unchecked( + &GenericArray::clone_from_slice(&blind_bytes), + )?; + match scalar + .ct_eq(&::scalar_zero()) + .into() + { + false => break scalar, + true => (), + } + }; + voprf::NonVerifiableClient::deterministic_blind_unchecked(password.to_vec(), blind)? + }; + + Ok(result) +} + // Zeroize on drop implementations // This can't be derived because of the use of a phantom parameter impl Zeroize for ClientRegistration { fn zeroize(&mut self) { - self.token.data.zeroize(); - self.token.blind.zeroize(); + self.oprf_client.zeroize(); + self.blinded_element.zeroize(); } } @@ -1100,8 +1128,7 @@ impl Drop for ServerRegistration { // This can't be derived because of the use of a phantom parameter impl Zeroize for ClientLogin { fn zeroize(&mut self) { - self.token.data.zeroize(); - self.token.blind.zeroize(); + self.oprf_client.zeroize(); self.ke1_state.zeroize(); self.serialized_credential_request.zeroize(); } diff --git a/src/oprf.rs b/src/oprf.rs deleted file mode 100644 index 9bd9dc3..0000000 --- a/src/oprf.rs +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. -// -// This source code is licensed under the MIT license found in the -// LICENSE file in the root directory of this source tree. - -use crate::{errors::ProtocolError, group::Group, hash::Hash, serialization::serialize}; -use digest::Digest; -use generic_array::GenericArray; -use rand::{CryptoRng, RngCore}; - -/// Used to store the OPRF input and blinding factor -#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] -pub struct Token { - pub(crate) data: alloc::vec::Vec, - pub(crate) blind: Grp::Scalar, -} - -impl_clone_for!(struct Token, [data, blind]); -impl_debug_eq_hash_for!(struct Token, [data, blind], [Grp::Scalar]); - -static STR_VOPRF: &[u8] = b"HashToGroup-VOPRF07-"; -static STR_VOPRF_FINALIZE: &[u8] = b"Finalize-VOPRF07-"; -static MODE_BASE: u8 = 0x00; - -/// Computes the first step for the multiplicative blinding version of DH-OPRF. This -/// message is sent from the client (who holds the input) to the server (who holds the OPRF key). -/// The client can also pass in an optional "pepper" string to be mixed in with the input through -/// an HKDF computation. -pub(crate) fn blind( - input: &[u8], - blinding_factor_rng: &mut R, -) -> Result<(Token, G), ProtocolError> { - // Choose a random scalar that must be non-zero - let blind = G::random_nonzero_scalar(blinding_factor_rng); - let dst = [STR_VOPRF, &G::get_context_string(MODE_BASE)?].concat(); - let mapped_point = G::map_to_curve::(input, &dst)?; - let blind_token = mapped_point * &blind; - Ok(( - Token { - data: input.to_vec(), - blind, - }, - blind_token, - )) -} - -/// Computes the second step for the multiplicative blinding version of DH-OPRF. This -/// message is sent from the server (who holds the OPRF key) to the client. -pub(crate) fn evaluate(point: G, oprf_key: &G::Scalar) -> G { - point * oprf_key -} - -/// Computes the third step for the multiplicative blinding version of DH-OPRF, in which -/// the client unblinds the server's message. -pub(crate) fn finalize( - input: &[u8], - blind: &G::Scalar, - evaluated_element: G, -) -> Result::OutputSize>, ProtocolError> { - let unblinded_element = evaluated_element * &G::scalar_invert(blind); - finalize_after_unblind::(input, unblinded_element) -} - -fn finalize_after_unblind( - input: &[u8], - unblinded_element: G, -) -> Result::OutputSize>, ProtocolError> { - let finalize_dst = [STR_VOPRF_FINALIZE, &G::get_context_string(MODE_BASE)?].concat(); - let hash_input = [ - serialize(input, 2)?, - serialize(&unblinded_element.to_arr().to_vec(), 2)?, - serialize(&finalize_dst, 2)?, - ] - .concat(); - Ok(::digest(&hash_input)) -} - -//////////////////////// -// Benchmarking Shims // -//////////////////////// - -#[cfg(feature = "bench")] -#[doc(hidden)] -#[inline] -pub fn blind_shim( - input: &[u8], - blinding_factor_rng: &mut R, -) -> Result<(Token, G), ProtocolError> { - blind::(input, blinding_factor_rng) -} - -#[cfg(feature = "bench")] -#[doc(hidden)] -#[inline] -pub fn evaluate_shim(point: G, oprf_key: &G::Scalar) -> G { - evaluate(point, oprf_key) -} - -#[cfg(feature = "bench")] -#[doc(hidden)] -#[inline] -pub fn finalize_shim( - token: &Token, - point: G, -) -> Result::OutputSize>, ProtocolError> { - finalize::(&token.data, &token.blind, point) -} - -/////////// -// Tests // -// ===== // -/////////// - -#[cfg(test)] -mod tests { - use super::*; - use crate::group::Group; - use curve25519_dalek::ristretto::RistrettoPoint; - use generic_array::{arr, GenericArray}; - use rand::rngs::OsRng; - use sha2::Sha512; - - fn prf(input: &[u8], oprf_key: &[u8; 32]) -> GenericArray::OutputSize> { - let dst = [ - STR_VOPRF, - &RistrettoPoint::get_context_string(MODE_BASE).unwrap(), - ] - .concat(); - let point = RistrettoPoint::map_to_curve::(input, &dst).unwrap(); - let scalar = - RistrettoPoint::from_scalar_slice(GenericArray::from_slice(&oprf_key[..])).unwrap(); - let res = point * scalar; - - finalize_after_unblind::(&input, res).unwrap() - } - - #[test] - fn oprf_retrieval() { - let input = b"hunter2"; - let mut rng = OsRng; - let (token, alpha) = blind::<_, RistrettoPoint, Sha512>(&input[..], &mut rng).unwrap(); - let oprf_key_bytes = arr![ - u8; 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, - ]; - let oprf_key = RistrettoPoint::from_scalar_slice(&oprf_key_bytes).unwrap(); - let beta = evaluate::(alpha, &oprf_key); - let res = - finalize::(&token.data, &token.blind, beta).unwrap(); - let res2 = prf(&input[..], &oprf_key.as_bytes()); - assert_eq!(res, res2); - } - - #[test] - fn oprf_inversion_unsalted() { - let mut rng = OsRng; - let mut input = alloc::vec![0u8; 64]; - rng.fill_bytes(&mut input); - let (token, alpha) = blind::<_, RistrettoPoint, sha2::Sha512>(&input, &mut rng).unwrap(); - let res = - finalize::(&token.data, &token.blind, alpha).unwrap(); - - let dst = [ - STR_VOPRF, - &RistrettoPoint::get_context_string(MODE_BASE).unwrap(), - ] - .concat(); - let point = RistrettoPoint::map_to_curve::(&input, &dst).unwrap(); - let res2 = finalize_after_unblind::(&input, point).unwrap(); - - assert_eq!(res, res2); - } -} diff --git a/src/serialization/tests.rs b/src/serialization/tests.rs index 950b2a1..5a444f1 100644 --- a/src/serialization/tests.rs +++ b/src/serialization/tests.rs @@ -7,7 +7,6 @@ use crate::{ ciphersuite::CipherSuite, envelope::{Envelope, InnerEnvelopeMode}, errors::*, - group::Group, key_exchange::{ traits::{FromBytes, KeyExchange, ToBytes}, tripledh::{NonceLen, TripleDH}, @@ -21,10 +20,11 @@ use alloc::vec; #[cfg(test)] use alloc::vec::Vec; -use curve25519_dalek::{ristretto::RistrettoPoint, traits::Identity}; +use curve25519_dalek::ristretto::RistrettoPoint; use generic_array::typenum::Unsigned; use proptest::{collection::vec, prelude::*}; use rand::{rngs::OsRng, RngCore}; +use voprf::group::Group; use sha2::Digest; @@ -55,21 +55,27 @@ fn random_ristretto_point() -> RistrettoPoint { } #[test] -fn client_registration_roundtrip() { +fn client_registration_roundtrip() -> Result<(), ProtocolError> { let pw = b"hunter2"; let mut rng = OsRng; - let sc = ::random_nonzero_scalar(&mut rng); - let elem = ::base_point() * sc; - // serialization order: scalar, password, group element - let bytes: Vec = [&elem.to_arr(), &sc.as_bytes()[..], &pw[..]].concat(); - let reg = ClientRegistration::::deserialize(&bytes[..]).unwrap(); - let reg_bytes = reg.serialize(); + let blind_result = + &voprf::NonVerifiableClient::::blind(pw.to_vec(), &mut rng)?; + + let bytes: Vec = [ + serialize(&blind_result.state.serialize(), 2)?, + serialize(&blind_result.message.serialize(), 2)?, + ] + .concat(); + + let reg = ClientRegistration::::deserialize(&bytes[..])?; + let reg_bytes = reg.serialize()?; assert_eq!(reg_bytes, bytes); + Ok(()) } #[test] -fn server_registration_roundtrip() { +fn server_registration_roundtrip() -> Result<(), ProtocolError> { // If we don't have envelope and client_pk, the server registration just // contains the prf key let mut rng = OsRng; @@ -82,27 +88,28 @@ fn server_registration_roundtrip() { // mock_envelope_bytes.extend_from_slice(&ciphertext); // ciphertext which is an encrypted private key mock_envelope_bytes.extend_from_slice(&[0; MAC_SIZE]); // length-MAC_SIZE hmac - let mock_client_kp = KeyPair::<::OprfGroup>::generate_random(&mut rng); + let mock_client_kp = KeyPair::<::OprfGroup>::generate_random(&mut rng)?; // serialization order: oprf_key, public key, envelope let mut bytes = Vec::::new(); bytes.extend_from_slice(&mock_client_kp.public().to_arr()); bytes.extend_from_slice(&masking_key); bytes.extend_from_slice(&mock_envelope_bytes); - let reg = ServerRegistration::::deserialize(&bytes[..]).unwrap(); - let reg_bytes = reg.serialize(); + let reg = ServerRegistration::::deserialize(&bytes[..])?; + let reg_bytes = reg.serialize()?; assert_eq!(reg_bytes, bytes); + Ok(()) } #[test] -fn registration_request_roundtrip() { +fn registration_request_roundtrip() -> Result<(), ProtocolError> { let pt = random_ristretto_point(); let pt_bytes = pt.to_arr().to_vec(); let mut input = Vec::new(); input.extend_from_slice(pt_bytes.as_slice()); - let r1 = RegistrationRequest::::deserialize(input.as_slice()).unwrap(); - let r1_bytes = r1.serialize(); + let r1 = RegistrationRequest::::deserialize(input.as_slice())?; + let r1_bytes = r1.serialize()?; assert_eq!(input, r1_bytes); // Assert that identity group element is rejected @@ -111,26 +118,30 @@ fn registration_request_roundtrip() { assert!( match RegistrationRequest::::deserialize(identity_bytes.as_slice()) { - Err(ProtocolError::IdentityGroupElementError) => true, + Err(ProtocolError::LibraryError(InternalError::OprfError( + voprf::errors::InternalError::PointError, + ))) => true, _ => false, } ); + + Ok(()) } #[test] -fn registration_response_roundtrip() { +fn registration_response_roundtrip() -> Result<(), ProtocolError> { let pt = random_ristretto_point(); let beta_bytes = pt.to_arr(); let mut rng = OsRng; - let skp = KeyPair::<::OprfGroup>::generate_random(&mut rng); + let skp = KeyPair::<::OprfGroup>::generate_random(&mut rng)?; let pubkey_bytes = skp.public().to_arr(); let mut input = Vec::new(); input.extend_from_slice(beta_bytes.as_slice()); input.extend_from_slice(&pubkey_bytes.as_slice()); - let r2 = RegistrationResponse::::deserialize(input.as_slice()).unwrap(); - let r2_bytes = r2.serialize(); + let r2 = RegistrationResponse::::deserialize(input.as_slice())?; + let r2_bytes = r2.serialize()?; assert_eq!(input, r2_bytes); // Assert that identity group element is rejected @@ -140,15 +151,19 @@ fn registration_response_roundtrip() { assert!(match RegistrationResponse::::deserialize( &[identity_bytes, pubkey_bytes.to_vec()].concat() ) { - Err(ProtocolError::IdentityGroupElementError) => true, + Err(ProtocolError::LibraryError(InternalError::OprfError( + voprf::errors::InternalError::PointError, + ))) => true, _ => false, }); + + Ok(()) } #[test] -fn registration_upload_roundtrip() { +fn registration_upload_roundtrip() -> Result<(), ProtocolError> { let mut rng = OsRng; - let skp = KeyPair::<::OprfGroup>::generate_random(&mut rng); + let skp = KeyPair::<::OprfGroup>::generate_random(&mut rng)?; let pubkey_bytes = skp.public().to_arr(); let mut key = [0u8; 32]; @@ -159,9 +174,15 @@ fn registration_upload_roundtrip() { let mut masking_key = vec![0u8; ::OutputSize::USIZE]; rng.fill_bytes(&mut masking_key); - let (envelope, _, _) = - Envelope::::seal_raw(&key, &nonce, &pubkey_bytes, InnerEnvelopeMode::Internal) - .unwrap(); + let randomized_pwd_hasher = hkdf::Hkdf::new(None, &key); + + let (envelope, _, _) = Envelope::::seal_raw( + randomized_pwd_hasher, + &nonce, + &pubkey_bytes, + InnerEnvelopeMode::Internal, + ) + .unwrap(); let envelope_bytes = envelope.serialize(); let mut input = Vec::new(); @@ -169,18 +190,20 @@ fn registration_upload_roundtrip() { input.extend_from_slice(&masking_key[..]); input.extend_from_slice(&envelope_bytes); - let r3 = RegistrationUpload::::deserialize(&input[..]).unwrap(); - let r3_bytes = r3.serialize(); + let r3 = RegistrationUpload::::deserialize(&input[..])?; + let r3_bytes = r3.serialize()?; assert_eq!(input, r3_bytes); + + Ok(()) } #[test] -fn credential_request_roundtrip() { +fn credential_request_roundtrip() -> Result<(), ProtocolError> { let mut rng = OsRng; let alpha = random_ristretto_point(); let alpha_bytes = alpha.to_arr().to_vec(); - let client_e_kp = KeyPair::<::OprfGroup>::generate_random(&mut rng); + let client_e_kp = KeyPair::<::OprfGroup>::generate_random(&mut rng)?; let mut client_nonce = vec![0u8; NonceLen::USIZE]; rng.fill_bytes(&mut client_nonce); @@ -190,8 +213,8 @@ fn credential_request_roundtrip() { input.extend_from_slice(&alpha_bytes); input.extend_from_slice(&ke1m[..]); - let l1 = CredentialRequest::::deserialize(input.as_slice()).unwrap(); - let l1_bytes = l1.serialize(); + let l1 = CredentialRequest::::deserialize(input.as_slice())?; + let l1_bytes = l1.serialize()?; assert_eq!(input, l1_bytes); // Assert that identity group element is rejected @@ -201,13 +224,17 @@ fn credential_request_roundtrip() { assert!(match CredentialRequest::::deserialize( &[identity_bytes, ke1m.to_vec()].concat() ) { - Err(ProtocolError::IdentityGroupElementError) => true, + Err(ProtocolError::LibraryError(InternalError::OprfError( + voprf::errors::InternalError::PointError, + ))) => true, _ => false, }); + + Ok(()) } #[test] -fn credential_response_roundtrip() { +fn credential_response_roundtrip() -> Result<(), ProtocolError> { let pt = random_ristretto_point(); let pt_bytes = pt.to_arr().to_vec(); @@ -220,7 +247,7 @@ fn credential_response_roundtrip() { vec![0u8; ::ElemLen::USIZE + Envelope::::len()]; rng.fill_bytes(&mut masked_response); - let server_e_kp = KeyPair::<::OprfGroup>::generate_random(&mut rng); + let server_e_kp = KeyPair::<::OprfGroup>::generate_random(&mut rng)?; let mut mac = [0u8; MAC_SIZE]; rng.fill_bytes(&mut mac); let mut server_nonce = vec![0u8; NonceLen::USIZE]; @@ -234,8 +261,8 @@ fn credential_response_roundtrip() { input.extend_from_slice(&masked_response); input.extend_from_slice(&ke2m[..]); - let l2 = CredentialResponse::::deserialize(&input).unwrap(); - let l2_bytes = l2.serialize(); + let l2 = CredentialResponse::::deserialize(&input)?; + let l2_bytes = l2.serialize()?; assert_eq!(input, l2_bytes); // Assert that identity group element is rejected @@ -251,72 +278,80 @@ fn credential_response_roundtrip() { ] .concat() ) { - Err(ProtocolError::IdentityGroupElementError) => true, + Err(ProtocolError::LibraryError(InternalError::OprfError( + voprf::errors::InternalError::PointError, + ))) => true, _ => false, }); + + Ok(()) } #[test] -fn credential_finalization_roundtrip() { +fn credential_finalization_roundtrip() -> Result<(), ProtocolError> { let mut rng = OsRng; let mut mac = [0u8; MAC_SIZE]; rng.fill_bytes(&mut mac); let input: Vec = [&mac[..]].concat(); - let l3 = CredentialFinalization::::deserialize(&input).unwrap(); - let l3_bytes = l3.serialize(); + let l3 = CredentialFinalization::::deserialize(&input)?; + let l3_bytes = l3.serialize()?; assert_eq!(input, l3_bytes); + + Ok(()) } #[test] -fn client_login_roundtrip() { +fn client_login_roundtrip() -> Result<(), ProtocolError> { let pw = b"hunter2"; let mut rng = OsRng; - let sc = ::random_nonzero_scalar(&mut rng); - let client_e_kp = KeyPair::<::OprfGroup>::generate_random(&mut rng); + let client_e_kp = KeyPair::<::OprfGroup>::generate_random(&mut rng)?; let mut client_nonce = vec![0u8; NonceLen::USIZE]; rng.fill_bytes(&mut client_nonce); let serialized_credential_request = b"serialized credential_request".to_vec(); let l1_data = [client_e_kp.private().to_arr().to_vec(), client_nonce].concat(); - // serialization order: scalar, credential_request, ke1_state, password + let blind_result = + &voprf::NonVerifiableClient::::blind(pw.to_vec(), &mut rng)?; + let bytes: Vec = [ - &sc.as_bytes()[..], - &serialize(&serialized_credential_request, 2).unwrap(), - &serialize(&l1_data, 2).unwrap(), - &pw[..], + serialize(&blind_result.state.serialize(), 2)?, + serialize(&serialized_credential_request, 2)?, + serialize(&l1_data, 2)?, ] .concat(); - let reg = ClientLogin::::deserialize(&bytes[..]).unwrap(); - let reg_bytes = reg.serialize().unwrap(); + let reg = ClientLogin::::deserialize(&bytes[..])?; + let reg_bytes = reg.serialize()?; assert_eq!(reg_bytes, bytes); + Ok(()) } #[test] -fn ke1_message_roundtrip() { +fn ke1_message_roundtrip() -> Result<(), ProtocolError> { let mut rng = OsRng; - let client_e_kp = KeyPair::<::OprfGroup>::generate_random(&mut rng); + let client_e_kp = KeyPair::<::OprfGroup>::generate_random(&mut rng)?; let mut client_nonce = vec![0u8; NonceLen::USIZE]; rng.fill_bytes(&mut client_nonce); let ke1m: Vec = [&client_nonce[..], &client_e_kp.public()].concat(); let reg = >::KE1Message::from_bytes::< Default, - >(&ke1m[..]) - .unwrap(); + >(&ke1m[..])?; let reg_bytes = reg.to_bytes(); assert_eq!(reg_bytes, ke1m); + + Ok(()) } #[test] -fn ke2_message_roundtrip() { +fn ke2_message_roundtrip() -> Result<(), ProtocolError> { let mut rng = OsRng; - let server_e_kp = KeyPair::<::OprfGroup>::generate_random(&mut rng); + let server_e_kp = KeyPair::<::OprfGroup>::generate_random(&mut rng)?; let mut mac = [0u8; MAC_SIZE]; rng.fill_bytes(&mut mac); let mut server_nonce = vec![0u8; NonceLen::USIZE]; @@ -326,14 +361,15 @@ fn ke2_message_roundtrip() { let reg = >::KE2Message::from_bytes::< Default, - >(&ke2m[..]) - .unwrap(); + >(&ke2m[..])?; let reg_bytes = reg.to_bytes(); assert_eq!(reg_bytes, ke2m); + + Ok(()) } #[test] -fn ke3_message_roundtrip() { +fn ke3_message_roundtrip() -> Result<(), ProtocolError> { let mut rng = OsRng; let mut mac = [0u8; MAC_SIZE]; rng.fill_bytes(&mut mac); @@ -342,10 +378,11 @@ fn ke3_message_roundtrip() { let reg = >::KE3Message::from_bytes::< Default, - >(&ke3m[..]) - .unwrap(); + >(&ke3m[..])?; let reg_bytes = reg.to_bytes(); assert_eq!(reg_bytes, ke3m); + + Ok(()) } proptest! { diff --git a/src/tests/full_test.rs b/src/tests/full_test.rs index a42c8bc..9989bb7 100644 --- a/src/tests/full_test.rs +++ b/src/tests/full_test.rs @@ -12,7 +12,6 @@ use crate::{ use alloc::string::ToString; use alloc::vec; use alloc::vec::Vec; -use core::slice::from_raw_parts; use curve25519_dalek::{ristretto::RistrettoPoint, traits::Identity}; use rand::rngs::OsRng; use serde_json::Value; @@ -71,38 +70,38 @@ static STR_CREDENTIAL_IDENTIFIER: &str = "credential_identifier"; // To regenerate, run: cargo test -- --nocapture generate_test_vectors static TEST_VECTOR: &str = r#" { - "client_s_pk": "520364faf278ac5b407465c8a802ac57bf61ec4bbf3e1c508a39e1667c7cf904", - "client_s_sk": "e30c1ca67d865b65333bd5567a3ea54c6df36d1641953ca4470a886e7117bf06", - "client_e_pk": "1c0344d0e057a1f6651d80b00e188aa35efb4844173c531d186729f81411bc24", - "client_e_sk": "56b7ff7a43c7d6f75d881606f733c12a53af70b35d8c5cd982a8e94a816dfa0d", - "server_s_pk": "bcf39fe745ca2f945c7d75b542bfe4217fe6f481c49b7ce410f4a13079cd0d2b", - "server_s_sk": "cd4a4916bf0226c7fa1f18c97cbf5e0c03d1ae8b74d1155270202d848f4d640e", - "server_e_pk": "e02644fdcb782c335c0c1f801b70f7a873bc2613e3ed2a6ad05103a85dd2ec01", - "server_e_sk": "b9b14238988b6ea6e6aa6e8981a65c45e28ba01ddedf9437aff62a7071a64d09", - "fake_sk": "55ed2b029216bd0953db96c614c737001fc9c79ad4b2c896f2aeebe052e9770a", + "client_s_pk": "181bbea01a5e444390c4b335f8bcb9a846a1c60042669ce6d731af4587960c06", + "client_s_sk": "2f21529b9fb27c8c12770b765dc36750c4a51c5ccaf2f83d0182504a85a22c0b", + "client_e_pk": "58a16b672e100b18069d0716715a9a8d9a643954bb24c0887e46d542eab9e417", + "client_e_sk": "c1a4db9d650ce1700e05fbd472d30c13e0a4c6926b114e7ca11e2e9f397c5005", + "server_s_pk": "dc8c66b6dcf4731836a0cdb336985c77a6321ffb75db6bb1aef20974c141dd3c", + "server_s_sk": "410ef173f972994eeabb2cb39fd5db907e39a1abd6b36c9f514ab903d9d16305", + "server_e_pk": "68630597c4593cb8158f398ab6ff956a1d87232be4300be1f96a6860663d9461", + "server_e_sk": "155452c6b08b889b31d5a810925136adfb2d363eee4ccaed0ef15b594fb88b04", + "fake_sk": "b009e67b83c418f0ae271b8790d6c5e06ea3874fa5a9e66752b5ebdbec953b04", "credential_identifier": "637265644964656e746966696572", "id_u": "696455", "id_s": "696453", "password": "70617373776f7264", - "blinding_factor": "1d906fc19848da043a39b51b22620802d7df4f11abf994a269d7ada2cc7edd04", - "oprf_seed": "aee2ac3f7c043526f1cbda952f34840482a63afbf959e32f9172343d8b9fe076cd0a87a017f16e6511c37f171cea277d906d6d1dc31a3de4c0cc99c46e76c94c", - "masking_nonce": "62f49aa47625d01466051799e1ba6f99e1916c15fa1cc651867d1bbc55c6c5255269d21a5854a9756ffd8fc33de366da1d715c5de16609f7aa55e7e8ce01c8f5", - "envelope_nonce": "3cf5a62a4ce8ffe7e1da0ac2066597f1d7cd7a91285e8a108943f6beea91b1aa", - "client_nonce": "de58ba7427030a4792107bf9f4a9334599f4e437d97a73ae1db4f1d3f9362879", - "server_nonce": "50d85de8a9a6fb656981a17811db0a381dd98aa37514a90d8abf990b56829daf", + "blinding_factor": "544ce97b02dff0201282a44cf73171a62a76e2a113d40dce8950f31bf4339403", + "oprf_seed": "f929fa161a065bec163bea6dbab6d6eccd960666951fc7fd3da7cf2b6baf20a2763598aba89a4e5bcaa57096c66cfded26d683e07ab1a3b37a7c82706dfaee81", + "masking_nonce": "4c0099b7067c7c243ed804def0fd490babd577abcd7b05a1f24a05d2d1cc344e079a75936ed36b89a3056661a2dc981a6628edde6a86da2714cce71659d84f8d", + "envelope_nonce": "9888ba54ffc1e1be5deb23a2efa5432f318f9a17d681d1273e909ca3bf1b2fea", + "client_nonce": "652a39daf155cc9b5a005b67951f19c2ccdf4667cf7bcd39f941a87565ed4c29", + "server_nonce": "5e2f19069ea9791d6b346b676d8d8aaf45536148ca0357a595f330c7aed107d2", "context": "636f6e74657874", - "registration_request": "7ea276a79194704a5056d17c3e2e1751d98b1d1fc5d1d78d11004e9a44c7c52c", - "registration_response": "389fcdd9b327e1ad9eaa30b8ea9db665b49a52b96583c4952f087d21aa47e15abcf39fe745ca2f945c7d75b542bfe4217fe6f481c49b7ce410f4a13079cd0d2b", - "registration_upload": "7a1daeb91dc6af4e8685287922020dda9e4445ddb107aa76f30ef3defec982046c81af9eba7baef5e5dc3aca419a62b207542315edc732834b18fa394cbfa9c210caed864e36bb19cdd7324553b7e35d1a7384f4262a15e38049f910b2ca6baae30c1ca67d865b65333bd5567a3ea54c6df36d1641953ca4470a886e7117bf06383ff4ec0a1f237f70e51cae0f859e7476917cb8d28cda3aeccd970a205a71e20d3e592eaadabc2216d0ed14e494dbf733984e607b6d5f8b68f9b2321ca01b30", - "credential_request": "7ea276a79194704a5056d17c3e2e1751d98b1d1fc5d1d78d11004e9a44c7c52cde58ba7427030a4792107bf9f4a9334599f4e437d97a73ae1db4f1d3f93628791c0344d0e057a1f6651d80b00e188aa35efb4844173c531d186729f81411bc24", - "credential_response": "389fcdd9b327e1ad9eaa30b8ea9db665b49a52b96583c4952f087d21aa47e15a62f49aa47625d01466051799e1ba6f99e1916c15fa1cc651867d1bbc55c6c5257e7d8f435b95456607ec8767840b5c192c1132d78b38cf72511ae4605a8539f570452bbfd864a34a71b635f77c9d7dee227b7906697ab0dea2dda3043672c523a0a2c651e9bbef212e85439c8f227c564470cdb6d9a74ac8b7d2f6f3eba59b43489082c93f17f7de3c2840848e2c5db07bf4a8c5011f6ff01895bcb0a3b10ca0b9b14238988b6ea6e6aa6e8981a65c45e28ba01ddedf9437aff62a7071a64d098eaf6a09f9dd3db7e9ea307c4df21852c24fdd27bc335815afdfa03237facb0834b5d393d8b5d61526d8a61077a345a864020a10be67f46a6a6a02fd86ea05a2f25e262d9fe23bfe9489d5bc585dd4a018fa5dea295e51a7824526b6360cc03a", - "credential_finalization": "eefb1823d84629dc0d9e149dbb07fb115e473d0d3a004e0b2dd20fdb168a7d33423347c07960d272f1fa670f45ca8e6571d3b396661383da8f07380d89488116", - "client_registration_state": "7ea276a79194704a5056d17c3e2e1751d98b1d1fc5d1d78d11004e9a44c7c52c1d906fc19848da043a39b51b22620802d7df4f11abf994a269d7ada2cc7edd0470617373776f7264", - "client_login_state": "1d906fc19848da043a39b51b22620802d7df4f11abf994a269d7ada2cc7edd0400607ea276a79194704a5056d17c3e2e1751d98b1d1fc5d1d78d11004e9a44c7c52cde58ba7427030a4792107bf9f4a9334599f4e437d97a73ae1db4f1d3f93628791c0344d0e057a1f6651d80b00e188aa35efb4844173c531d186729f81411bc24004056b7ff7a43c7d6f75d881606f733c12a53af70b35d8c5cd982a8e94a816dfa0dde58ba7427030a4792107bf9f4a9334599f4e437d97a73ae1db4f1d3f936287970617373776f7264", - "server_login_state": "b54d627533bf9545c5450b4c518e4cb298b118b34383db02ceddd2178683e7201d1e3ffd5eff03bc224a75760cecd10ce0a6514ba4f638fbfc21f968398d159f5f5a78b6309646b6e9ecd5856aa109abdc53d720791f198edde539d19ee7deeb5438f6a1156476d8e11404b344c2a97db92902eb2d77d723defeb3a7d3be1c5e954c3ba56dff507f6d0de3e9c02b1fe3d8935b32fbaa4be2cf5c82225b9e47c931bb51eb04a2a5345914c33a52c3f7d5184b631fe1a5efd4633f433e408589fa", - "password_file": "7a1daeb91dc6af4e8685287922020dda9e4445ddb107aa76f30ef3defec982046c81af9eba7baef5e5dc3aca419a62b207542315edc732834b18fa394cbfa9c210caed864e36bb19cdd7324553b7e35d1a7384f4262a15e38049f910b2ca6baae30c1ca67d865b65333bd5567a3ea54c6df36d1641953ca4470a886e7117bf06383ff4ec0a1f237f70e51cae0f859e7476917cb8d28cda3aeccd970a205a71e20d3e592eaadabc2216d0ed14e494dbf733984e607b6d5f8b68f9b2321ca01b30", - "export_key": "9eaea2ca345b9fac4c7924abe5783d23ac7452d61ebb026d40ecab402178c8669b135783e5dcd148554e9d861fa4af809f1e41c1ccca929000cdb3a78a2dc4fb", - "session_key": "954c3ba56dff507f6d0de3e9c02b1fe3d8935b32fbaa4be2cf5c82225b9e47c931bb51eb04a2a5345914c33a52c3f7d5184b631fe1a5efd4633f433e408589fa" + "registration_request": "f05048bb39f3f5a3a414f50254c425b36f842162a630bf73456df453351cb33d", + "registration_response": "2c6f5ba3de9af2719529e9a993097e8c0ecd5110a24471414e4225950189cc46dc8c66b6dcf4731836a0cdb336985c77a6321ffb75db6bb1aef20974c141dd3c", + "registration_upload": "08a51d9973140af4f911f235d4910e9536503157bfaffefaeaa11f69d723cc54d35d9ae50d6a0a7ab38614e571a81821cfbfec36ed9fd46e397e173252d02ff623287035e190153e9fb88509da1c225765bb200ed59249cbfd6201656d1672db2f21529b9fb27c8c12770b765dc36750c4a51c5ccaf2f83d0182504a85a22c0b19e07582aea6c5e782b15ff18f6188203f54ea62dfb1efb77d641f030b86062c9f0d1bc3c39b7f824fe81df456c702ea4fa084eba803fea7e5a80d2284c2ff15", + "credential_request": "f05048bb39f3f5a3a414f50254c425b36f842162a630bf73456df453351cb33d652a39daf155cc9b5a005b67951f19c2ccdf4667cf7bcd39f941a87565ed4c2958a16b672e100b18069d0716715a9a8d9a643954bb24c0887e46d542eab9e417", + "credential_response": "2c6f5ba3de9af2719529e9a993097e8c0ecd5110a24471414e4225950189cc464c0099b7067c7c243ed804def0fd490babd577abcd7b05a1f24a05d2d1cc344e4100aea1aa461bed485d0e441c275d0d04388e1b6b739379cd9ebabe6a53ca025375487ea73cc17ea884fb3fd91d05a8970628459ab7996e9e20aacdc613a669a687c95479814b866c43cde2ca225f43a44035ba921b5986877589013acd8eba3f4e00bd4c5e83756651c5faa0319487e4b80bcedfef82b9e46c3cace5b4aa8e155452c6b08b889b31d5a810925136adfb2d363eee4ccaed0ef15b594fb88b04605c82fb3bb14125d8274619e5ceef90151b6309610aa35c2c0a004337c44b50162d27726ed40aca064e88cff1413d7098f81b16567919ca8d9c425b8b3445684e2ad7e7d8d14475c2ca1ace56a5949199fb836f9f343d46994098955bb9f129", + "credential_finalization": "e13284ada3e78eed48047934115ce7e6c2cdff0c3012e9ba2d423759c4000ddf11ecd186dd7f0740ee3413ff0d253e2437eced56f3717e45c071b170d12db1dd", + "client_registration_state": "0028544ce97b02dff0201282a44cf73171a62a76e2a113d40dce8950f31bf433940370617373776f72640020f05048bb39f3f5a3a414f50254c425b36f842162a630bf73456df453351cb33d", + "client_login_state": "0028544ce97b02dff0201282a44cf73171a62a76e2a113d40dce8950f31bf433940370617373776f72640060f05048bb39f3f5a3a414f50254c425b36f842162a630bf73456df453351cb33d652a39daf155cc9b5a005b67951f19c2ccdf4667cf7bcd39f941a87565ed4c2958a16b672e100b18069d0716715a9a8d9a643954bb24c0887e46d542eab9e4170040c1a4db9d650ce1700e05fbd472d30c13e0a4c6926b114e7ca11e2e9f397c5005652a39daf155cc9b5a005b67951f19c2ccdf4667cf7bcd39f941a87565ed4c29", + "server_login_state": "2a009e5881454a2b42fb8c039762f78828c5b4ba7008d2e57b16ffdc937ee846b56461f9cda751b2a1c2b03793d72d5ca8c482adf8009880779323e64e12eb1d4c0cf45ec665be918cb9d655f9eca974494f0f4c0f6e714c6ddcca37b547c122cf7419984e123fa4c7981212e5171b01bfd6f8ac88e7964c8da4a88b5df4f2c85da5318465cef76fbddd389ff36be66c693cfc6feecbcf43bf16a22c97de8430e824b2812449934d13fb666b24de78a007f1fc06064304b0abfae3fc5caba7f6", + "password_file": "08a51d9973140af4f911f235d4910e9536503157bfaffefaeaa11f69d723cc54d35d9ae50d6a0a7ab38614e571a81821cfbfec36ed9fd46e397e173252d02ff623287035e190153e9fb88509da1c225765bb200ed59249cbfd6201656d1672db2f21529b9fb27c8c12770b765dc36750c4a51c5ccaf2f83d0182504a85a22c0b19e07582aea6c5e782b15ff18f6188203f54ea62dfb1efb77d641f030b86062c9f0d1bc3c39b7f824fe81df456c702ea4fa084eba803fea7e5a80d2284c2ff15", + "export_key": "ea8d1f871a3c8ad5d2a7a2d647e020105a33f8b8534055c56ab4bae2b8467d22806968159f918d9c31098602790fcad3e5969f1d8ff0b90b48c26b4132877ed4", + "session_key": "5da5318465cef76fbddd389ff36be66c693cfc6feecbcf43bf16a22c97de8430e824b2812449934d13fb666b24de78a007f1fc06064304b0abfae3fc5caba7f6" } "#; @@ -272,19 +271,20 @@ fn stringify_test_vectors(p: &TestVectorParameters) -> alloc::string::String { s } -fn generate_parameters() -> TestVectorParameters { - use crate::{group::Group, key_exchange::tripledh::NonceLen, keypair::KeyPair}; +fn generate_parameters() -> Result { + use crate::{key_exchange::tripledh::NonceLen, keypair::KeyPair}; use generic_array::typenum::Unsigned; use rand::RngCore; + use voprf::group::Group; let mut rng = OsRng; // Inputs - let server_s_kp = KeyPair::::generate_random(&mut rng); - let server_e_kp = KeyPair::::generate_random(&mut rng); - let client_s_kp = KeyPair::::generate_random(&mut rng); - let client_e_kp = KeyPair::::generate_random(&mut rng); - let fake_kp = KeyPair::::generate_random(&mut rng); + let server_s_kp = KeyPair::::generate_random(&mut rng)?; + let server_e_kp = KeyPair::::generate_random(&mut rng)?; + let client_s_kp = KeyPair::::generate_random(&mut rng)?; + let client_e_kp = KeyPair::::generate_random(&mut rng)?; + let fake_kp = KeyPair::::generate_random(&mut rng)?; let credential_identifier = b"credIdentifier"; let id_u = b"idU"; let id_s = b"idS"; @@ -313,18 +313,19 @@ fn generate_parameters() -> TestVectorParameters { let mut blinding_factor_registration_rng = CycleRng::new(blinding_factor_bytes.to_vec()); let client_registration_start_result = ClientRegistration::::start(&mut blinding_factor_registration_rng, password).unwrap(); - let blinding_factor_bytes_returned = - CS::OprfGroup::scalar_as_bytes(client_registration_start_result.state.token.blind); + let blinding_factor_bytes_returned = CS::OprfGroup::scalar_as_bytes( + client_registration_start_result + .state + .oprf_client + .get_blind(), + ); assert_eq!( hex::encode(&blinding_factor_bytes), hex::encode(&blinding_factor_bytes_returned) ); - let registration_request_bytes = client_registration_start_result - .message - .serialize() - .to_vec(); - let client_registration_state = client_registration_start_result.state.serialize().to_vec(); + let registration_request_bytes = client_registration_start_result.message.serialize()?; + let client_registration_state = client_registration_start_result.state.serialize()?; let server_registration_start_result = ServerRegistration::::start( &server_setup, @@ -332,10 +333,7 @@ fn generate_parameters() -> TestVectorParameters { &credential_identifier[..], ) .unwrap(); - let registration_response_bytes = server_registration_start_result - .message - .serialize() - .to_vec(); + let registration_response_bytes = server_registration_start_result.message.serialize()?; let mut client_s_sk_and_nonce: Vec = Vec::new(); client_s_sk_and_nonce.extend_from_slice(&client_s_kp.private().to_arr()); @@ -356,13 +354,10 @@ fn generate_parameters() -> TestVectorParameters { ), ) .unwrap(); - let registration_upload_bytes = client_registration_finish_result - .message - .serialize() - .to_vec(); + let registration_upload_bytes = client_registration_finish_result.message.serialize()?; let password_file = ServerRegistration::finish(client_registration_finish_result.message); - let password_file_bytes = password_file.serialize(); + let password_file_bytes = password_file.serialize()?; let mut client_login_start: Vec = Vec::new(); client_login_start.extend_from_slice(&blinding_factor_bytes); @@ -372,7 +367,7 @@ fn generate_parameters() -> TestVectorParameters { let mut client_login_start_rng = CycleRng::new(client_login_start); let client_login_start_result = ClientLogin::::start(&mut client_login_start_rng, password).unwrap(); - let credential_request_bytes = client_login_start_result.message.serialize().to_vec(); + let credential_request_bytes = client_login_start_result.message.serialize()?; let client_login_state = client_login_start_result .state .serialize() @@ -399,8 +394,8 @@ fn generate_parameters() -> TestVectorParameters { ), ) .unwrap(); - let credential_response_bytes = server_login_start_result.message.serialize().to_vec(); - let server_login_state = server_login_start_result.state.serialize().to_vec(); + let credential_response_bytes = server_login_start_result.message.serialize()?; + let server_login_state = server_login_start_result.state.serialize()?; let client_login_finish_result = client_login_start_result .state @@ -416,9 +411,9 @@ fn generate_parameters() -> TestVectorParameters { ), ) .unwrap(); - let credential_finalization_bytes = client_login_finish_result.message.serialize(); + let credential_finalization_bytes = client_login_finish_result.message.serialize()?; - TestVectorParameters { + Ok(TestVectorParameters { client_s_pk: client_s_kp.public().to_arr().to_vec(), client_s_sk: client_s_kp.private().to_arr().to_vec(), client_e_pk: client_e_kp.public().to_arr().to_vec(), @@ -451,13 +446,14 @@ fn generate_parameters() -> TestVectorParameters { server_login_state, session_key: client_login_finish_result.session_key, export_key: client_registration_finish_result.export_key.to_vec(), - } + }) } #[test] -fn generate_test_vectors() { - let parameters = generate_parameters::(); +fn generate_test_vectors() -> Result<(), ProtocolError> { + let parameters = generate_parameters::()?; println!("{}", stringify_test_vectors(¶meters)); + Ok(()) } #[test] @@ -468,11 +464,11 @@ fn test_registration_request() -> Result<(), ProtocolError> { ClientRegistration::::start(&mut rng, ¶meters.password)?; assert_eq!( hex::encode(¶meters.registration_request), - hex::encode(client_registration_start_result.message.serialize()) + hex::encode(client_registration_start_result.message.serialize()?) ); assert_eq!( hex::encode(¶meters.client_registration_state), - hex::encode(client_registration_start_result.state.serialize()) + hex::encode(client_registration_start_result.state.serialize()?) ); Ok(()) } @@ -490,13 +486,13 @@ fn test_serialization() -> Result<(), ProtocolError> { serde_json::to_string(&client_registration_start_result.message).unwrap(); assert_eq!( registration_request_json, - r#""fqJ2p5GUcEpQVtF8Pi4XUdmLHR/F0deNEQBOmkTHxSw=""# + r#""8FBIuznz9aOkFPUCVMQls2+EIWKmML9zRW30UzUcsz0=""# ); let registration_request: RegistrationRequest = serde_json::from_str(®istration_request_json).unwrap(); assert_eq!( - hex::encode(client_registration_start_result.message.serialize()), - hex::encode(registration_request.serialize()), + hex::encode(client_registration_start_result.message.serialize()?), + hex::encode(registration_request.serialize()?), ); } { @@ -507,15 +503,17 @@ fn test_serialization() -> Result<(), ProtocolError> { let registration_request: RegistrationRequest = bincode::deserialize(®istration_request_bin).unwrap(); assert_eq!( - hex::encode(client_registration_start_result.message.serialize()), - hex::encode(registration_request.serialize()), + hex::encode(client_registration_start_result.message.serialize()?), + hex::encode(registration_request.serialize()?), ); } Ok(()) } #[test] fn test_registration_response() -> Result<(), ProtocolError> { - let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); + let parameters = populate_test_vectors( + &serde_json::from_str(TEST_VECTOR).map_err(|_| ProtocolError::SerializationError)?, + ); let server_setup = ServerSetup::::deserialize( &[ @@ -534,14 +532,16 @@ fn test_registration_response() -> Result<(), ProtocolError> { )?; assert_eq!( hex::encode(parameters.registration_response), - hex::encode(server_registration_start_result.message.serialize()) + hex::encode(server_registration_start_result.message.serialize()?) ); Ok(()) } #[test] fn test_registration_upload() -> Result<(), ProtocolError> { - let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); + let parameters = populate_test_vectors( + &serde_json::from_str(TEST_VECTOR).map_err(|_| ProtocolError::SerializationError)?, + ); let client_s_sk_and_nonce: Vec = [parameters.client_s_sk, parameters.envelope_nonce].concat(); @@ -563,7 +563,7 @@ fn test_registration_upload() -> Result<(), ProtocolError> { assert_eq!( hex::encode(parameters.registration_upload), - hex::encode(result.message.serialize()) + hex::encode(result.message.serialize()?) ); assert_eq!( hex::encode(parameters.export_key), @@ -585,7 +585,7 @@ fn test_password_file() -> Result<(), ProtocolError> { assert_eq!( hex::encode(parameters.password_file), - hex::encode(password_file.serialize()) + hex::encode(password_file.serialize()?) ); Ok(()) } @@ -607,7 +607,7 @@ fn test_credential_request() -> Result<(), ProtocolError> { )?; assert_eq!( hex::encode(¶meters.credential_request), - hex::encode(client_login_start_result.message.serialize()) + hex::encode(client_login_start_result.message.serialize()?) ); assert_eq!( hex::encode(¶meters.client_login_state), @@ -654,11 +654,11 @@ fn test_credential_response() -> Result<(), ProtocolError> { )?; assert_eq!( hex::encode(¶meters.credential_response), - hex::encode(server_login_start_result.message.serialize()) + hex::encode(server_login_start_result.message.serialize()?) ); assert_eq!( hex::encode(¶meters.server_login_state), - hex::encode(server_login_start_result.state.serialize()) + hex::encode(server_login_start_result.state.serialize()?) ); Ok(()) } @@ -694,7 +694,7 @@ fn test_credential_finalization() -> Result<(), ProtocolError> { ); assert_eq!( hex::encode(¶meters.credential_finalization), - hex::encode(client_login_finish_result.message.serialize()) + hex::encode(client_login_finish_result.message.serialize()?) ); assert_eq!( hex::encode(¶meters.export_key), @@ -730,7 +730,7 @@ fn test_complete_flow( let credential_identifier = b"credentialIdentifier"; let mut client_rng = OsRng; let mut server_rng = OsRng; - let server_setup = ServerSetup::::new(&mut server_rng); + let server_setup = ServerSetup::::new(&mut server_rng)?; let client_registration_start_result = ClientRegistration::::start( &mut client_rng, @@ -810,11 +810,8 @@ fn test_zeroize_client_registration_start() -> Result<(), ProtocolError> { )?; let mut state = client_registration_start_result.state; - let ptrs = state.as_byte_ptrs(); - state.zeroize(); - - for (ptr, len) in ptrs { - let bytes = unsafe { from_raw_parts(ptr, len) }; + Zeroize::zeroize(&mut state); + for bytes in state.to_vec() { assert!(bytes.iter().all(|&x| x == 0)); } @@ -825,7 +822,7 @@ fn test_zeroize_client_registration_start() -> Result<(), ProtocolError> { fn test_zeroize_client_registration_finish() -> Result<(), ProtocolError> { let mut client_rng = OsRng; let mut server_rng = OsRng; - let server_setup = ServerSetup::::new(&mut server_rng); + let server_setup = ServerSetup::::new(&mut server_rng)?; let client_registration_start_result = ClientRegistration::::start( &mut client_rng, @@ -844,11 +841,8 @@ fn test_zeroize_client_registration_finish() -> Result<(), ProtocolError> { )?; let mut state = client_registration_finish_result.state; - let ptrs = state.as_byte_ptrs(); - state.zeroize(); - - for (ptr, len) in ptrs { - let bytes = unsafe { from_raw_parts(ptr, len) }; + Zeroize::zeroize(&mut state); + for bytes in state.to_vec() { assert!(bytes.iter().all(|&x| x == 0)); } @@ -859,7 +853,7 @@ fn test_zeroize_client_registration_finish() -> Result<(), ProtocolError> { fn test_zeroize_server_registration_finish() -> Result<(), ProtocolError> { let mut client_rng = OsRng; let mut server_rng = OsRng; - let server_setup = ServerSetup::::new(&mut server_rng); + let server_setup = ServerSetup::::new(&mut server_rng)?; let client_registration_start_result = ClientRegistration::::start( &mut client_rng, @@ -879,11 +873,8 @@ fn test_zeroize_server_registration_finish() -> Result<(), ProtocolError> { let p_file = ServerRegistration::finish(client_registration_finish_result.message); let mut state = p_file; - let ptrs = state.as_byte_ptrs(); - state.zeroize(); - - for (ptr, len) in ptrs { - let bytes = unsafe { from_raw_parts(ptr, len) }; + Zeroize::zeroize(&mut state); + for bytes in state.serialize() { assert!(bytes.iter().all(|&x| x == 0)); } @@ -899,11 +890,8 @@ fn test_zeroize_client_login_start() -> Result<(), ProtocolError> { )?; let mut state = client_login_start_result.state; - let ptrs = state.as_byte_ptrs(); - state.zeroize(); - - for (ptr, len) in ptrs { - let bytes = unsafe { from_raw_parts(ptr, len) }; + Zeroize::zeroize(&mut state); + for bytes in state.to_vec() { assert!(bytes.iter().all(|&x| x == 0)); } @@ -914,7 +902,7 @@ fn test_zeroize_client_login_start() -> Result<(), ProtocolError> { fn test_zeroize_server_login_start() -> Result<(), ProtocolError> { let mut client_rng = OsRng; let mut server_rng = OsRng; - let server_setup = ServerSetup::::new(&mut server_rng); + let server_setup = ServerSetup::::new(&mut server_rng)?; let client_registration_start_result = ClientRegistration::::start( &mut client_rng, @@ -946,11 +934,8 @@ fn test_zeroize_server_login_start() -> Result<(), ProtocolError> { )?; let mut state = server_login_start_result.state; - let ptrs = state.as_byte_ptrs(); - state.zeroize(); - - for (ptr, len) in ptrs { - let bytes = unsafe { from_raw_parts(ptr, len) }; + Zeroize::zeroize(&mut state); + for bytes in state.serialize() { assert!(bytes.iter().all(|&x| x == 0)); } @@ -961,7 +946,7 @@ fn test_zeroize_server_login_start() -> Result<(), ProtocolError> { fn test_zeroize_client_login_finish() -> Result<(), ProtocolError> { let mut client_rng = OsRng; let mut server_rng = OsRng; - let server_setup = ServerSetup::::new(&mut server_rng); + let server_setup = ServerSetup::::new(&mut server_rng)?; let client_registration_start_result = ClientRegistration::::start( &mut client_rng, @@ -997,11 +982,8 @@ fn test_zeroize_client_login_finish() -> Result<(), ProtocolError> { )?; let mut state = client_login_finish_result.state; - let ptrs = state.as_byte_ptrs(); - state.zeroize(); - - for (ptr, len) in ptrs { - let bytes = unsafe { from_raw_parts(ptr, len) }; + Zeroize::zeroize(&mut state); + for bytes in state.to_vec() { assert!(bytes.iter().all(|&x| x == 0)); } @@ -1012,7 +994,7 @@ fn test_zeroize_client_login_finish() -> Result<(), ProtocolError> { fn test_zeroize_server_login_finish() -> Result<(), ProtocolError> { let mut client_rng = OsRng; let mut server_rng = OsRng; - let server_setup = ServerSetup::::new(&mut server_rng); + let server_setup = ServerSetup::::new(&mut server_rng)?; let client_registration_start_result = ClientRegistration::::start( &mut client_rng, @@ -1051,11 +1033,8 @@ fn test_zeroize_server_login_finish() -> Result<(), ProtocolError> { .finish(client_login_finish_result.message)?; let mut state = server_login_finish_result.state; - let ptrs = state.as_byte_ptrs(); - state.zeroize(); - - for (ptr, len) in ptrs { - let bytes = unsafe { from_raw_parts(ptr, len) }; + Zeroize::zeroize(&mut state); + for bytes in state.serialize() { assert!(bytes.iter().all(|&x| x == 0)); } @@ -1076,7 +1055,23 @@ fn test_scalar_always_nonzero() -> Result<(), ProtocolError> { RistrettoPoint::identity(), client_registration_start_result .message - .get_alpha_for_testing() + .get_blinded_element_for_testing() + .value(), + ); + + // Start out with a bunch of zeros to force resampling of scalar + let mut client_login_rng = CycleRng::new([vec![0u8; 128], vec![1u8; 128]].concat()); + let client_login_start_result = ClientLogin::::start( + &mut client_login_rng, + STR_PASSWORD.as_bytes(), + )?; + + assert_ne!( + RistrettoPoint::identity(), + client_login_start_result + .message + .get_blinded_element_for_testing() + .value(), ); Ok(()) @@ -1088,12 +1083,13 @@ fn test_reflected_value_error_registration() -> Result<(), ProtocolError> { let password = b"password"; let mut client_rng = OsRng; let mut server_rng = OsRng; - let server_setup = ServerSetup::::new(&mut server_rng); + let server_setup = ServerSetup::::new(&mut server_rng)?; let client_registration_start_result = ClientRegistration::::start(&mut client_rng, password)?; let alpha = client_registration_start_result .message - .get_alpha_for_testing(); + .get_blinded_element_for_testing() + .value(); let server_registration_start_result = ServerRegistration::::start( &server_setup, @@ -1103,7 +1099,7 @@ fn test_reflected_value_error_registration() -> Result<(), ProtocolError> { let reflected_registration_response = server_registration_start_result .message - .set_beta_for_testing(alpha); + .set_evaluation_element_for_testing(alpha); let client_registration_finish_result = client_registration_start_result.state.finish( &mut client_rng, @@ -1125,7 +1121,7 @@ fn test_reflected_value_error_login() -> Result<(), ProtocolError> { let password = b"password"; let mut client_rng = OsRng; let mut server_rng = OsRng; - let server_setup = ServerSetup::::new(&mut server_rng); + let server_setup = ServerSetup::::new(&mut server_rng)?; let client_registration_start_result = ClientRegistration::::start(&mut client_rng, password)?; let server_registration_start_result = @@ -1142,7 +1138,10 @@ fn test_reflected_value_error_login() -> Result<(), ProtocolError> { let p_file = ServerRegistration::finish(client_registration_finish_result.message); let client_login_start_result = ClientLogin::::start(&mut client_rng, password)?; - let alpha = client_login_start_result.message.get_alpha_for_testing(); + let alpha = client_login_start_result + .message + .get_blinded_element_for_testing() + .value(); let server_login_start_result = ServerLogin::::start( &mut server_rng, &server_setup, @@ -1154,7 +1153,7 @@ fn test_reflected_value_error_login() -> Result<(), ProtocolError> { let reflected_credential_response = server_login_start_result .message - .set_beta_for_testing(alpha); + .set_evaluation_element_for_testing(alpha); let client_login_result = client_login_start_result.state.finish( reflected_credential_response, diff --git a/src/tests/mod.rs b/src/tests/mod.rs index f99deaa..da2aaa0 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -8,4 +8,3 @@ pub mod mock_rng; mod opaque_vectors; mod parser; mod test_opaque_vectors; -mod voprf_test_vectors; diff --git a/src/tests/opaque_vectors.rs b/src/tests/opaque_vectors.rs index 1acbb2c..cc2341b 100644 --- a/src/tests/opaque_vectors.rs +++ b/src/tests/opaque_vectors.rs @@ -66,25 +66,25 @@ bf8d6503 #### Intermediate Values ~~~ -client_public_key: a2ba5f0b3c7d340f33852c354ba36983230abbcb78a2ae2219 -6f6d6272faba00 -auth_key: bfe5696d23897e60acd8d8df3b458983bebf8c7322982b0de6f56311367 -887bef0bbaecc2c4a6de3528851030a0d5256288b902e37798e129988814faa5aad33 -randomized_pwd: 8bdf54a171e0c0bcbad3a821e0954fe5e758a3c1c07df697e5532 -265cc06687b295ca98e1f635a7f31ac78af483a9d272e8ee763dd3b567f9a5dc9f4e7 -05a36b +client_public_key: 3ea81ee30a44d65ca6db8f42a9c125277898ead7fe1604da98 +70ad4542044a56 +auth_key: 7816871e2ab2d039dc0d8a07ce94081dfd975de003ea1b7ff2b120cc74c +f18c32e11d2ed730fae9040f87be5c11cfc90cdf3393557c47065d7127ece8ca2b09c +randomized_pwd: c9c8c47dece13aed16b80ca049cedc86e984177d98b549ba40390 +eb77981f954537e743ca3ec854fce472981714aadb3a280f4b2c15040d97653c64b7e +5c265e envelope: 71b8f14b7a1059cdadc414c409064a22cf9e970b0ffc6f1fc6fdd539c46 -76775749f7c447c49605d117f4cea49637ae644f2fee8a9c5eeb44dc78c60f1fc7a31 -caec487e7117b4fa948042615525820f0dce8c763b4859db56fef87816b106e0 -handshake_secret: 3847f696cc538d008e5e2d54f5b92a18404022ec7c3fc6f16c5 -1cec65cff678dc1fdee81e3e001d282dabb9be877aa15de1d425f7612a94af864d00e -6d84350f -server_mac_key: 8b8ed457decef90f8864aec84f5d3716ab2091a8013d377976a32 -073007b1685b851539a04cbfeaeedf63565f441ca840479a84d9e6dc2b264278c4913 -08947c -client_mac_key: 9e891f73fabfce6a232ac10c2191fa9fc7ee9421cb39bcab5e376 -a24b5c89a8f7d8589ba5f110298ef209a40676af9cf53a31f16fac47a97440597544b -036a07 +76775eca8b48680e4973b26f754fbe0865af5135b284b00afce879c285db39b2ef0a9 +c1bd2f847c3f2139556b34885691be2d1917be4de9d2f50ef198950964142256 +handshake_secret: 706bae252489cd38228083790e3ead93f6b5b41682468741508 +124a58185fbb5fc2ee23850dd5285095d6e18dd133f16ecda7e35837429efdf71455e +a6642cf6 +server_mac_key: b63f98de411d2b008abaee6699ed14fa2834986f9fc2f9ae59af2 +292fe8aa5e125d0ab0d5e5954cb8de5966da7994d798c800062c17fc167c598aa0d86 +071685 +client_mac_key: 187335e3edd777cb419d055e54001325cb19ebac8d43cf736f101 +127bb19c932717960b2401699b0d5029cd3637a3175248048f1eba202eada9428b475 +f2867a oprf_key: 3f76113135e6ca7e51ac5bb3e8774eb84709ad36b8907ec8f7bc3537828 71906 ~~~ @@ -94,36 +94,36 @@ oprf_key: 3f76113135e6ca7e51ac5bb3e8774eb84709ad36b8907ec8f7bc3537828 ~~~ registration_request: 76cc85628d5ac0e01de4ede72479d607490e7f58b94578d b7a0606d74bc58b03 -registration_response: 865f3305ff73be7388313e7a74b5fc277a165ff2895f92 -60391057b84c7bc72718d5035fd0a9c1d6412226df037125901a43f4dff660c0549d4 +registration_response: 583fd26fd3130386b1a1a970e4617d45dc21c7a6f07052 +8f0175985570b4ea2018d5035fd0a9c1d6412226df037125901a43f4dff660c0549d4 02f672bcc0933 -registration_upload: a2ba5f0b3c7d340f33852c354ba36983230abbcb78a2ae22 -196f6d6272faba0079a983ebe317141ee594ffd03535c1efce5411da15a92f92981bb -c1c02c292a5e472eb7d953cdecd10691aff451364b3eb67578758184c89830c420d0e -bee4ad71b8f14b7a1059cdadc414c409064a22cf9e970b0ffc6f1fc6fdd539c467677 -5749f7c447c49605d117f4cea49637ae644f2fee8a9c5eeb44dc78c60f1fc7a31caec -487e7117b4fa948042615525820f0dce8c763b4859db56fef87816b106e0 +registration_upload: 3ea81ee30a44d65ca6db8f42a9c125277898ead7fe1604da +9870ad4542044a56dde0af371300d8b13f4dc7039ae643560c279c0e67be6086bfbe2 +188a65480689c9044a36b844b3f137a1fb594caff5e79002232a877aba39da085ce62 +e1a59f71b8f14b7a1059cdadc414c409064a22cf9e970b0ffc6f1fc6fdd539c467677 +5eca8b48680e4973b26f754fbe0865af5135b284b00afce879c285db39b2ef0a9c1bd +2f847c3f2139556b34885691be2d1917be4de9d2f50ef198950964142256 KE1: e47c1c5e5eed1910a1cbb6420c5edf26ea3c099aaaedcb03599fc311a724d84f 804133133e7ee6836c8515752e24bb44d323fef4ead34cde967798f2e9784f69f6792 6bd036c5dc4971816b9376e9f64737f361ef8269c18f69f1ab555e96d4a -KE2: 9692d473e0bde7a1fbb6d2c0e4001ccc58902102857d0e67e5fa44f4b902b17f -54f9341ca183700f6b6acf28dbfe4a86afad788805de49f2d680ab86ff39ed7fbd513 -832aba2b4ce0057dad8a9264cba31eee4713745b502b741bb8255c500dc16f1e5a57c -347bca59229cf4ed4e65abd59529fcaa0fd25ec20a777895785c1dba58c6a2517e5db -5e2940109fd2bf77d7db86b65a2032519b89559775dca96a0e8ea76fafacab0fff0de -09e1e8fecca1b835d6759b51cce90120391b3aa0bd6ff9c5ec75a8cd571370add249e +KE2: b01355438b21dc20aaa46a5dea61d5b60f81b81f347f80e8ee3addc12f62be05 +54f9341ca183700f6b6acf28dbfe4a86afad788805de49f2d680ab86ff39ed7f98431 +127a277d18a821887ef0e23d92d73000161f14a3437d5fa6e028e4b33edad12212183 +73ef515b13f023d4a17ed5f62201416b4ae07f56605093eda407390f83457ce80c4ad +48e25a7875deacb41fa9ee9c3fb9895407733ef147a97fb0eee9b47abd930777b6599 +1c53b126f9f5aae3307a111a85107fe2229f93db8fe0f9c5ec75a8cd571370add249e 99cb8a8c43f6ef05610ac6e354642bf4fedbf696e77d4749eb304c4d74be9457c5975 -46bc22aed699225499910fc913b3e9071235c693acb2f588a9ff4a7d1eb9ae3bc7f97 -c2b29d939c405cd9f010a53ee5c20400c605ee4b85bccb2523d8a00b62a160a7ad6f5 -45950e92e21802c73a1e3fa3 -KE3: 5bab0d8fdabe655eb370d81c0233588b2c3e1519ed82aa8c6faf747db4a3210b -3db5b8b7807fd1f9fb5f4304f369bab081a4fce002aa3a55bc1a45239956e3ea -export_key: 5b622c41b12add9bd88da466777d2b34ba09c975164b573280340495f -26f17d3a7dbbe163a069d54bf10e95750e86ea4f4c3a4eaa78c435bc26ef3f9058439 -a9 -session_key: 32cfd43d68402222c9ee4ba93a266aca15c0e57a9fc728d03b557be9 -ac1f8222d1c083b49988c34575ba4b2c972c5da728f3d8e8d78b2f99e62c74398c299 -881 +46bc22aed699225499910fc913b3e907125ff3ab1b935c6740b10dd4441074507761b +1d955aba1296ba52061dfec49c5e003bf84a354d30f2a1f00fb320256002b781da075 +e838cc535ece42b26d6043af +KE3: 19624915863ad577d4c6c15d56207fe0dfd96a8ec489b4bc25a7b79cc33c1d75 +717c0ba4db2cc5bb95d275ba5a5bcd39ad17459fb162d83455658acd20a4b4e4 +export_key: 252cfa49d8fd722663fe16ef7451d8cc25345f05f4967859e7500763f +42b18b036fb1c4e7f0ad0110c71fb87f80da9ab53724976c4f8fa368d1c4cb6b9c6cd +e4 +session_key: f31b89ce77171a51e6da037ad77f634184406844057f7875f04c7c4d +e08aa26d2f3bf9ab78e1e8a5faa6f625b12008d98da5e00d8aa36f9e097890337ac9c +005 ~~~ ### OPAQUE-3DH Real Test Vector 2 @@ -185,25 +185,25 @@ blind_login: e6f161ac189e6873a19a54efca4baa0719e801e336d929d35ca28b5b #### Intermediate Values ~~~ -client_public_key: 4e54972ea754782f53c77c027ee2d39a12ec1c8821047fc7c9 -5bd286cbc9c279 -auth_key: 0ffb3201ca1ee9cb60e18a952ad6f178cc9944f54c7b6e7ad29fef8e7f8 -95dfc4446ab9e26f150b7e4a710853d4a63616d4c4c7cd5e95d2fbc706c16d5d8e846 -randomized_pwd: 6f0250970339fbf8dae7950e3010fe30eb7eb4e83bfadf428983c -451f310f3765cfcfd1d6ed0c8929cb9df59443adbdc6b554f5c636f9d08ac545e84b1 -9921dc +client_public_key: 003e4b5cd9124a026302a223cd2187fb62285d33d987155c4a +3307aa55f60c5e +auth_key: c34629e50ddce1db7aa01edb9cf6979890c40f0e55d8abfc388dae762a6 +5300955344d1e273be49a1a513dbadeb045163b8882809f885dd46e978797484da85b +randomized_pwd: 2d0285e5872af71e6b6f3e76e1605de56ee229d1436563988aab1 +13b2f8104a7a14377b71158451988bb4ec11a235fbac2063dad136cffd0b1f76019d4 +123199 envelope: d0c7b0f0047682bd87a87e0c3553b9bcdce7e1ae3348570df20bf274782 -9b2d2906f10d832323d9a2a32a9f785e53cdb4a8534069409ab58e1886fa3e22de6b2 -9476314d24fdb01a4ea80e358babe708a237ac17474b858a59482a58430c11ce -handshake_secret: b404a36e7d92890dbee76e443a86a0c2868569e81b5cfc7e16c -e60dfe1614d0707e875dcc65a9447b5ba00b9b0b97fcf4e6af7c47fc6199ec7122feb -9138c3e3 -server_mac_key: 23ed6e15617061e9d7fdae661c1b746139ebb84f53bef5f6c29cc -e312c60fa36719aea50a338d7b02871da291ec44d0f5087c582c9147ac272c62f1244 -6bd0bb -client_mac_key: 13750671047d8d0b7f119b222b6b33b7dd63d4e128a766f2cc76e -49792055232a58d5919117e2b91a5afd5756f93e3cb68ac2c9424651b05d30c282a59 -e70f6c +9b2d2d20f701d2571da6f0fe29687724db96371b552df07e06c6cf3a2534147161ba4 +5e543a6d4a15eda6af8ec5aab229abbaac2af81b48c1737a419a524bb618a48d +handshake_secret: b7cab8367758393c3a7c72b38accaa91e164d77be82d4c01364 +0b8d2d6e1750f5ce1b77dcaf4173d07deeefc67895ad2d9ae6c6be8bdb384a9f54c7d +946ad85e +server_mac_key: aa959f4338f440f08dbaee86a576aae1ba65ee348f51e339feeda +8ce93ab0673904c1bb3de591689613f489705de68ae9e96146b0f54c7e0333fa50855 +587fd5 +client_mac_key: 3a3ca98df30379ac1cf59f3508b3daa60ea644c28f1e9de5a4911 +f641514db7df1f46ec850ed2f9a2053f5a7cccd77f0a9b14e0e7c570319b8080c4484 +cecf74 oprf_key: 531b0c7b0a3f90060c28d3d96ef5fecf56e25b8e4bf71c14bc770804c3f b4507 ~~~ @@ -213,36 +213,36 @@ b4507 ~~~ registration_request: ec2927a03ced1220168b6d5a54f0372f813ced8ad3673d5 1dee92d2cbfee500c -registration_response: f6e244e131f8cd14bc37a856a933c91128b2498c06540d -2dba3a197ed7d8bd778aa90cb321a38759fc253c444f317782962ca18d33101eab2c8 +registration_response: 588b259785af29c162958ebfdc4ca3b1fcf46bd1894c81 +854db7d2d41bf1933c8aa90cb321a38759fc253c444f317782962ca18d33101eab2c8 cda04405a181f -registration_upload: 4e54972ea754782f53c77c027ee2d39a12ec1c8821047fc7 -c95bd286cbc9c2793d81743982004acf05243d6c5405be93c137a8015727f59a4128d -db373cae8ffcef5db11460df6cbcc2c8d3ce0bb48fc58712a32de8dea7ac746671413 -f9eeaad0c7b0f0047682bd87a87e0c3553b9bcdce7e1ae3348570df20bf2747829b2d -2906f10d832323d9a2a32a9f785e53cdb4a8534069409ab58e1886fa3e22de6b29476 -314d24fdb01a4ea80e358babe708a237ac17474b858a59482a58430c11ce +registration_upload: 003e4b5cd9124a026302a223cd2187fb62285d33d987155c +4a3307aa55f60c5e3114123a82551b4851bbc26b4a6a25444a60c501bb3e8eae95f2a +eae7971f1358c5fe0cfa3cbf7f493be05418740bc884c1a20ad220c43ea30a7af1e92 +3e81b7d0c7b0f0047682bd87a87e0c3553b9bcdce7e1ae3348570df20bf2747829b2d +2d20f701d2571da6f0fe29687724db96371b552df07e06c6cf3a2534147161ba45e54 +3a6d4a15eda6af8ec5aab229abbaac2af81b48c1737a419a524bb618a48d KE1: d0a498e621d3ff7a011b37166a63ef40fe268f93c7d75a467eea42a98c0a490d a6bcd29b5aecc3507fc1f8f7631af3d2f5105155222e48099e5e6085d8c1187a642e7 eecf19b804a62817486663d6c6c239396f709b663a4350cda67d025687a -KE2: e6d0c01cdc14f7bfdb38effbd63394f6304b47c2dc26fd510ecdbf471486b972 -30635396b708ddb7fc10fb73c4e3a9258cd9c3f6f761b2c227853b5def228c8563f8f -17910f13eee01e10947637da13dc9a31c07c9dd324a8a59b22b4b58546a073d289b82 -0b9658dcd6c387032248c1eae07ecf16eb63ea49c39cb3e9973d619e2e41299f00e42 -defca71d240e72062c4466b4cd8b69b05ba4b3692e63f5ae15f08f142ae96fb245c48 -4610d4a169fd99911bebae28dbb14a622452e7372b823fa57f7ef652185f89114109f +KE2: 6810ba4cf3049dab416529542385d194eddf0105ad6480ea0c92f872cab6af46 +30635396b708ddb7fc10fb73c4e3a9258cd9c3f6f761b2c227853b5def228c854aa9f +c9066d61f2a64d5890ed79ff8e4a200444dc2e180ae57d6e78bdb594722d57283a3ff +9a6600f61341f9ad49c32be9054ad8a26837d8ccea6de753520b1eb836c85faa907a4 +428828fb5ad362ff1b5327afdd1d1c300e03db05f9d55c1535bac50d76b4e5f19dcd7 +0ee043d2254ffcae31bffaf426f0b862f5fd311649723fa57f7ef652185f89114109f 5a61cc8c9216fdd7398246bb7a0c20e2fbca2d8ae070cdffe5bb4b1c373e71be8e7d8 -f356ee5de37881533f10397bcd84d354454fa8a9e5f63ce02d024dc602e2f61be2d7d -454ca88550af0851f3ee5e4de05e6e255c6b3bde1a47d808ca920783d5409040312d2 -b3ee35f1fd8e24d009ed37ca -KE3: ea093c79f3d9d7718f3f632ed9c2e9004d069dea13ba7f08cd33de0f3042886e -bf6c4dc9cc9898a96f4b539b5b735f440005d07c9a636b6c5f76f2e78582ddc1 -export_key: 987da0b1086098b5e11fcd74e9232515f2545779865cab6d911d78b4f -d4c9a464e9df1864419c26d7eb8708edf95a2a17b76ebbeb2979d30d6c2b4a3f26345 -64 -session_key: 5c2f66f5e3602b3150af772515ffb7cdfc9ace8626b319659f86f6c2 -a01fe437d12a0c3f72f216bbaef007a35273bfa0d8d1220fb54e53a70f3fb9ae39345 -064 +f356ee5de37881533f10397bcd84d35445a04e7e9047091f5b371582b6f7eb8f8f665 +6f800f2ad2491122a28fb5623987c28cd9d8fae45e0ab205cff7635c83fe993a83b0f +b2c1c85e599f0a33bd65a7ef +KE3: 37de6eb166462dd8d36a6f0c75bbe826856cf6f7f067935aaf5fddaeb6e56935 +e8f1b53af96d5194dd49cb2438801745597affd3af7508f260746573d42144bd +export_key: cb43c5c131fc4ee3c72a9c5a664a0137d46088c58cf24de4408e838d8 +1b743f6e1b9516cdc307775c45d3c70d3a446180782cb4a7dea4be5777b1934a78c11 +e1 +session_key: a40fa034f8220e18c1991f981655e8255009d62a596a579f08fd476a +08cca7d66189a19e68f180e520de10ee841ab4e4f1a61291468429f093776f0bc8663 +528 ~~~ ### OPAQUE-3DH Real Test Vector 3 @@ -301,20 +301,20 @@ cd15c478 #### Intermediate Values ~~~ -client_public_key: 0286913c9cdffc7a71c54cc5b32a3faa62cfba0180f0b76351 -096409c9e19cd72e -auth_key: 91ec5a156946628872502569eee2a1a3d9deaa3320a11175ea462bfa607 -98af5 -randomized_pwd: 10706a06fc4c1c9101c655e0bc1fe9cf6fecdf38d8d0c15219bd2 -4fc6d738214 +client_public_key: 03c84a1dc96d2b896f20b390e75ae7e5ebedbb4db6c6cc9a78 +96e3c5d5f280e7ab +auth_key: 9a6ce467dca8841cb0f706bfd83f39cc8e855d000d982554af799acc33d +8354e +randomized_pwd: d8a0060fa0d6118cf89fe9df92a9b65dd1b0dd86cccbdee067926 +7dce6f50e3a envelope: 2527e48c983deeb54c9c6337fdd9e120de85343dc7887f00248f1acacc4 -a831981a30154a6b4305a0d6906b704a148673ec0266683850abe9a2a91971eaa53b9 -handshake_secret: 742560ea50065a0af34e458d98ef78f935bedf880437bc73164 -15324dbab525c -server_mac_key: c3299b1ed28d5b8cb3e37ab09ef31a2a0406c0521d0faf2320a20 -d7fdbb53ddc -client_mac_key: f96f80d13370635c3822fe6eba028ae3e05c1b8c298e05b32c669 -83663cdb3a3 +a8319bb19ce6d364d86bd95a1516a49e288e0e013a197609e1de0b4e9e5950ade9c13 +handshake_secret: 3f11e9bb0233a88d5c00f236485058ebcbfd24180d0b8f7f078 +e8b88fa8a1c04 +server_mac_key: 7d82030842afc175e1a0fbddceba0e0e53102bf170ee394419d2b +fdefefda358 +client_mac_key: a605eb6d2e72308f29fc1d1709a683aaa5d09e775134bded0deae +e17dccaa9a1 oprf_key: d153d662a1e7dd4383837aa7125685d2be6f8041472ecbfd610e46952a6 a24f1 ~~~ @@ -324,30 +324,30 @@ a24f1 ~~~ registration_request: 0325768a660df0c15f6f2a1dcbb7efd4f1c92702401edf3 e2f0742c8dce85d5fa8 -registration_response: 0244211a4d2a067f7a61ed88dff6764856d347465f330d -0e15502700afd1865911025b95a6add1f2f3d038811b5ad3494bed73b1e2500d8dade +registration_response: 03de5c8f7d8ea7fd9590b0c8321b5f508bb8f49bbff83c +5449ef50d66bf3e93892025b95a6add1f2f3d038811b5ad3494bed73b1e2500d8dade c592d88406e25c2f2 -registration_upload: 0286913c9cdffc7a71c54cc5b32a3faa62cfba0180f0b763 -51096409c9e19cd72e2dd8b5c51a4689c469f067e2766b987fa6c4ee4353b19f2974f -2407e2dc07fd92527e48c983deeb54c9c6337fdd9e120de85343dc7887f00248f1aca -cc4a831981a30154a6b4305a0d6906b704a148673ec0266683850abe9a2a91971eaa5 -3b9 +registration_upload: 03c84a1dc96d2b896f20b390e75ae7e5ebedbb4db6c6cc9a +7896e3c5d5f280e7aba453596f5b719f2cf3c0982ebd2466a8442f3a98d9dcfe420b3 +5acb7cd8d0e592527e48c983deeb54c9c6337fdd9e120de85343dc7887f00248f1aca +cc4a8319bb19ce6d364d86bd95a1516a49e288e0e013a197609e1de0b4e9e5950ade9 +c13 KE1: 03884e56429f1ee53559f2e244392eb8f994fd46c8fd9ffdd24ac5a7af963a66 3b967fcded96ed46986e60fcbdf985232639f537377ca3fcf07ad489956b2e9019033 58b4eae039953116889466bfddeb40168e39ed83809fd5f0d5f2de9c5234398 -KE2: 0383fff1b3e8003723dff1b1f90a7934a036bd6691aca0366b07a100bf2bb3dc -2acb792f3657240ce5296dd5633e7333531009c11ee6ab46b6111f156d96a160b2977 -de9d90759e8c525469d291422e8f4d66751b4b8d01fa9f62d142e46b78e672b8212b2 -9d7d6d5d6884efd48a34827c7699d92e6dea7a8b608735f6bae399b1840c3f2eb0901 -da4a2468467677abc843ec7d12e0dcc6359b74d145506fa79aebf8018e88ecfc53891 +KE2: 0225dbce19cf48eb908d66d0e955d0fe7d0f67d09bb0362154c7316d69700e23 +29cb792f3657240ce5296dd5633e7333531009c11ee6ab46b6111f156d96a160b268f +4a82c2b61a752672a3e322b6b8580c1a2c76fad4563d06c12a27146f73dbb5267ca8b +b86a83d0b902b97ac14d12501697300815c5d5fdc262830a351bb4416baceb16938e6 +cfd021f43dee80b9bc400304a4398480e195bc51b3bcc186ff0bf8018e88ecfc53891 529278c47239f8fe6f1be88972721898ef81cc0a76a0b5500242bc29993976185dacf -6be815cbfa923aac80fad8b7f020c9d4f18e0b6867a175e6476d88558e5c2035530b5 -3ce13597a06d22b9586040847b6d1e6f24a5ab90 -KE3: a3dbf3ba53af908306d649ff0f7ff8db80311143d340356d0c817d02632e5e23 -export_key: 4cd8130ebe797e83bb86a60dc100923349180d1da9c2f880791097111 -c18aa96 -session_key: 049948e93acc746a961a1efeec71af938957281a640623670f6f7a86 -f5a3539b +6be815cbfa923aac80fad8b7f020c9d4f18e0b6867a17c4af032f0221800fec352a2b +ec9ddb2dd8b91a087aa51c7fbbaf5efcbbea52fe +KE3: eb86a68c5e8812293d1da4a60e499236ffdffb34b29f6f8f0ac46979f07b1ef4 +export_key: b755602f5d0a8c2118f38608a98cf08f20adadf5ef759cea8e246e5ed +5bf95c3 +session_key: 08b539a036c888da87a25205c9c386f382bc53b098dae42f88f2320c +48f1a3dd ~~~ ### OPAQUE-3DH Real Test Vector 4 @@ -408,20 +408,20 @@ blind_login: 4308682dc1bdab92ff91bb1a5fc5bc084223fe4369beddca3f1640a6 #### Intermediate Values ~~~ -client_public_key: 035d5fedb78e26e301de5b93bf181fba61f6407d8df3da212d -fdcfea66ef271f55 -auth_key: 3bd6e6627f0f880f290fb77904f39ed2699e90b80440ba26f5eeb1bf30f -5f565 -randomized_pwd: 59ae24f1bcf649d009bf972845e0aab0b37951c5201b6f2699707 -2c79d84e104 +client_public_key: 03a12f7047c8a1774a745520b2eaac995687fbb6212a418f9c +1696d4186278eaa3 +auth_key: 2461460e02dde8a10c98e2911d4d5a3be0bd85f095064ade2f3a0ae79a4 +89b07 +randomized_pwd: 3d990218aab34ca0137bbbb298adcf585d4495ae843eddcbe3ca8 +f969b690676 envelope: 75c245690f9669a9af5699e8b23d6d1fa9e697aeb4526267d942b842e44 -26e42e05c3e55ce81561193b699bd0981f86cda079361b1864b332335e239c707fa67 -handshake_secret: 208f53119e80ea03007ab521ccad95a90fafb815551a6b0996c -0c96620415176 -server_mac_key: d246f0371736ce72116f88cd15b59a87a7d3fe25a1b09d34a3f88 -56bcb1b5785 -client_mac_key: 2fc9858f5e228e9e344a790ff5157e96dbfa3b5ed2cfd050073cf -9ca4b49ee9f +26e423d452786ba80be94bd8ebe643394d1a07e745a07e97b37a88b585b8afd6ce3cb +handshake_secret: 586e927091452c797e0eb69fc90840f4c1923a6852834644c5f +47cc8b5810d55 +server_mac_key: 09f90fb15227c185ba6102b797251d32f6bfaec56b218743ba4bb +c64696f9734 +client_mac_key: 7612361b97d074852344ee92ecfe154b93241e696808438745d4c +b4f5b8513fb oprf_key: f14e1fc34ba1218bfd3f7373f036889bf4f35a8fbc9e8c9c07ccf2d2388 79d9c ~~~ @@ -431,30 +431,30 @@ oprf_key: f14e1fc34ba1218bfd3f7373f036889bf4f35a8fbc9e8c9c07ccf2d2388 ~~~ registration_request: 02792b0f4670aced5970a68b01bb951004ccad962159be4 b6783170c9ad68f6052 -registration_response: 03cc3491b4bcb3e4804f3eadbc6a04c8fff18cc9ca5a4f -eeb577fdfebd71f5060f029a2c6097fbbcf3457fe3ff7d4ef8e89dab585a67dfed090 +registration_response: 02101f7b9999e363b44dfa946eaad9930fda88d53632aa +701778747b6a411a071c029a2c6097fbbcf3457fe3ff7d4ef8e89dab585a67dfed090 5c9f104d909138bae -registration_upload: 035d5fedb78e26e301de5b93bf181fba61f6407d8df3da21 -2dfdcfea66ef271f55f83490fc11b3ceccad8e302f5d7e98661dc91191f6fb742e05f -3278095a9741875c245690f9669a9af5699e8b23d6d1fa9e697aeb4526267d942b842 -e4426e42e05c3e55ce81561193b699bd0981f86cda079361b1864b332335e239c707f -a67 +registration_upload: 03a12f7047c8a1774a745520b2eaac995687fbb6212a418f +9c1696d4186278eaa37b6e2a7531d9ca9a324ac5c1a02303f00175c41646a873441a5 +eb69dcbec4ea975c245690f9669a9af5699e8b23d6d1fa9e697aeb4526267d942b842 +e4426e423d452786ba80be94bd8ebe643394d1a07e745a07e97b37a88b585b8afd6ce +3cb KE1: 02fe96fc48d9fc921edd8e92ada581cbcc2a65e30962d0002ea5242f5baf627f f646498f95ec7986f0602019b3fbb646db87a2fdbc12176d4f7ab74fa5fadace6002a 9f857ad3eabe09047049e8b8cee72feea2acb7fc487777c0b22d3add6a0e0c0 -KE2: 035115b21dde0992cb812926d65c7dccd5e0f8ffff573da4a7c1e603e0e40827 -895947586f69259e0708bdfab794f689eec14c7deb7edde68c81645156cf278f219d6 -6e0743f1106cd46a10f249f404071bc798a39273d3cc3d2f4b88bb34e3b0df9488d32 -72d42be1fddf60ee63c0628015aaefb8f4fa3ca2365e70041a1014c9dff4014550cd0 -dd29a9b476f699b3a6d9528f98c27f471054f4883c875be99a0ed581ac468101aee52 +KE2: 03463f69fc22bfa666c55bd38319addcf5816f063ec5ae9fdeb7e572603c6698 +025947586f69259e0708bdfab794f689eec14c7deb7edde68c81645156cf278f219cb +3882d08a0617909c1a9f545dace3d56b5034d8025220e0280d5d541eb22ade140fa11 +663bd0a4c787203b93e423f431b3702ffcc635919dcf0d22520d90596fccdef52f3ca +c75d2804a96d1521b78205c47c998cdb4aafcb7cce4c174671423581ac468101aee52 8cc6b69daac7a90de8837d49708e76310767cbe4af18594d022aa8746ab4329d59129 -6652d44f6dfb04470103311bacd7ad51060ef5abac41bb37b28686cee6592632e4dba -19c914f418da2f9b79a4f0783edb013d189fe9b8 -KE3: 342b1213e0b0ab4101decbae6a6759e174cfbaf916ebe304e07128ae3ebf6076 -export_key: 446f5a94d53f60823f27547118f7b499636617a81c9bbcd28e0ddacb8 -5bdce59 -session_key: 6d15cde32da560bf0d2363066e66028515d9117348cf78d205bca58b -e35550b0 +6652d44f6dfb04470103311bacd7ad51060ef5abac41ba38a2e46ce2cfd59c6dfdd1e +77758505d944b28e753a7254bac79302947dc7d0 +KE3: 2ae94c682a2bc4c89eb16c395dc09d2b14d216dee0e59f34c317f5a6d8bbc717 +export_key: 04f3100265180b083abbd84109f5ed963481eb78a5d377e888810217f +fb8af04 +session_key: 7a0a42051497621e659270552be01baadddd1ee829f802891535a3fb +ac2a33ec ~~~ ## Fake Test Vectors {#fake-vectors} @@ -515,16 +515,16 @@ KE1: 1ef5fc13fa7695e81b5fcadf57eb49a579b10e4f51bbee11afb278608592456b #### Output Values ~~~ -KE2: 2e1bb024ff255d0f35eb7b1f11174b3e60d8aaabb11ea347a6da0c1964594f4f +KE2: 02648d7558231b92265efe08ec0b3dec70e596e36ea6c70ceae961411bf8f328 7cb33db5ba8082e4f4bfb830e8e3f525b0ddcb70469b34224758d725ce53ac76094c0 aa800d9a0884392e4efbc0479e3cb84a38c9ead879f1ff755ad762c06812b9858f82c 9722acc61b8eb1d156bc994839bf9ed8a760615258d23e0f94fa2cffadc655ed0d6ff 6914066427366019d4e6989b65d13e38e8edc5ae6f82aa1b6a46bfe6ca0256c64d0cf db50a3eb7676e1d212e155e152e3bbc9d1fae3c679aacae1f4fee4ee4ba509fda550e a0421a85762305b1db20e37f4539b2327d37b805e5c0ac2904c7d9bf38f99e0050594 -e484b4d8ded8038ef6e0c141a985fa6b3528ef79e28dbd3783322ab69900a43be8919 -a840cfcc5aa31a8f42b6f2a0c1ce1f9fa50c58dc5787a957af588580117b70d304639 -dc68851224301bbbae9cd654 +e484b4d8ded8038ef6e0c141a985fa6b35afc0c330be0512ba1eace7c1cae0b807f01 +6f2a67b604008b270f3e41a8fb3d54084b62510495baa0309a993a48cf2110cfe2555 +33047291134a010c13509ba1 ~~~ ### OPAQUE-3DH Fake Test Vector 2 @@ -581,13 +581,13 @@ KE1: 031ac7e5c8099fcb7de5ad5b6cf33ff53078dbee1da64f15f6cd53b2afe6e332 #### Output Values ~~~ -KE2: 02200f91b03819f6a4b0957216fc94a2230d75d0e1be1fe0ced9434b0ec9d23a -5621cd364318a92b2afbfccea5d80d337f07defe40d92673a52f3844058f5d949a604 +KE2: 02ed3cb4182cb2c2659d6c1d88014e821ea4fc00de1aca987fae5483f5f8aa59 +d021cd364318a92b2afbfccea5d80d337f07defe40d92673a52f3844058f5d949a604 39294e7567fc29643e0d5c8799d0dffbbfc8609558b982012fa90aef2ce52b1ffdd8f 96bda49f5306ae346cd745812d3a953ff94712e4ed0acc67c99b432860e337fe3234b ba88415ac55368b938106cca4049b5c13496fe167d3a092bd990e2b772c1eb569cc2b 57741bf3be630e377c8245b11d0b6ad1fe1d606490c2720802a59205c836a2ab86e19 -dbd9a417818052179e9a5c99221e2d1d8a780dfe4734d7325a81225091665460460ec -37fcf0431f738ba6cb80b63756ee70c6e43aeae5 +dbd9a417818052179e9a5c99221e2d1d8a780dfe4734dc9b9b3f64e5b3572a8f05f68 +93b0fa4dd12fba85ea99c8760b8011321bc37263 ~~~ "#; diff --git a/src/tests/parser.rs b/src/tests/parser.rs index 7bb6000..aed783f 100644 --- a/src/tests/parser.rs +++ b/src/tests/parser.rs @@ -15,10 +15,8 @@ fn parse_vector_types(input: &str) -> String { let chunks: Vec<&str> = re.split(input).collect(); - println!("{:?}", chunks.len()); let mut count = 1; for caps in re.captures_iter(input) { - println!("{:?}", caps["type"].to_string()); let vector_type = format!( "\"{}\": [\n {} \n]", caps["type"].to_string(), diff --git a/src/tests/test_opaque_vectors.rs b/src/tests/test_opaque_vectors.rs index b6c353b..a653358 100644 --- a/src/tests/test_opaque_vectors.rs +++ b/src/tests/test_opaque_vectors.rs @@ -11,6 +11,7 @@ use alloc::{string::ToString, vec, vec::Vec}; use json::JsonValue; #[allow(non_snake_case)] +#[derive(Debug)] pub struct OpaqueTestVectorParameters { pub dummy_private_key: Vec, pub dummy_masking_key: Vec, @@ -66,6 +67,19 @@ macro_rules! parse_default { }; } +/// If no entry is found, default to filling a random buffer of a specified size +macro_rules! parse_default_random { + ( $v:ident, $s:expr, $size:expr ) => { + parse_default!($v, $s, { + use rand::{rngs::OsRng, RngCore}; + let mut rng = OsRng; + let mut v = vec![0u8; $size]; + rng.fill_bytes(&mut v); + v + }) + }; +} + fn decode(values: &JsonValue, key: &str) -> Option> { values[key] .as_str() @@ -74,8 +88,8 @@ fn decode(values: &JsonValue, key: &str) -> Option> { fn populate_test_vectors(values: &JsonValue) -> OpaqueTestVectorParameters { OpaqueTestVectorParameters { - dummy_private_key: parse_default!(values, "client_private_key", vec![0u8; 32]), - dummy_masking_key: parse_default!(values, "masking_key", vec![0u8; 64]), + dummy_private_key: parse_default_random!(values, "client_private_key", 32), + dummy_masking_key: parse_default_random!(values, "masking_key", 64), context: parse!(values, "Context"), client_private_key: decode(values, "client_private_key"), client_keyshare: parse!(values, "client_keyshare"), @@ -121,7 +135,7 @@ fn get_password_file_bytes( RegistrationUpload::deserialize(¶meters.registration_upload[..]).unwrap(), ); - Ok(password_file.serialize()) + password_file.serialize() } fn parse_identifiers( @@ -141,9 +155,15 @@ fn parse_identifiers( macro_rules! json_to_test_vectors { ( $v:ident, $vector_type:expr, $cs:expr, ) => { - $v[$vector_type][$cs] + $v[$vector_type] .members() - .map(|x| populate_test_vectors(&x)) + .filter_map(|x| { + if x.has_key($cs) { + Some(populate_test_vectors(&x[$cs])) + } else { + None + } + }) .collect::>() }; } @@ -153,16 +173,13 @@ fn tests() -> Result<(), ProtocolError> { let rfc = json::parse(super::parser::rfc_to_json(super::opaque_vectors::VECTORS).as_str()) .expect("Could not parse json"); - let ristretto_real_tvs = json_to_test_vectors!( - rfc, - String::from("Real"), - String::from("ristretto255, SHA512"), - ); - let ristretto_fake_tvs = json_to_test_vectors!( - rfc, - String::from("Fake"), - String::from("ristretto255, SHA512"), - ); + let ristretto_real_tvs = json_to_test_vectors!(rfc, "Real", "ristretto255, SHA512",); + + let ristretto_fake_tvs = json_to_test_vectors!(rfc, "Fake", "ristretto255, SHA512",); + + if ristretto_real_tvs.len() == 0 || ristretto_fake_tvs.len() == 0 { + panic!("Parsing error"); + } struct Ristretto255Sha512NoSlowHash; impl CipherSuite for Ristretto255Sha512NoSlowHash { @@ -184,16 +201,14 @@ fn tests() -> Result<(), ProtocolError> { #[cfg(feature = "p256")] { - let p256_real_tvs = json_to_test_vectors!( - rfc, - String::from("Real"), - String::from("P256_XMD:SHA-256_SSWU_RO_, SHA256"), - ); - let p256_fake_tvs = json_to_test_vectors!( - rfc, - String::from("Fake"), - String::from("P256_XMD:SHA-256_SSWU_RO_, SHA256"), - ); + let p256_real_tvs = + json_to_test_vectors!(rfc, "Real", "P256_XMD:SHA-256_SSWU_RO_, SHA256",); + let p256_fake_tvs = + json_to_test_vectors!(rfc, "Fake", "P256_XMD:SHA-256_SSWU_RO_, SHA256",); + + if p256_real_tvs.len() == 0 || p256_fake_tvs.len() == 0 { + panic!("Parsing error"); + } struct P256Sha256NoSlowHash; impl CipherSuite for P256Sha256NoSlowHash { @@ -226,7 +241,7 @@ fn test_registration_request( ClientRegistration::::start(&mut rng, ¶meters.password)?; assert_eq!( hex::encode(¶meters.registration_request), - hex::encode(client_registration_start_result.message.serialize()) + hex::encode(client_registration_start_result.message.serialize()?) ); } Ok(()) @@ -255,7 +270,7 @@ fn test_registration_response( ); assert_eq!( hex::encode(¶meters.registration_response), - hex::encode(server_registration_start_result.message.serialize()) + hex::encode(server_registration_start_result.message.serialize()?) ); } Ok(()) @@ -278,7 +293,6 @@ fn test_registration_upload( Some(ids) => ClientRegistrationFinishParameters::new(Some(ids), None), }, )?; - assert_eq!( hex::encode(¶meters.auth_key), hex::encode(result.auth_key) @@ -289,7 +303,7 @@ fn test_registration_upload( ); assert_eq!( hex::encode(¶meters.registration_upload), - hex::encode(result.message.serialize()) + hex::encode(result.message.serialize()?) ); assert_eq!( hex::encode(¶meters.export_key), @@ -308,12 +322,18 @@ fn test_ke1(tvs: &[OpaqueTestVectorParameters]) -> Result<(), P ¶meters.client_nonce[..], ] .concat(); + + println!( + "¶meters.blind_login[..]: {:?}", + hex::encode(¶meters.blind_login[..]) + ); + let mut client_login_start_rng = CycleRng::new(client_login_start); let client_login_start_result = ClientLogin::::start(&mut client_login_start_rng, ¶meters.password)?; assert_eq!( hex::encode(¶meters.KE1), - hex::encode(client_login_start_result.message.serialize()) + hex::encode(client_login_start_result.message.serialize()?) ); } Ok(()) @@ -370,7 +390,7 @@ fn test_ke2(tvs: &[OpaqueTestVectorParameters]) -> Result<(), P ); assert_eq!( hex::encode(¶meters.KE2), - hex::encode(server_login_start_result.message.serialize()) + hex::encode(server_login_start_result.message.serialize()?) ); } Ok(()) @@ -416,7 +436,7 @@ fn test_ke3(tvs: &[OpaqueTestVectorParameters]) -> Result<(), P ); assert_eq!( hex::encode(¶meters.KE3), - hex::encode(client_login_finish_result.message.serialize()) + hex::encode(client_login_finish_result.message.serialize()?) ); assert_eq!( hex::encode(¶meters.export_key), @@ -516,7 +536,7 @@ fn test_fake_vectors( )?; assert_eq!( hex::encode(¶meters.KE2), - hex::encode(server_login_start_result.message.serialize()) + hex::encode(server_login_start_result.message.serialize()?) ); } Ok(()) diff --git a/src/tests/voprf_test_vectors.rs b/src/tests/voprf_test_vectors.rs deleted file mode 100644 index f5cf423..0000000 --- a/src/tests/voprf_test_vectors.rs +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. -// -// This source code is licensed under the MIT license found in the -// LICENSE file in the root directory of this source tree. - -use crate::group::Group; -use crate::hash::Hash; -use crate::tests::mock_rng::CycleRng; -use crate::{errors::*, oprf}; -use alloc::string::ToString; -use alloc::vec::Vec; -use curve25519_dalek::ristretto::RistrettoPoint; -use generic_array::GenericArray; -use serde_json::Value; -use sha2::Sha512; - -struct VOPRFTestVectorParameters { - sksm: Vec, - input: Vec, - blind: Vec, - blinded_element: Vec, - evaluation_element: Vec, - output: Vec, -} - -// Taken from https://github.com/cfrg/draft-irtf-cfrg-voprf/blob/master/draft-irtf-cfrg-voprf.md -// in base mode -static OPRF_RISTRETTO255_SHA512: &[&str] = &[ - r#" - { - "sksm": "caeff69352df4905a9121a4997704ca8cee1524a110819eb87deba1a39ec1701", - "input": "00", - "blind": "c604c785ada70d77a5256ae21767de8c3304115237d262134f5e46e512cf8e03", - "blinded_element": "fc20e03aff3a9de9b37e8d35886ade11ec7d85c2a1fb5bb0b1686c64e07ac467", - "evaluation_element": "7c72cc293cd7d44c0b57c273f27befd598b132edc665694bdc9c42a4d3083c0a", - "output": "e3a209dce2d3ea3d84fcddb282818caebb756a341e08a310d9904314f5392085d13c3f76339d745db0f46974a6049c3ea9546305af55d37760b2136d9b3f0134" - } - "#, - r#" - { - "sksm": "caeff69352df4905a9121a4997704ca8cee1524a110819eb87deba1a39ec1701", - "input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", - "blind": "5ed895206bfc53316d307b23e46ecc6623afb3086da74189a416012be037e50b", - "blinded_element": "483d4f39de5ff77fa0f9a0ad2334dd5bf87f2cda868539d21de67ce49e7d1536", - "evaluation_element": "026f2758fc62f02a7ff95f35ec6f20186aa57c0274361655543ea235d7b2aa34", - "output": "2c17dc3e9398dadb44bb2d3360c446302e99f1fe0ec40f0b1ad25c9cf002be1e4b41b4900ef056537fe8c14532ccea4d796f5feab9541af48057d83c0db86fe9" - } - "#, -]; -#[cfg(feature = "p256")] -static OPRF_P256_SHA256: &[&str] = &[ - r#" - { - "sksm": "a1b2355828f2c76de6749af9d093bd9fe0f2cada3ec653cd9a6d3126a7a7827b", - "input": "00", - "blind": "5d9e7f6efd3093c32ecceabd57fb03cf760c926d2a7bfa265babf29ec98af0d0", - "blinded_element": "03e3c379698da853d9844098fa0ac676970d5ec24167b598714cd2ee188604ddd2", - "evaluation_element": "03ea54e8d095332d1a601a3f8a5013188aea036bf9b563236f7fd3b046908b42fd", - "output": "464e3e51e4086a824d9a2f939524d7069ae4072a788bc9d5daa0762b25826437" - } - "#, - r#" - { - "sksm": "a1b2355828f2c76de6749af9d093bd9fe0f2cada3ec653cd9a6d3126a7a7827b", - "input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", - "blind": "825155ab61f17605af2ae2e935c78d857c9407bcd45128d57d338f1671b5fcbe", - "blinded_element": "030b40be181ffbb3c3ae4a4911287c43261f5e4034781def69c51608f372a02102", - "evaluation_element": "03115ad70ea55dbb4006da0ee3589a3582f31ef9cd143996d1e31a25ad3abdcf6f", - "output": "b597d58c843d0f9d2712121b0a3e2912ebee1c829eed3089eade9af4359ab275" - } - "#, -]; - -fn decode(values: &Value, key: &str) -> Option> { - values[key] - .as_str() - .and_then(|s| hex::decode(&s.to_string()).ok()) -} - -fn populate_test_vectors(values: &Value) -> VOPRFTestVectorParameters { - VOPRFTestVectorParameters { - sksm: decode(&values, "sksm").unwrap(), - input: decode(&values, "input").unwrap(), - blind: decode(&values, "blind").unwrap(), - blinded_element: decode(&values, "blinded_element").unwrap(), - evaluation_element: decode(&values, "evaluation_element").unwrap(), - output: decode(&values, "output").unwrap(), - } -} - -#[test] -fn tests() -> Result<(), ProtocolError> { - test_blind::(OPRF_RISTRETTO255_SHA512)?; - test_evaluate::(OPRF_RISTRETTO255_SHA512)?; - test_finalize::(OPRF_RISTRETTO255_SHA512)?; - - #[cfg(feature = "p256")] - { - use p256_::ProjectivePoint; - use sha2::Sha256; - - test_blind::(OPRF_P256_SHA256)?; - test_evaluate::(OPRF_P256_SHA256)?; - test_finalize::(OPRF_P256_SHA256)?; - } - - Ok(()) -} - -// Tests input -> blind, blinded_element -fn test_blind(tvs: &[&str]) -> Result<(), ProtocolError> { - for tv in tvs { - let parameters = populate_test_vectors(&serde_json::from_str(tv).unwrap()); - let mut rng = CycleRng::new(parameters.blind.to_vec()); - - let (token, blinded_element) = oprf::blind::<_, G, H>(¶meters.input, &mut rng)?; - - assert_eq!(¶meters.blind, &G::scalar_as_bytes(token.blind).to_vec()); - assert_eq!( - ¶meters.blinded_element, - &blinded_element.to_arr().to_vec() - ); - } - Ok(()) -} - -// Tests sksm, blinded_element -> evaluation_element -fn test_evaluate(tvs: &[&str]) -> Result<(), ProtocolError> { - for tv in tvs { - let parameters = populate_test_vectors(&serde_json::from_str(tv).unwrap()); - let evaluation_element = oprf::evaluate::( - G::from_element_slice(GenericArray::from_slice(¶meters.blinded_element)).unwrap(), - &G::from_scalar_slice(GenericArray::from_slice(¶meters.sksm)).unwrap(), - ); - - assert_eq!( - ¶meters.evaluation_element, - &evaluation_element.to_arr().to_vec() - ); - } - Ok(()) -} - -// Tests input, blind, evaluation_element -> output -fn test_finalize(tvs: &[&str]) -> Result<(), ProtocolError> { - for tv in tvs { - let parameters = populate_test_vectors(&serde_json::from_str(tv).unwrap()); - - let output = oprf::finalize::( - ¶meters.input, - &G::from_scalar_slice(GenericArray::from_slice(¶meters.blind))?, - G::from_element_slice(GenericArray::from_slice(¶meters.evaluation_element))?, - )?; - - assert_eq!(¶meters.output, &output.to_vec()); - } - Ok(()) -}