From 82e4436d39acdc42b597f36e088f3c05b269c29a Mon Sep 17 00:00:00 2001 From: daxpedda Date: Tue, 4 Jan 2022 00:50:40 +0100 Subject: [PATCH] General improvements (#250) * Remove unnecessary constraints on hash * Remove unnecessary `Result` on `KeyPair::generate_random` * Fix de-serialization issue on `Ke1State` * Fix rustfmt * Remove allocations in `envelope` * Run Clippy for tests and rustdoc lints too * Fix `Debug` implementation * Fix missing constraints on `ClientRegistration` * Fix de-serialization * Pin temporary dependency * Update dependencies * Replace macro with derive-where * Remove unnecessary installation of Rust components * Improve macro naming * Implement `Copy`, `Debug`, `Ord` and `PartialOrd` for high-level items * Add `rust-version` field to `Cargo.toml` * Remove unnecessary allocations * Fix MSRV * Fix no_std * Remove unnecessary allocations * Remove unnecessary allocations * Not importing items from voprf helps readability * Fix rustdoc * Remove unnecessary allocations * Remove unnecessary allocations * Replace `Vec` from `diffie_hellman` with `GenericArray` * Remove unnecessary allocations * Remove unnecessary allocations * Remove `cfg(feature = bench)` guard for `missing_docs` * Fix documentation * Remove all remaining allocations from `KeyExchange` * Improve type-safety * Remove all remaining allocations in `keypair` * Remove last remaining allocations except `NonVerifiableClient` input * Remove base64 encoding in Serde implementation * Remove unnecessary Serde `alloc` feature * Make curve25519-dalek optional * Rename `serialize` crate feature to `serde` * Switch `KeGroup` implementations to higher-level libraries - Fixes missing clamping in X25519 - X25519 is now a separate crate feature * Fix typo --- .github/workflows/main.yml | 66 +- Cargo.toml | 38 +- benches/opaque.rs | 97 +- examples/digital_locker.rs | 61 +- examples/simple_login.rs | 61 +- src/envelope.rs | 225 ++-- src/errors.rs | 4 +- src/impls.rs | 170 +-- src/key_exchange/group/mod.rs | 2 + src/key_exchange/group/p256.rs | 32 +- src/key_exchange/group/ristretto255.rs | 2 +- src/key_exchange/group/x25519.rs | 135 +- src/key_exchange/traits.rs | 69 +- src/key_exchange/tripledh.rs | 411 +++--- src/keypair.rs | 368 +++--- src/lib.rs | 293 +++-- src/messages.rs | 347 +++-- src/opaque.rs | 806 ++++++------ src/serialization/mod.rs | 178 ++- src/serialization/tests.rs | 779 +++++++---- src/slow_hash.rs | 13 +- src/tests/full_test.rs | 1670 ++++++++++++++++-------- src/tests/mock_rng.rs | 2 +- src/tests/parser.rs | 6 +- src/tests/test_opaque_vectors.rs | 375 ++++-- 25 files changed, 3604 insertions(+), 2606 deletions(-) mode change 100644 => 100755 Cargo.toml mode change 100644 => 100755 src/envelope.rs mode change 100644 => 100755 src/impls.rs mode change 100644 => 100755 src/key_exchange/traits.rs mode change 100644 => 100755 src/key_exchange/tripledh.rs mode change 100644 => 100755 src/messages.rs mode change 100644 => 100755 src/opaque.rs mode change 100644 => 100755 src/serialization/tests.rs mode change 100644 => 100755 src/tests/full_test.rs mode change 100644 => 100755 src/tests/test_opaque_vectors.rs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e0d8dab..0da0f99 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,7 +4,7 @@ on: branches: - main pull_request: - types: [opened, repoened, synchronize] + types: [opened, reopened, synchronize] jobs: test: @@ -13,14 +13,15 @@ jobs: fail-fast: false matrix: backend_feature: - - u64_backend - - u32_backend - - p256,u64_backend + - ristretto255_u64 + - ristretto255_u32 + - p256 + - x25519_u64,ristretto255_u64 toolchain: - stable - 1.51.0 exclude: - - backend_feature: p256,u64_backend + - backend_feature: p256 toolchain: 1.51.0 name: test steps: @@ -33,7 +34,6 @@ jobs: profile: minimal toolchain: ${{ matrix.toolchain }} override: true - components: rustfmt, clippy - name: Run cargo test uses: actions-rs/cargo@v1 @@ -57,9 +57,10 @@ jobs: # 32-bit x86 - i686-unknown-linux-gnu backend_feature: - - u64_backend - - u32_backend - - p256,u64_backend + - ristretto255_u64 + - ristretto255_u32 + - p256 + - x25519_u64,ristretto255_u64 steps: - uses: actions/checkout@v2 - uses: hecrj/setup-rust-action@v1 @@ -76,12 +77,18 @@ jobs: fail-fast: false matrix: backend_feature: - - u64_backend - - u32_backend - - p256,u64_backend + - ristretto255_u64 + - ristretto255_u32 + - p256 + - ristretto255_u64,p256 + - x25519_u64,ristretto255_u64 + - x25519_u32,ristretto255_u32 + - x25519_u64,p256 + - x25519_u32,p256 + - x25519_u64,ristretto255_u64,p256 frontend_feature: - slow-hash - - serialize + - serde steps: - uses: actions/checkout@v2 - uses: hecrj/setup-rust-action@v1 @@ -108,7 +115,6 @@ jobs: profile: minimal toolchain: ${{ matrix.toolchain }} override: true - components: rustfmt, clippy - name: Run expect (which then runs cargo run) run: expect -f scripts/simple_login.exp @@ -132,7 +138,6 @@ jobs: profile: minimal toolchain: ${{ matrix.toolchain }} override: true - components: rustfmt, clippy - name: Run expect (which then runs cargo run) run: expect -f scripts/digital_locker.exp @@ -148,12 +153,13 @@ jobs: # for any no_std target - thumbv6m-none-eabi backend_feature: - - u64_backend - - u32_backend - - p256,u64_backend + - ristretto255_u64 + - ristretto255_u32 + - p256 + - x25519_u64,ristretto255_u64 frontend_feature: - slow-hash - - serialize + - serde steps: - uses: actions/checkout@v2 - uses: hecrj/setup-rust-action@v1 @@ -167,9 +173,10 @@ jobs: fail-fast: false matrix: backend_feature: - - u64_backend - - u32_backend - - p256,u64_backend + - ristretto255_u64 + - ristretto255_u32 + - p256 + - x25519_u64,ristretto255_u64 steps: - name: Checkout sources uses: actions/checkout@v2 @@ -185,7 +192,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: bench - args: --no-default-features --features bench --features ${{ matrix.backend_feature }} --no-run + args: --no-default-features --features ${{ matrix.backend_feature }} --no-run clippy: name: cargo clippy @@ -200,14 +207,21 @@ jobs: profile: minimal toolchain: stable override: true - components: rustfmt, clippy + components: clippy - name: Run cargo clippy uses: actions-rs/cargo@v1 with: command: clippy - args: -- -D warnings + args: --all-targets -- -D warnings + - name: Run cargo doc + uses: actions-rs/cargo@v1 + env: + RUSTDOCFLAGS: -D warnings + with: + command: doc + args: --no-deps --document-private-items --features p256,slow-hash,std format: name: cargo fmt @@ -222,7 +236,7 @@ jobs: profile: minimal toolchain: stable override: true - components: rustfmt, clippy + components: rustfmt - name: Run cargo fmt uses: actions-rs/cargo@v1 diff --git a/Cargo.toml b/Cargo.toml old mode 100644 new mode 100755 index 90db53c..b56addc --- a/Cargo.toml +++ b/Cargo.toml @@ -10,22 +10,33 @@ license = "Apache-2.0 OR MIT" edition = "2018" readme = "README.md" resolver = "2" +rust-version = "1.51" [features] -default = ["u64_backend", "serialize"] +default = ["ristretto255_u64", "serde"] slow-hash = ["argon2"] p256 = ["p256_", "voprf/p256"] -bench = [] -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"] +ristretto255 = [] +ristretto255_u64 = ["curve25519-dalek/u64_backend", "ristretto255", "voprf/ristretto255_u64"] +ristretto255_u32 = ["curve25519-dalek/u32_backend", "ristretto255", "voprf/ristretto255_u32"] +ristretto255_fiat_u64 = ["curve25519-dalek/fiat_u64_backend", "ristretto255", "voprf/ristretto255_fiat_u64"] +ristretto255_fiat_u32 = ["curve25519-dalek/fiat_u32_backend", "ristretto255", "voprf/ristretto255_fiat_u32"] +ristretto255_simd = ["curve25519-dalek/simd_backend", "ristretto255", "voprf/ristretto255_simd"] +x25519 = [] +x25519_u64 = ["x25519", "x25519-dalek/u64_backend"] +x25519_u32 = ["x25519", "x25519-dalek/u32_backend"] +x25519_fiat_u64 = ["x25519", "x25519-dalek/fiat_u64_backend"] +x25519_fiat_u32 = ["x25519", "x25519-dalek/fiat_u32_backend"] +# x25519-dalek isn't properly re-exposing `simd_backend`. +x25519_simd = ["curve25519-dalek/simd_backend", "x25519", "x25519-dalek/nightly"] +std = ["getrandom", "rand/std", "rand/std_rng", "voprf/std"] +serde = ["serde_", "generic-array/serde", "voprf/serde"] [dependencies] argon2 = { version = "0.3", default-features = false, features = ["alloc"], optional = true } -base64 = { version = "0.13", default-features = false, features = ["alloc"], optional = true } constant_time_eq = "0.1" -curve25519-dalek = { version = "3", default-features = false } +curve25519-dalek = { version = "3", default-features = false, optional = true } +derive-where = { version = "1.0.0-rc.1", features = ["zeroize"] } digest = "0.9" displaydoc = { version = "0.2", default-features = false } generic-array = "0.14" @@ -34,9 +45,10 @@ hkdf = "0.11" hmac = "0.11" 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 } +serde_ = { version = "1", package = "serde", default-features = false, features = ["derive"], optional = true } subtle = { version = "2.3", default-features = false } -voprf = { version = "0.2", default-features = false, features = ["danger"] } +voprf = { git = "https://github.com/khonsulabs/voprf", rev = "f8c19eab4ecc9e7a2a5ae26c59661ce797229566", default-features = false, features = ["danger"] } +x25519-dalek = { version = "1", default-features = false, optional = true } zeroize = { version = "1", features = ["zeroize_derive"] } [target.'cfg(target_arch = "wasm32")'.dependencies] @@ -45,7 +57,7 @@ getrandom = { version = "0.2", features = ["js"], optional = true } [dev-dependencies] base64 = "0.13" bincode = "1" -chacha20poly1305 = "0.8" +chacha20poly1305 = "0.9" criterion = "0.3" hex = "0.4" json = "0.12" @@ -55,9 +67,9 @@ serde_json = "1" sha2 = "0.9" proptest = "1" regex = "1" -rustyline = "8" +# Version 9.1 requires an MSRV of 1.56 +rustyline = "~9.0" [[bench]] name = "opaque" harness = false -required-features = ["bench"] diff --git a/benches/opaque.rs b/benches/opaque.rs index 0c64bb7..5009498 100644 --- a/benches/opaque.rs +++ b/benches/opaque.rs @@ -12,16 +12,20 @@ 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")] +#[cfg(feature = "ristretto255_u64")] +static SUFFIX: &str = "ristretto255_u64"; +#[cfg(feature = "ristretto255_u32")] +static SUFFIX: &str = "ristretto255_u32"; +#[cfg(feature = "ristretto255_fiat_u64")] +static SUFFIX: &str = "ristretto255_fiat_u64"; +#[cfg(feature = "ristretto255_fiat_u32")] +static SUFFIX: &str = "ristretto255_fiat_u32"; +#[cfg(all(not(feature = "ristretto255"), feature = "p256"))] static SUFFIX: &str = "p256"; struct Default; -#[cfg(not(feature = "p256"))] +#[cfg(feature = "ristretto255")] impl CipherSuite for Default { type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; @@ -30,10 +34,10 @@ impl CipherSuite for Default { type SlowHash = opaque_ke::slow_hash::NoOpHash; } -#[cfg(feature = "p256")] +#[cfg(not(feature = "ristretto255"))] impl CipherSuite for Default { type OprfGroup = p256_::ProjectivePoint; - type KeGroup = p256_::ProjectivePoint; + type KeGroup = p256_::PublicKey; type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; type Hash = sha2::Sha256; type SlowHash = opaque_ke::slow_hash::NoOpHash; @@ -44,7 +48,7 @@ fn server_setup(c: &mut Criterion) { c.bench_function(&format!("server setup ({})", SUFFIX), move |b| { b.iter(|| { - ServerSetup::::new(&mut rng).unwrap(); + ServerSetup::::new(&mut rng); }) }); } @@ -57,7 +61,7 @@ fn client_registration_start(c: &mut Criterion) { &format!("client registration start ({})", SUFFIX), move |b| { b.iter(|| { - ClientRegistration::::start(&mut rng, &password[..]).unwrap(); + ClientRegistration::::start(&mut rng, password).unwrap(); }) }, ); @@ -67,9 +71,9 @@ 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 server_setup = ServerSetup::::new(&mut rng); let client_registration_start_result = - ClientRegistration::::start(&mut rng, &password[..]).unwrap(); + ClientRegistration::::start(&mut rng, password).unwrap(); c.bench_function( &format!("server registration start ({})", SUFFIX), @@ -78,7 +82,7 @@ fn server_registration_start(c: &mut Criterion) { ServerRegistration::::start( &server_setup, client_registration_start_result.message.clone(), - &username[..], + username, ) .unwrap(); }) @@ -90,13 +94,13 @@ 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 server_setup = ServerSetup::::new(&mut rng); let client_registration_start_result = - ClientRegistration::::start(&mut rng, &password[..]).unwrap(); + ClientRegistration::::start(&mut rng, password).unwrap(); let server_registration_start_result = ServerRegistration::::start( &server_setup, client_registration_start_result.message.clone(), - &username[..], + username, ) .unwrap(); @@ -122,21 +126,20 @@ 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 server_setup = ServerSetup::::new(&mut rng); let client_registration_start_result = - ClientRegistration::::start(&mut rng, &password[..]).unwrap(); + ClientRegistration::::start(&mut rng, password).unwrap(); let server_registration_start_result = ServerRegistration::::start( &server_setup, client_registration_start_result.message.clone(), - &username[..], + username, ) .unwrap(); let client_registration_finish_result = client_registration_start_result - .clone() .state .finish( &mut rng, - server_registration_start_result.message.clone(), + server_registration_start_result.message, ClientRegistrationFinishParameters::default(), ) .unwrap(); @@ -157,7 +160,7 @@ fn client_login_start(c: &mut Criterion) { c.bench_function(&format!("client login start ({})", SUFFIX), move |b| { b.iter(|| { - ClientLogin::::start(&mut rng, &password[..]).unwrap(); + ClientLogin::::start(&mut rng, password).unwrap(); }) }); } @@ -166,26 +169,25 @@ 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 server_setup = ServerSetup::::new(&mut rng); let client_registration_start_result = - ClientRegistration::::start(&mut rng, &password[..]).unwrap(); + ClientRegistration::::start(&mut rng, password).unwrap(); let server_registration_start_result = ServerRegistration::::start( &server_setup, client_registration_start_result.message.clone(), - &username[..], + username, ) .unwrap(); let client_registration_finish_result = client_registration_start_result - .clone() .state .finish( &mut rng, - server_registration_start_result.message.clone(), + server_registration_start_result.message, 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 client_login_start_result = ClientLogin::::start(&mut rng, password).unwrap(); c.bench_function( &format!("server login start (real) ({})", SUFFIX), @@ -196,7 +198,7 @@ fn server_login_start_real(c: &mut Criterion) { &server_setup, Some(password_file.clone()), client_login_start_result.clone().message, - &username[..], + username, ServerLoginStartParameters::default(), ) .unwrap(); @@ -209,8 +211,8 @@ 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(); + let server_setup = ServerSetup::::new(&mut rng); + let client_login_start_result = ClientLogin::::start(&mut rng, password).unwrap(); c.bench_function( &format!("server login start (fake) ({})", SUFFIX), @@ -221,7 +223,7 @@ fn server_login_start_fake(c: &mut Criterion) { &server_setup, None, client_login_start_result.clone().message, - &username[..], + username, ServerLoginStartParameters::default(), ) .unwrap(); @@ -234,32 +236,31 @@ 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 server_setup = ServerSetup::::new(&mut rng); let client_registration_start_result = - ClientRegistration::::start(&mut rng, &password[..]).unwrap(); + ClientRegistration::::start(&mut rng, password).unwrap(); let server_registration_start_result = ServerRegistration::::start( &server_setup, client_registration_start_result.message.clone(), - &username[..], + username, ) .unwrap(); let client_registration_finish_result = client_registration_start_result - .clone() .state .finish( &mut rng, - server_registration_start_result.message.clone(), + server_registration_start_result.message, 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 client_login_start_result = ClientLogin::::start(&mut rng, password).unwrap(); let server_login_start = ServerLogin::start( &mut rng, &server_setup, - Some(password_file.clone()), + Some(password_file), client_login_start_result.clone().message, - &username[..], + username, ServerLoginStartParameters::default(), ) .unwrap(); @@ -282,37 +283,35 @@ 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 server_setup = ServerSetup::::new(&mut rng); let client_registration_start_result = - ClientRegistration::::start(&mut rng, &password[..]).unwrap(); + ClientRegistration::::start(&mut rng, password).unwrap(); let server_registration_start_result = ServerRegistration::::start( &server_setup, client_registration_start_result.message.clone(), - &username[..], + username, ) .unwrap(); let client_registration_finish_result = client_registration_start_result - .clone() .state .finish( &mut rng, - server_registration_start_result.message.clone(), + server_registration_start_result.message, 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 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()), + Some(password_file), client_login_start_result.clone().message, - &username[..], + username, ServerLoginStartParameters::default(), ) .unwrap(); let client_login_finish_result = client_login_start_result - .clone() .state .finish( server_login_start_result.clone().message, diff --git a/examples/digital_locker.rs b/examples/digital_locker.rs index 76b98ba..719a6cb 100644 --- a/examples/digital_locker.rs +++ b/examples/digital_locker.rs @@ -28,6 +28,8 @@ use chacha20poly1305::aead::{Aead, NewAead}; use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce}; +use generic_array::GenericArray; +use opaque_ke::ServerRegistrationLen; use rustyline::error::ReadlineError; use rustyline::Editor; use std::process::exit; @@ -45,6 +47,8 @@ use opaque_ke::{ // that will be used in the OPAQUE protocol #[allow(dead_code)] struct Default; + +#[cfg(feature = "ristretto255")] impl CipherSuite for Default { type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; @@ -53,9 +57,18 @@ impl CipherSuite for Default { type SlowHash = opaque_ke::slow_hash::NoOpHash; } +#[cfg(not(feature = "ristretto255"))] +impl CipherSuite for Default { + type OprfGroup = p256_::ProjectivePoint; + type KeGroup = p256_::PublicKey; + type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; + type Hash = sha2::Sha256; + type SlowHash = opaque_ke::slow_hash::NoOpHash; +} + struct Locker { contents: Vec, - password_file: Vec, + password_file: GenericArray>, } // Given a key and plaintext, produce an AEAD ciphertext along with a nonce @@ -92,22 +105,16 @@ 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() - .unwrap(); + let registration_request_bytes = client_registration_start_result.message.serialize(); // Client sends registration_request_bytes to server let server_registration_start_result = ServerRegistration::::start( - &server_setup, - RegistrationRequest::deserialize(®istration_request_bytes[..]).unwrap(), + server_setup, + RegistrationRequest::deserialize(®istration_request_bytes).unwrap(), &locker_id.to_be_bytes(), ) .unwrap(); - let registration_response_bytes = server_registration_start_result - .message - .serialize() - .unwrap(); + let registration_response_bytes = server_registration_start_result.message.serialize(); // Server sends registration_response_bytes to client @@ -115,14 +122,11 @@ fn register_locker( .state .finish( &mut client_rng, - RegistrationResponse::deserialize(®istration_response_bytes[..]).unwrap(), + RegistrationResponse::deserialize(®istration_response_bytes).unwrap(), ClientRegistrationFinishParameters::default(), ) .unwrap(); - let message_bytes = client_finish_registration_result - .message - .serialize() - .unwrap(); + let message_bytes = client_finish_registration_result.message.serialize(); // Client encrypts secret message using export key let ciphertext = encrypt( @@ -133,12 +137,12 @@ fn register_locker( // Client sends message_bytes to server let password_file = ServerRegistration::finish( - RegistrationUpload::::deserialize(&message_bytes[..]).unwrap(), + RegistrationUpload::::deserialize(&message_bytes).unwrap(), ); Locker { contents: ciphertext, - password_file: password_file.serialize().unwrap(), + password_file: password_file.serialize(), } } @@ -152,28 +156,27 @@ 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().unwrap(); + let credential_request_bytes = client_login_start_result.message.serialize(); // Client sends credential_request_bytes to server - let password_file = - ServerRegistration::::deserialize(&locker.password_file[..]).unwrap(); + let password_file = ServerRegistration::::deserialize(&locker.password_file).unwrap(); let mut server_rng = OsRng; let server_login_start_result = ServerLogin::start( &mut server_rng, - &server_setup, + server_setup, Some(password_file), - CredentialRequest::deserialize(&credential_request_bytes[..]).unwrap(), + CredentialRequest::deserialize(&credential_request_bytes).unwrap(), &locker_id.to_be_bytes(), ServerLoginStartParameters::default(), ) .unwrap(); - let credential_response_bytes = server_login_start_result.message.serialize().unwrap(); + let credential_response_bytes = server_login_start_result.message.serialize(); // Server sends credential_response_bytes to client let result = client_login_start_result.state.finish( - CredentialResponse::deserialize(&credential_response_bytes[..]).unwrap(), + CredentialResponse::deserialize(&credential_response_bytes).unwrap(), ClientLoginFinishParameters::default(), ); @@ -182,13 +185,13 @@ 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().unwrap(); + let credential_finalization_bytes = client_login_finish_result.message.serialize(); // Client sends credential_finalization_bytes to server let server_login_finish_result = server_login_start_result .state - .finish(CredentialFinalization::deserialize(&credential_finalization_bytes[..]).unwrap()) + .finish(CredentialFinalization::deserialize(&credential_finalization_bytes).unwrap()) .unwrap(); // Server sends locker contents, encrypted under the session key, to the client @@ -208,7 +211,7 @@ fn open_locker( fn main() { let mut rng = OsRng; - let server_setup = ServerSetup::::new(&mut rng).unwrap(); + let server_setup = ServerSetup::::new(&mut rng); let mut rl = Editor::<()>::new(); let mut registered_lockers: Vec = vec![]; @@ -292,7 +295,7 @@ fn main() { // Helper functions -fn display_lockers(lockers: &Vec) { +fn display_lockers(lockers: &[Locker]) { let mut locker_numbers = vec![]; for (i, _) in lockers.iter().enumerate() { locker_numbers.push(i); diff --git a/examples/simple_login.rs b/examples/simple_login.rs index bd06604..5e2ef3f 100644 --- a/examples/simple_login.rs +++ b/examples/simple_login.rs @@ -22,6 +22,7 @@ //! messages over "the wire" to the server. These bytes are serialized //! and explicitly annotated in the below functions. +use generic_array::GenericArray; use rustyline::error::ReadlineError; use rustyline::Editor; use std::collections::HashMap; @@ -31,13 +32,16 @@ use opaque_ke::{ ciphersuite::CipherSuite, rand::rngs::OsRng, ClientLogin, ClientLoginFinishParameters, ClientRegistration, ClientRegistrationFinishParameters, CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse, - RegistrationUpload, ServerLogin, ServerLoginStartParameters, ServerRegistration, ServerSetup, + RegistrationUpload, ServerLogin, ServerLoginStartParameters, ServerRegistration, + ServerRegistrationLen, ServerSetup, }; // The ciphersuite trait allows to specify the underlying primitives // that will be used in the OPAQUE protocol #[allow(dead_code)] struct Default; + +#[cfg(feature = "ristretto255")] impl CipherSuite for Default { type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; @@ -46,32 +50,35 @@ impl CipherSuite for Default { type SlowHash = opaque_ke::slow_hash::NoOpHash; } +#[cfg(not(feature = "ristretto255"))] +impl CipherSuite for Default { + type OprfGroup = p256_::ProjectivePoint; + type KeGroup = p256_::PublicKey; + type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; + type Hash = sha2::Sha256; + type SlowHash = opaque_ke::slow_hash::NoOpHash; +} + // Password-based registration between a client and server fn account_registration( server_setup: &ServerSetup, username: String, password: String, -) -> Vec { +) -> GenericArray> { 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() - .unwrap(); + let registration_request_bytes = client_registration_start_result.message.serialize(); // Client sends registration_request_bytes to server let server_registration_start_result = ServerRegistration::::start( - &server_setup, - RegistrationRequest::deserialize(®istration_request_bytes[..]).unwrap(), + server_setup, + RegistrationRequest::deserialize(®istration_request_bytes).unwrap(), username.as_bytes(), ) .unwrap(); - let registration_response_bytes = server_registration_start_result - .message - .serialize() - .unwrap(); + let registration_response_bytes = server_registration_start_result.message.serialize(); // Server sends registration_response_bytes to client @@ -79,21 +86,18 @@ fn account_registration( .state .finish( &mut client_rng, - RegistrationResponse::deserialize(®istration_response_bytes[..]).unwrap(), + RegistrationResponse::deserialize(®istration_response_bytes).unwrap(), ClientRegistrationFinishParameters::default(), ) .unwrap(); - let message_bytes = client_finish_registration_result - .message - .serialize() - .unwrap(); + let message_bytes = client_finish_registration_result.message.serialize(); // Client sends message_bytes to server let password_file = ServerRegistration::finish( - RegistrationUpload::::deserialize(&message_bytes[..]).unwrap(), + RegistrationUpload::::deserialize(&message_bytes).unwrap(), ); - password_file.serialize().unwrap() + password_file.serialize() } // Password-based login between a client and server @@ -106,7 +110,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().unwrap(); + let credential_request_bytes = client_login_start_result.message.serialize(); // Client sends credential_request_bytes to server @@ -114,19 +118,19 @@ fn account_login( let mut server_rng = OsRng; let server_login_start_result = ServerLogin::start( &mut server_rng, - &server_setup, + server_setup, Some(password_file), - CredentialRequest::deserialize(&credential_request_bytes[..]).unwrap(), + CredentialRequest::deserialize(&credential_request_bytes).unwrap(), username.as_bytes(), ServerLoginStartParameters::default(), ) .unwrap(); - let credential_response_bytes = server_login_start_result.message.serialize().unwrap(); + let credential_response_bytes = server_login_start_result.message.serialize(); // Server sends credential_response_bytes to client let result = client_login_start_result.state.finish( - CredentialResponse::deserialize(&credential_response_bytes[..]).unwrap(), + CredentialResponse::deserialize(&credential_response_bytes).unwrap(), ClientLoginFinishParameters::default(), ); @@ -135,13 +139,13 @@ fn account_login( return false; } let client_login_finish_result = result.unwrap(); - let credential_finalization_bytes = client_login_finish_result.message.serialize().unwrap(); + let credential_finalization_bytes = client_login_finish_result.message.serialize(); // Client sends credential_finalization_bytes to server let server_login_finish_result = server_login_start_result .state - .finish(CredentialFinalization::deserialize(&credential_finalization_bytes[..]).unwrap()) + .finish(CredentialFinalization::deserialize(&credential_finalization_bytes).unwrap()) .unwrap(); client_login_finish_result.session_key == server_login_finish_result.session_key @@ -149,10 +153,11 @@ fn account_login( fn main() { let mut rng = OsRng; - let server_setup = ServerSetup::::new(&mut rng).unwrap(); + let server_setup = ServerSetup::::new(&mut rng); let mut rl = Editor::<()>::new(); - let mut registered_users = HashMap::>::new(); + let mut registered_users = + HashMap::>>::new(); loop { println!( "\nCurrently registered usernames: {:?}\n", diff --git a/src/envelope.rs b/src/envelope.rs old mode 100644 new mode 100755 index 94f1066..53073f6 --- a/src/envelope.rs +++ b/src/envelope.rs @@ -12,12 +12,17 @@ use crate::{ key_exchange::group::KeGroup, keypair::{KeyPair, PublicKey}, opaque::{bytestrings_from_identifiers, Identifiers}, + serialization::{MacExt, Serialize}, }; -use alloc::vec; -use alloc::vec::Vec; use core::convert::TryFrom; -use digest::Digest; -use generic_array::{typenum::Unsigned, GenericArray}; +use core::ops::Add; +use derive_where::DeriveWhere; +use digest::{Digest, FixedOutput}; +use generic_array::{ + sequence::Concat, + typenum::{Sum, Unsigned, U2, U32}, + ArrayLength, GenericArray, +}; use hkdf::Hkdf; use hmac::{Hmac, Mac, NewMac}; use rand::{CryptoRng, RngCore}; @@ -25,13 +30,13 @@ use voprf::group::Group; use zeroize::Zeroize; // Constant string used as salt for HKDF computation -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; +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"; +type NonceLen = U32; -#[derive(Clone, Debug, Eq, Hash, PartialEq, Zeroize)] +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize)] #[zeroize(drop)] pub(crate) enum InnerEnvelopeMode { Zero = 0, @@ -49,42 +54,31 @@ impl TryFrom for InnerEnvelopeMode { } /// This struct is an instantiation of the envelope as described in -/// https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-06#section-4 +/// /// /// Note that earlier versions of this specification described an /// implementation of this envelope using an encryption scheme that /// satisfied random-key robustness -/// (https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-05#section-4). +/// (). /// The specification update has simplified this assumption by taking /// an XOR-based approach without compromising on security, and to avoid /// the confusion around the implementation of an RKR-secure encryption. +#[derive(DeriveWhere)] +#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))] pub(crate) struct Envelope { mode: InnerEnvelopeMode, - nonce: Vec, + nonce: GenericArray, hmac: GenericArray::OutputSize>, } -// Cannot be derived because it would require for CS to be Clone. -impl Clone for Envelope { - fn clone(&self) -> Self { - Self { - mode: self.mode.clone(), - nonce: self.nonce.clone(), - hmac: self.hmac.clone(), - } - } -} - -impl_debug_eq_hash_for!(struct Envelope, [mode, nonce, hmac]); - // Note that this struct represents an envelope that has been "opened" with the asssociated // key. This key is also used to derive the export_key parameter, which is technically // unrelated to the envelope's encrypted and authenticated contents. -pub(crate) struct OpenedEnvelope { +pub(crate) struct OpenedEnvelope<'a, CS: CipherSuite> { pub(crate) client_static_keypair: KeyPair, pub(crate) export_key: GenericArray::OutputSize>, - pub(crate) id_u: Vec, - pub(crate) id_s: Vec, + pub(crate) id_u: Serialize<'a, U2, ::PkLen>, + pub(crate) id_s: Serialize<'a, U2, ::PkLen>, } pub(crate) struct OpenedInnerEnvelope { @@ -100,7 +94,7 @@ type SealRawResult = ( type SealRawResult = ( Envelope, GenericArray::Hash as Digest>::OutputSize>, - Vec, + GenericArray::Hash as Digest>::OutputSize>, ); #[cfg(not(test))] type SealResult = ( @@ -113,30 +107,36 @@ type SealResult = ( Envelope, PublicKey<::KeGroup>, GenericArray::Hash as Digest>::OutputSize>, - Vec, + GenericArray::Hash as Digest>::OutputSize>, ); +#[allow(type_alias_bounds)] +pub(crate) type EnvelopeLen = Sum::OutputSize>; + impl Envelope { #[allow(clippy::type_complexity)] pub(crate) fn seal( rng: &mut R, randomized_pwd_hasher: Hkdf, - server_s_pk: &[u8], - optional_ids: Option, + server_s_pk: &PublicKey, + ids: Identifiers, ) -> Result, ProtocolError> { - let mut nonce = vec![0u8; NONCE_LEN]; + let mut nonce = GenericArray::default(); rng.fill_bytes(&mut nonce); let (mode, client_s_pk) = ( InnerEnvelopeMode::Internal, - build_inner_envelope_internal::(randomized_pwd_hasher.clone(), &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 (id_u, id_s) = bytestrings_from_identifiers::( + ids, + client_s_pk.to_arr(), + server_s_pk.to_arr(), + )?; + let aad = construct_aad(id_u.iter(), id_s.iter(), server_s_pk); - let result = Self::seal_raw(randomized_pwd_hasher, &nonce, &aad, mode)?; + let result = Self::seal_raw(randomized_pwd_hasher, nonce, aad, mode)?; Ok(( result.0, client_s_pk, @@ -149,64 +149,64 @@ impl Envelope { /// Uses a key to convert the plaintext into an envelope, authenticated by the aad field. /// Note that a new nonce is sampled for each call to seal. #[allow(clippy::type_complexity)] - pub(crate) fn seal_raw( + pub(crate) fn seal_raw<'a>( randomized_pwd_hasher: Hkdf, - nonce: &[u8], - aad: &[u8], + nonce: GenericArray, + aad: impl Iterator, mode: InnerEnvelopeMode, ) -> Result, InternalError> { - let mut hmac_key = vec![0u8; Self::hmac_key_size()]; - let mut export_key = vec![0u8; Self::export_key_size()]; + let mut hmac_key = GenericArray::<_, ::OutputSize>::default(); + let mut export_key = GenericArray::<_, ::OutputSize>::default(); randomized_pwd_hasher - .expand(&[nonce, STR_AUTH_KEY].concat(), &mut hmac_key) + .expand_multi_info(&[&nonce, &STR_AUTH_KEY], &mut hmac_key) .map_err(|_| InternalError::HkdfError)?; randomized_pwd_hasher - .expand(&[nonce, STR_EXPORT_KEY].concat(), &mut export_key) + .expand_multi_info(&[&nonce, &STR_EXPORT_KEY], &mut export_key) .map_err(|_| InternalError::HkdfError)?; let mut hmac = Hmac::::new_from_slice(&hmac_key).map_err(|_| InternalError::HmacError)?; - hmac.update(nonce); - hmac.update(aad); + hmac.update(&nonce); + hmac.update_iter(aad); let hmac_bytes = hmac.finalize().into_bytes(); Ok(( Self { mode, - nonce: nonce.to_vec(), + nonce, hmac: hmac_bytes, }, - GenericArray::clone_from_slice(&export_key), + export_key, #[cfg(test)] hmac_key, )) } - pub(crate) fn open( + pub(crate) fn open<'a>( &self, randomized_pwd_hasher: Hkdf, - server_s_pk: &[u8], - optional_ids: &Option, - ) -> Result, ProtocolError> { + server_s_pk: PublicKey, + optional_ids: Identifiers<'a>, + ) -> Result, ProtocolError> { let client_static_keypair = match self.mode { InnerEnvelopeMode::Zero => { return Err(InternalError::IncompatibleEnvelopeModeError.into()) } InnerEnvelopeMode::Internal => { - recover_keys_internal::(randomized_pwd_hasher.clone(), &self.nonce)? + recover_keys_internal::(randomized_pwd_hasher.clone(), self.nonce)? } }; - let (id_u, id_s) = bytestrings_from_identifiers( + let (id_u, id_s) = bytestrings_from_identifiers::( optional_ids, - &client_static_keypair.public().to_arr(), - server_s_pk, + client_static_keypair.public().to_arr(), + server_s_pk.to_arr(), )?; - let aad = construct_aad(&id_u, &id_s, server_s_pk); + let aad = construct_aad(id_u.iter(), id_s.iter(), &server_s_pk); - let opened = self.open_raw(randomized_pwd_hasher, &aad)?; + let opened = self.open_raw(randomized_pwd_hasher, aad)?; Ok(OpenedEnvelope { client_static_keypair, @@ -218,51 +218,37 @@ impl Envelope { /// Attempts to decrypt the envelope using a key, which is successful only if the key and /// aad used to construct the envelope are the same. - pub(crate) fn open_raw( + pub(crate) fn open_raw<'a>( &self, randomized_pwd_hasher: Hkdf, - aad: &[u8], + aad: impl Iterator, ) -> Result, InternalError> { - let mut hmac_key = vec![0u8; Self::hmac_key_size()]; - let mut export_key = vec![0u8; Self::export_key_size()]; + let mut hmac_key = GenericArray::<_, ::OutputSize>::default(); + let mut export_key = GenericArray::<_, ::OutputSize>::default(); randomized_pwd_hasher - .expand( - &[self.nonce.clone(), STR_AUTH_KEY.to_vec()].concat(), - &mut hmac_key, - ) + .expand(&self.nonce.concat(STR_AUTH_KEY.into()), &mut hmac_key) .map_err(|_| InternalError::HkdfError)?; randomized_pwd_hasher - .expand( - &[self.nonce.clone(), STR_EXPORT_KEY.to_vec()].concat(), - &mut export_key, - ) + .expand(&self.nonce.concat(STR_EXPORT_KEY.into()), &mut export_key) .map_err(|_| InternalError::HkdfError)?; let mut hmac = Hmac::::new_from_slice(&hmac_key).map_err(|_| InternalError::HmacError)?; hmac.update(&self.nonce); - hmac.update(aad); - if hmac.verify(&self.hmac).is_err() { - return Err(InternalError::SealOpenHmacError); - } + hmac.update_iter(aad); + hmac.verify(&self.hmac) + .map_err(|_| InternalError::SealOpenHmacError)?; - Ok(OpenedInnerEnvelope { - export_key: GenericArray::::OutputSize>::clone_from_slice( - &export_key, - ), - }) + Ok(OpenedInnerEnvelope { export_key }) } // Creates a dummy envelope object that serializes to the all-zeros byte string pub(crate) fn dummy() -> Self { Self { mode: InnerEnvelopeMode::Zero, - nonce: vec![0u8; NONCE_LEN], - hmac: GenericArray::clone_from_slice(&vec![ - 0u8; - ::OutputSize::USIZE - ]), + nonce: GenericArray::default(), + hmac: GenericArray::default(), } } @@ -270,34 +256,36 @@ impl Envelope { ::OutputSize::USIZE } - fn export_key_size() -> usize { - ::OutputSize::USIZE - } - pub(crate) fn len() -> usize { - ::OutputSize::USIZE + NONCE_LEN + ::OutputSize::USIZE + NonceLen::USIZE } - pub(crate) fn serialize(&self) -> Vec { - [&self.nonce[..], &self.hmac[..]].concat() + pub(crate) fn serialize(&self) -> GenericArray> + where + // Envelope: Nonce + Hash + NonceLen: Add<::OutputSize>, + EnvelopeLen: ArrayLength, + { + self.nonce.concat(self.hmac.clone()) } + pub(crate) fn deserialize(bytes: &[u8]) -> Result { let mode = InnerEnvelopeMode::Internal; // Better way to hard-code this? - if bytes.len() < NONCE_LEN { + if bytes.len() < NonceLen::USIZE { return Err(ProtocolError::SerializationError); } - let nonce = bytes[..NONCE_LEN].to_vec(); + let nonce = GenericArray::clone_from_slice(&bytes[..NonceLen::USIZE]); let remainder = match mode { InnerEnvelopeMode::Zero => { return Err(InternalError::IncompatibleEnvelopeModeError.into()) } - InnerEnvelopeMode::Internal => bytes[NONCE_LEN..].to_vec(), + InnerEnvelopeMode::Internal => &bytes[NonceLen::USIZE..], }; let hmac_key_size = Self::hmac_key_size(); - let hmac = check_slice_size(&remainder, hmac_key_size, "hmac_key_size")?; + let hmac = check_slice_size(remainder, hmac_key_size, "hmac_key_size")?; Ok(Self { mode, @@ -307,35 +295,20 @@ impl Envelope { } } -// This can't be derived because of the use of a phantom parameter -impl Zeroize for Envelope { - fn zeroize(&mut self) { - self.mode.zeroize(); - self.nonce.zeroize(); - self.hmac.zeroize(); - } -} - -impl Drop for Envelope { - fn drop(&mut self) { - self.zeroize(); - } -} - // Helper functions fn build_inner_envelope_internal( randomized_pwd_hasher: Hkdf, - nonce: &[u8], + nonce: GenericArray, ) -> Result, ProtocolError> { - let mut keypair_seed = vec![0u8; ::SkLen::USIZE]; + let mut keypair_seed = GenericArray::<_, ::SkLen>::default(); randomized_pwd_hasher - .expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed) + .expand(&nonce.concat(STR_PRIVATE_KEY.into()), &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::( - Some(&keypair_seed[..]), - GenericArray::from(*STR_OPAQUE_DERIVE_AUTH_KEY_PAIR), + Some(keypair_seed.as_slice()), + GenericArray::from(STR_OPAQUE_DERIVE_AUTH_KEY_PAIR), )?), )?; @@ -344,22 +317,26 @@ fn build_inner_envelope_internal( fn recover_keys_internal( randomized_pwd_hasher: Hkdf, - nonce: &[u8], + nonce: GenericArray, ) -> Result, ProtocolError> { - let mut keypair_seed = vec![0u8; ::SkLen::USIZE]; + let mut keypair_seed = GenericArray::<_, ::SkLen>::default(); randomized_pwd_hasher - .expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed) + .expand(&nonce.concat(STR_PRIVATE_KEY.into()), &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::( - Some(&keypair_seed[..]), - GenericArray::from(*STR_OPAQUE_DERIVE_AUTH_KEY_PAIR), + Some(keypair_seed.as_slice()), + GenericArray::from(STR_OPAQUE_DERIVE_AUTH_KEY_PAIR), )?), )?; Ok(client_static_keypair) } -fn construct_aad(id_u: &[u8], id_s: &[u8], server_s_pk: &[u8]) -> Vec { - [server_s_pk, id_s, id_u].concat() +fn construct_aad<'a>( + id_u: impl Iterator, + id_s: impl Iterator, + server_s_pk: &'a [u8], +) -> impl Iterator { + chain!(Some(server_s_pk).into_iter(), id_s, id_u) } diff --git a/src/errors.rs b/src/errors.rs index 18bfeee..36810fe 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -14,7 +14,7 @@ use std::error::Error; use displaydoc::Display; /// Represents an error in the manipulation of internal cryptographic data -#[derive(Clone, Display, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Display, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum InternalError { /// Custom [`SecretKey`](crate::keypair::SecretKey) error type Custom(T), @@ -129,7 +129,7 @@ impl From for ProtocolError { } /// Represents an error in protocol handling -#[derive(Clone, Display, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Display, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum ProtocolError { /// Internal error encountered LibraryError(InternalError), diff --git a/src/impls.rs b/src/impls.rs old mode 100644 new mode 100755 index d54ac5a..6489d5f --- a/src/impls.rs +++ b/src/impls.rs @@ -5,139 +5,67 @@ // License, Version 2.0 found in the LICENSE-APACHE file in the root directory // of this source tree. -macro_rules! impl_debug_eq_hash_for { - (struct $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:ident$(, $field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => { - impl$(<$($gen$(: $bound)?),+>)? core::fmt::Debug for $name$(<$($gen),+>)? - $(where $($type: core::fmt::Debug,)+)? - { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("$name") - .field("$field1", &self.$field1) - $(.field("$field2", &self.$field2))* - .finish() - } - } - - impl$(<$($gen$(: $bound)?),+>)? Eq for $name$(<$($gen),+>)? - $(where $($type: Eq,)+)? - {} - - impl$(<$($gen$(: $bound)?),+>)? PartialEq for $name$(<$($gen),+>)? - $(where $($type: PartialEq,)+)? - { - fn eq(&self, other: &Self) -> bool { - PartialEq::eq(&self.$field1, &other.$field1) - $(&& PartialEq::eq(&self.$field2, &other.$field2))* - } - } - - impl$(<$($gen$(: $bound)?),+>)? core::hash::Hash for $name$(<$($gen),+>)? - $(where $($type: core::hash::Hash,)+)? - { - fn hash(&self, state: &mut H) { - core::hash::Hash::hash(&self.$field1, state); - $(core::hash::Hash::hash(&self.$field2, state);)* - } - } - }; - (tuple $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:tt$(, $field2:tt)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => { - impl$(<$($gen$(: $bound)?),+>)? core::fmt::Debug for $name$(<$($gen),+>)? - $(where $($type: core::fmt::Debug,)+)? - { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_tuple("$name") - .field(&self.$field1) - $(.field(&self.$field2))* - .finish() - } - } - - impl$(<$($gen$(: $bound)?),+>)? Eq for $name$(<$($gen),+>)? - $(where $($type: Eq,)+)? - {} - - impl$(<$($gen$(: $bound)?),+>)? PartialEq for $name$(<$($gen),+>)? - $(where $($type: PartialEq,)+)? - { - fn eq(&self, other: &Self) -> bool { - PartialEq::eq(&self.$field1, &other.$field1) - $(&& PartialEq::eq(&self.$field2, &other.$field2))* - } - } - - impl$(<$($gen$(: $bound)?),+>)? core::hash::Hash for $name$(<$($gen),+>)? - $(where $($type: core::hash::Hash,)+)? - { - fn hash(&self, state: &mut H) { - core::hash::Hash::hash(&self.$field1, state); - $(core::hash::Hash::hash(&self.$field2, state);)* - } - } - }; -} - -macro_rules! impl_clone_for { - (struct $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:ident$(, $field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => { - impl$(<$($gen$(: $bound)?),+>)? Clone for $name$(<$($gen),+>)? - $(where $($type: Clone,)+)? - { - fn clone(&self) -> Self { - Self { - $field1: self.$field1.clone(), - $($field2: self.$field2.clone(),)* - } - } - } - }; - (tuple $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:tt$(, $field2:tt)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => { - impl$(<$($gen$(: $bound)?),+>)? Clone for $name$(<$($gen),+>)? - $(where $($type: Clone,)+)? - { - fn clone(&self) -> Self { - Self( - self.$field1.clone(), - $(self.$field2.clone(),)* - ) - } - } - }; -} - -/// Inner macro used for deriving `serde`'s `Serialize` and `Deserialize` traits. +/// Macro used for deriving `serde`'s `Serialize` and `Deserialize` traits. macro_rules! impl_serialize_and_deserialize_for { - ($t:ident) => { - #[cfg(feature = "serialize")] - impl serde::Serialize for $t { + ($item:ident$( where $($path:ty: $bound1:path $(| $bound2:path)*),+$(,)?)?$(; $error:expr)?) => { + #[cfg(feature = "serde")] + impl serde_::Serialize for $item + $(where + $($path: $bound1 $(+ $bound2)*),+ + )? + { fn serialize(&self, serializer: S) -> Result where - S: serde::Serializer, + S: serde_::Serializer, { - use serde::ser::Error; - - if serializer.is_human_readable() { - serializer - .serialize_str(&base64::encode(&self.serialize().map_err(Error::custom)?)) - } else { - serializer.serialize_bytes(&self.serialize().map_err(Error::custom)?) - } + serializer.serialize_bytes(&self.serialize()$(.map_err($error)?)?) } } - #[cfg(feature = "serialize")] - impl<'de, CS: CipherSuite> serde::Deserialize<'de> for $t { + #[cfg(feature = "serde")] + impl<'de, CS: CipherSuite> serde_::Deserialize<'de> for $item + { fn deserialize(deserializer: D) -> Result where - D: serde::Deserializer<'de>, + D: serde_::Deserializer<'de>, { - use serde::de::Error; + use serde_::de::Error; - if deserializer.is_human_readable() { - let s = <&str>::deserialize(deserializer)?; - Self::deserialize(&base64::decode(s).map_err(Error::custom)?) - } else { - Self::deserialize(<&[u8]>::deserialize(deserializer)?) + struct ByteVisitor(core::marker::PhantomData); + + impl<'de, CS: CipherSuite> serde_::de::Visitor<'de> for ByteVisitor + { + type Value = $item; + + 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: Error, + { + $item::::deserialize(value).map_err(|_| { + Error::invalid_value( + serde_::de::Unexpected::Bytes(value), + &core::concat!( + "invalid byte sequence for ", + core::stringify!($t) + ), + ) + }) + } } - .map_err(Error::custom) + + deserializer + .deserialize_bytes(ByteVisitor::(core::marker::PhantomData)) + .map_err(Error::custom) } } }; diff --git a/src/key_exchange/group/mod.rs b/src/key_exchange/group/mod.rs index e7d7c22..3fcf5df 100644 --- a/src/key_exchange/group/mod.rs +++ b/src/key_exchange/group/mod.rs @@ -37,5 +37,7 @@ pub trait KeGroup: Sized + Clone { #[cfg(feature = "p256")] pub mod p256; +#[cfg(feature = "ristretto255")] pub mod ristretto255; +#[cfg(feature = "x25519")] pub mod x25519; diff --git a/src/key_exchange/group/p256.rs b/src/key_exchange/group/p256.rs index d46dcad..9f53e18 100644 --- a/src/key_exchange/group/p256.rs +++ b/src/key_exchange/group/p256.rs @@ -11,39 +11,39 @@ use super::KeGroup; use crate::errors::InternalError; use generic_array::typenum::{U32, U33}; use generic_array::GenericArray; +use p256_::elliptic_curve::group::GroupEncoding; +use p256_::elliptic_curve::sec1::ToEncodedPoint; +use p256_::elliptic_curve::{PublicKey, SecretKey}; +use p256_::NistP256; use rand::{CryptoRng, RngCore}; -impl KeGroup for p256_::ProjectivePoint { +impl KeGroup for PublicKey { 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) + Self::from_sec1_bytes(element_bits).map_err(|_| InternalError::PointError) } fn random_sk(rng: &mut R) -> GenericArray { - use p256_::elliptic_curve::Field; - - p256_::Scalar::random(rng).into() + SecretKey::::random(rng).to_bytes() } fn public_key(sk: &GenericArray) -> Self { - Self::generator() * p256_::Scalar::from_bytes_reduced(sk) + SecretKey::::from_bytes(sk).unwrap().public_key() } 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 + GenericArray::clone_from_slice(self.to_encoded_point(true).as_bytes()) } fn diffie_hellman(&self, sk: &GenericArray) -> GenericArray { - (self * &p256_::Scalar::from_bytes_reduced(sk)).to_arr() + (self.to_projective() + * SecretKey::::from_bytes(sk) + .unwrap() + .to_secret_scalar() + .as_ref()) + .to_affine() + .to_bytes() } } diff --git a/src/key_exchange/group/ristretto255.rs b/src/key_exchange/group/ristretto255.rs index dcb4820..0121894 100644 --- a/src/key_exchange/group/ristretto255.rs +++ b/src/key_exchange/group/ristretto255.rs @@ -59,7 +59,7 @@ impl KeGroup for RistrettoPoint { self.compress().to_bytes().into() } - fn diffie_hellman(&self, sk: &GenericArray) -> GenericArray { + 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 index fc4f67e..07070ab 100644 --- a/src/key_exchange/group/x25519.rs +++ b/src/key_exchange/group/x25519.rs @@ -5,152 +5,47 @@ // License, Version 2.0 found in the LICENSE-APACHE file in the root directory // of this source tree. -//! Key Exchange group implementation for x25519 +//! 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}; +use x25519_dalek::{PublicKey, StaticSecret}; /// The implementation of such a subgroup for Ristretto -impl KeGroup for MontgomeryPoint { +impl KeGroup for PublicKey { type PkLen = U32; type SkLen = U32; fn from_pk_slice(element_bits: &GenericArray) -> Result { - Ok(Self(*element_bits.as_ref())) + Ok(Self::from(<[u8; 32]>::from(*element_bits))) } fn random_sk(rng: &mut R) -> GenericArray { + let mut scalar_bytes = [0u8; 32]; + 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) - } + rng.fill_bytes(&mut 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()); + if scalar_bytes != [0u8; 32] { + break StaticSecret::from(scalar_bytes).to_bytes().into(); } } } fn public_key(sk: &GenericArray) -> Self { - X25519_BASEPOINT * Scalar::from_bits(*sk.as_ref()) + Self::from(&StaticSecret::from(<[u8; 32]>::from(*sk))) } 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(()) + fn diffie_hellman(&self, sk: &GenericArray) -> GenericArray { + StaticSecret::from(<[u8; 32]>::from(*sk)) + .diffie_hellman(self) + .to_bytes() + .into() } } diff --git a/src/key_exchange/traits.rs b/src/key_exchange/traits.rs old mode 100644 new mode 100755 index 239567f..8fdb669 --- a/src/key_exchange/traits.rs +++ b/src/key_exchange/traits.rs @@ -12,7 +12,8 @@ use crate::{ hash::Hash, keypair::{PrivateKey, PublicKey, SecretKey}, }; -use alloc::vec::Vec; +use digest::Digest; +use generic_array::{ArrayLength, GenericArray}; use rand::{CryptoRng, RngCore}; use zeroize::Zeroize; @@ -25,23 +26,26 @@ pub type GenerateKe2Result = ( pub type GenerateKe2Result = ( >::KE2State, >::KE2Message, - Vec, - generic_array::GenericArray::OutputSize>, + GenericArray::OutputSize>, + GenericArray::OutputSize>, ); #[cfg(not(test))] -pub type GenerateKe3Result = (Vec, >::KE3Message); +pub type GenerateKe3Result = ( + GenericArray::OutputSize>, + >::KE3Message, +); #[cfg(test)] pub type GenerateKe3Result = ( - Vec, + GenericArray::OutputSize>, >::KE3Message, - Vec, - generic_array::GenericArray::OutputSize>, + GenericArray::OutputSize>, + GenericArray::OutputSize>, ); pub trait KeyExchange { type KE1State: FromBytes + ToBytes + Zeroize + Clone; type KE2State: FromBytes + ToBytes + Zeroize + Clone; - type KE1Message: FromBytes + ToBytes + Clone; + type KE1Message: FromBytes + ToBytes + Zeroize + Clone; type KE2Message: FromBytes + ToBytes + Clone; type KE3Message: FromBytes + ToBytes + Clone; @@ -50,44 +54,63 @@ pub trait KeyExchange { ) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError>; #[allow(clippy::too_many_arguments, clippy::type_complexity)] - fn generate_ke2>( + fn generate_ke2<'a, 'b, 'c, 'd, R: RngCore + CryptoRng, S: SecretKey>( rng: &mut R, - l1_bytes: Vec, - l2_bytes: Vec, + l1_bytes: impl Iterator, + l2_bytes: impl Iterator, ke1_message: Self::KE1Message, client_s_pk: PublicKey, server_s_sk: S, - id_u: Vec, - id_s: Vec, - context: Vec, + id_u: impl Iterator, + id_s: impl Iterator, + context: &[u8], ) -> Result, ProtocolError>; #[allow(clippy::too_many_arguments, clippy::type_complexity)] - fn generate_ke3( - l2_component: Vec, + fn generate_ke3<'a, 'b, 'c, 'd>( + l2_component: impl Iterator, ke2_message: Self::KE2Message, ke1_state: &Self::KE1State, - serialized_credential_request: &[u8], + serialized_credential_request: impl Iterator, server_s_pk: PublicKey, client_s_sk: PrivateKey, - id_u: Vec, - id_s: Vec, - context: Vec, + id_u: impl Iterator, + id_s: impl Iterator, + context: &[u8], ) -> Result, ProtocolError>; #[allow(clippy::type_complexity)] fn finish_ke( ke3_message: Self::KE3Message, ke2_state: &Self::KE2State, - ) -> Result, ProtocolError>; + ) -> Result, ProtocolError>; fn ke2_message_size() -> usize; } pub trait FromBytes: Sized { - fn from_bytes(input: &[u8]) -> Result; + fn from_bytes(input: &[u8]) -> Result; } pub trait ToBytes { - fn to_bytes(&self) -> Vec; + type Len: ArrayLength; + + fn to_bytes(&self) -> GenericArray; } + +#[allow(dead_code, type_alias_bounds)] +pub type Ke1StateLen = + <>::KE1State as ToBytes>::Len; +#[allow(type_alias_bounds)] +pub type Ke1MessageLen = + <>::KE1Message as ToBytes>::Len; +#[allow(type_alias_bounds)] +#[allow(type_alias_bounds)] +pub type Ke2StateLen = + <>::KE2State as ToBytes>::Len; +#[allow(type_alias_bounds)] +pub type Ke2MessageLen = + <>::KE2Message as ToBytes>::Len; +#[allow(type_alias_bounds)] +pub type Ke3MessageLen = + <>::KE3Message as ToBytes>::Len; diff --git a/src/key_exchange/tripledh.rs b/src/key_exchange/tripledh.rs old mode 100644 new mode 100755 index f15801b..bd8f0df --- a/src/key_exchange/tripledh.rs +++ b/src/key_exchange/tripledh.rs @@ -7,7 +7,6 @@ //! An implementation of the Triple Diffie-Hellman key exchange protocol use crate::{ - ciphersuite::CipherSuite, errors::{ utils::{check_slice_size, check_slice_size_atleast}, InternalError, ProtocolError, @@ -18,20 +17,21 @@ use crate::{ traits::{FromBytes, GenerateKe2Result, GenerateKe3Result, KeyExchange, ToBytes}, }, keypair::{KeyPair, PrivateKey, PublicKey, SecretKey}, - serialization::serialize, + serialization::{Serialize, UpdateExt}, }; -use alloc::vec; -use alloc::vec::Vec; +use core::array::IntoIter; use core::convert::TryFrom; +use core::ops::Add; +use derive_where::DeriveWhere; use digest::{Digest, FixedOutput}; +use generic_array::sequence::Concat; use generic_array::{ - typenum::{Unsigned, U32}, + typenum::{Sum, Unsigned, U1, U2, U32}, ArrayLength, GenericArray, }; -use hkdf::Hkdf; +use hkdf::{Hkdf, HkdfExtract}; use hmac::{Hmac, Mac, NewMac}; use rand::{CryptoRng, RngCore}; -use zeroize::Zeroize; /////////////// // Constants // @@ -56,55 +56,69 @@ static STR_OPAQUE: &[u8] = b"OPAQUE-"; pub struct TripleDH; /// The client state produced after the first key exchange message -#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr( + feature = "serde", + derive(serde_::Deserialize, serde_::Serialize), + serde(bound = "", crate = "serde_") +)] +#[derive(DeriveWhere)] +#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))] pub struct Ke1State { client_e_sk: PrivateKey, client_nonce: GenericArray, } -impl_clone_for!( - struct Ke1State, - [client_e_sk, client_nonce], -); -impl_debug_eq_hash_for!( - 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))] +#[cfg_attr( + feature = "serde", + derive(serde_::Deserialize, serde_::Serialize), + serde(bound = "", crate = "serde_") +)] +#[derive(DeriveWhere)] +#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize)] pub struct Ke1Message { pub(crate) client_nonce: GenericArray, pub(crate) client_e_pk: PublicKey, } /// The server state produced after the second key exchange message -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] -#[cfg_attr(feature = "serialize", serde(bound = ""))] -pub struct Ke2State> { - km3: GenericArray, - hashed_transcript: GenericArray, - session_key: GenericArray, +#[cfg_attr( + feature = "serde", + derive(serde_::Deserialize, serde_::Serialize), + serde(bound = "", crate = "serde_") +)] +#[derive(DeriveWhere)] +#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))] +pub struct Ke2State { + km3: GenericArray, + hashed_transcript: GenericArray, + session_key: GenericArray, } /// The second key exchange message -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] -#[cfg_attr(feature = "serialize", serde(bound = ""))] -pub struct Ke2Message> { +#[cfg_attr( + feature = "serde", + derive(serde_::Deserialize, serde_::Serialize), + serde(bound = "", crate = "serde_") +)] +#[derive(DeriveWhere)] +#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Ke2Message { server_nonce: GenericArray, server_e_pk: PublicKey, - mac: GenericArray, + mac: GenericArray, } /// The third key exchange message -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] -#[cfg_attr(feature = "serialize", serde(bound = ""))] -pub struct Ke3Message> { - mac: GenericArray, +#[cfg_attr( + feature = "serde", + derive(serde_::Deserialize, serde_::Serialize), + serde(bound = "", crate = "serde_") +)] +#[derive(DeriveWhere)] +#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Ke3Message { + mac: GenericArray, } //////////////////////////////// @@ -112,17 +126,33 @@ pub struct Ke3Message> { // ========================== // //////////////////////////////// -impl KeyExchange for TripleDH { +impl KeyExchange for TripleDH +where + // Ke1State: KeSk + Nonce + KG::SkLen: Add, + Sum: ArrayLength, + // Ke1Message: Nonce + KePk + NonceLen: Add, + Sum: ArrayLength, + // Ke2State: (Hash + Hash) + Hash + D::OutputSize: Add, + Sum: ArrayLength + Add, + Sum, D::OutputSize>: ArrayLength, + // Ke2Message: (Nonce + KePk) + Hash + NonceLen: Add, + Sum: ArrayLength + Add, + Sum, D::OutputSize>: ArrayLength, +{ type KE1State = Ke1State; - type KE2State = Ke2State<::OutputSize>; + type KE2State = Ke2State; type KE1Message = Ke1Message; - type KE2Message = Ke2Message::OutputSize>; - type KE3Message = Ke3Message<::OutputSize>; + type KE2Message = Ke2Message; + type KE3Message = Ke3Message; 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 { @@ -140,29 +170,32 @@ impl KeyExchange for TripleDH { } #[allow(clippy::type_complexity)] - fn generate_ke2>( + fn generate_ke2<'a, 'b, 'c, 'd, R: RngCore + CryptoRng, S: SecretKey>( rng: &mut R, - serialized_credential_request: Vec, - l2_bytes: Vec, + serialized_credential_request: impl Iterator, + l2_bytes: impl Iterator, ke1_message: Self::KE1Message, client_s_pk: PublicKey, server_s_sk: S, - id_u: Vec, - id_s: Vec, - context: Vec, + id_u: impl Iterator, + id_s: impl Iterator, + context: &[u8], ) -> Result, ProtocolError> { - let server_e_kp = - KeyPair::::generate_random(rng).map_err(|_| InternalError::InvalidKeypairError)?; + let server_e_kp = KeyPair::::generate_random(rng); let server_nonce = generate_nonce::(rng); let mut transcript_hasher = D::new() .chain(STR_RFC) - .chain(&serialize(&context, 2).map_err(ProtocolError::into_custom)?) - .chain(&id_u) - .chain(&serialized_credential_request[..]) - .chain(&id_s) - .chain(&l2_bytes[..]) - .chain(&server_nonce[..]) + .chain_iter( + Serialize::::from(context) + .map_err(ProtocolError::into_custom)? + .iter(), + ) + .chain_iter(id_u.into_iter()) + .chain_iter(serialized_credential_request) + .chain_iter(id_s.into_iter()) + .chain_iter(l2_bytes) + .chain(server_nonce) .chain(&server_e_kp.public().to_arr()); let result = derive_3dh_keys::( @@ -203,25 +236,25 @@ impl KeyExchange for TripleDH { } #[allow(clippy::type_complexity)] - fn generate_ke3( - l2_component: Vec, + fn generate_ke3<'a, 'b, 'c, 'd>( + l2_component: impl Iterator, ke2_message: Self::KE2Message, ke1_state: &Self::KE1State, - serialized_credential_request: &[u8], + serialized_credential_request: impl Iterator, server_s_pk: PublicKey, client_s_sk: PrivateKey, - id_u: Vec, - id_s: Vec, - context: Vec, + id_u: impl Iterator, + id_s: impl Iterator, + context: &[u8], ) -> Result, ProtocolError> { let mut transcript_hasher = D::new() .chain(STR_RFC) - .chain(&serialize(&context, 2)?) - .chain(&id_u) - .chain(&serialized_credential_request) - .chain(&id_s) - .chain(&l2_component[..]) - .chain(&ke2_message.to_bytes_without_info_or_mac()); + .chain_iter(Serialize::::from(context)?.iter()) + .chain_iter(id_u) + .chain_iter(serialized_credential_request) + .chain_iter(id_s) + .chain_iter(l2_component) + .chain_iter(ke2_message.to_bytes_without_info_or_mac()); let result = derive_3dh_keys::>( TripleDHComponents { @@ -239,18 +272,18 @@ impl KeyExchange for TripleDH { Hmac::::new_from_slice(&result.1).map_err(|_| InternalError::HmacError)?; server_mac.update(&transcript_hasher.clone().finalize()); - if server_mac.verify(&ke2_message.mac).is_err() { - return Err(ProtocolError::InvalidLoginError); - } + server_mac + .verify(&ke2_message.mac) + .map_err(|_| ProtocolError::InvalidLoginError)?; - transcript_hasher.update(ke2_message.mac.to_vec()); + transcript_hasher.update(&ke2_message.mac); let mut client_mac = Hmac::::new_from_slice(&result.2).map_err(|_| InternalError::HmacError)?; client_mac.update(&transcript_hasher.finalize()); Ok(( - result.0.to_vec(), + result.0, Ke3Message { mac: client_mac.finalize().into_bytes(), }, @@ -265,16 +298,16 @@ impl KeyExchange for TripleDH { fn finish_ke( ke3_message: Self::KE3Message, ke2_state: &Self::KE2State, - ) -> Result, ProtocolError> { + ) -> Result, ProtocolError> { let mut client_mac = Hmac::::new_from_slice(&ke2_state.km3).map_err(|_| InternalError::HmacError)?; client_mac.update(&ke2_state.hashed_transcript); - if client_mac.verify(&ke3_message.mac).is_err() { - return Err(ProtocolError::InvalidLoginError); - } + client_mac + .verify(&ke3_message.mac) + .map_err(|_| ProtocolError::InvalidLoginError)?; - Ok(ke2_state.session_key.to_vec()) + Ok(ke2_state.session_key.clone()) } fn ke2_message_size() -> usize { @@ -311,7 +344,7 @@ type TripleDHDerivationResult = ( GenericArray::OutputSize>, GenericArray::OutputSize>, GenericArray::OutputSize>, - Vec, + GenericArray::OutputSize>, ); //////////////////////////////////////////////// @@ -327,18 +360,21 @@ fn derive_3dh_keys>( dh: TripleDHComponents, hashed_derivation_transcript: &[u8], ) -> Result, ProtocolError> { - let ikm: Vec = [ + let mut hkdf = HkdfExtract::::new(None); + + hkdf.input_ikm( &dh.sk1 .diffie_hellman(dh.pk1) - .map_err(InternalError::into_custom)?[..], - &dh.sk2.diffie_hellman(dh.pk2)?[..], + .map_err(InternalError::into_custom)?, + ); + hkdf.input_ikm(&dh.sk2.diffie_hellman(dh.pk2)?); + hkdf.input_ikm( &dh.sk3 .diffie_hellman(dh.pk3) - .map_err(InternalError::into_custom)?[..], - ] - .concat(); + .map_err(InternalError::into_custom)?, + ); - let extracted_ikm = Hkdf::::new(None, &ikm); + let (_, extracted_ikm) = hkdf.finalize(); let handshake_secret = derive_secrets::( &extracted_ikm, STR_HANDSHAKE_SECRET, @@ -352,20 +388,10 @@ fn derive_3dh_keys>( ) .map_err(ProtocolError::into_custom)?; - let km2 = hkdf_expand_label::( - &handshake_secret, - STR_SERVER_MAC, - b"", - ::OutputSize::USIZE, - ) - .map_err(ProtocolError::into_custom)?; - let km3 = hkdf_expand_label::( - &handshake_secret, - STR_CLIENT_MAC, - b"", - ::OutputSize::USIZE, - ) - .map_err(ProtocolError::into_custom)?; + let km2 = hkdf_expand_label::(&handshake_secret, STR_SERVER_MAC, b"") + .map_err(ProtocolError::into_custom)?; + let km3 = hkdf_expand_label::(&handshake_secret, STR_CLIENT_MAC, b"") + .map_err(ProtocolError::into_custom)?; Ok(( GenericArray::clone_from_slice(&session_key), @@ -380,33 +406,35 @@ fn hkdf_expand_label( secret: &[u8], label: &[u8], context: &[u8], - length: usize, -) -> Result, ProtocolError> { +) -> Result, ProtocolError> { let h = Hkdf::::from_prk(secret).map_err(|_| InternalError::HkdfError)?; - hkdf_expand_label_extracted(&h, label, context, length) + hkdf_expand_label_extracted(&h, label, context) } fn hkdf_expand_label_extracted( hkdf: &Hkdf, label: &[u8], context: &[u8], - length: usize, -) -> Result, ProtocolError> { - let mut okm = vec![0u8; length]; +) -> Result, ProtocolError> { + let mut okm = GenericArray::default(); - let mut hkdf_label: Vec = Vec::new(); + let length_u16: u16 = + u16::try_from(D::OutputSize::USIZE).map_err(|_| ProtocolError::SerializationError)?; + let label = Serialize::::from_label(STR_OPAQUE, label)?; + let label = label.to_array_3(); + let context = Serialize::::from(context)?; + let context = context.to_array_2(); - let length_u16: u16 = u16::try_from(length).map_err(|_| ProtocolError::SerializationError)?; - hkdf_label.extend_from_slice(&length_u16.to_be_bytes()); + let hkdf_label = [ + &length_u16.to_be_bytes(), + label[0], + label[1], + label[2], + context[0], + context[1], + ]; - let mut opaque_label: Vec = Vec::new(); - opaque_label.extend_from_slice(STR_OPAQUE); - opaque_label.extend_from_slice(label); - hkdf_label.extend_from_slice(&serialize(&opaque_label, 1)?); - - hkdf_label.extend_from_slice(&serialize(context, 1)?); - - hkdf.expand(&hkdf_label, &mut okm) + hkdf.expand_multi_info(&hkdf_label, &mut okm) .map_err(|_| InternalError::HkdfError)?; Ok(okm) } @@ -415,27 +443,22 @@ fn derive_secrets( hkdf: &Hkdf, label: &[u8], hashed_derivation_transcript: &[u8], -) -> Result, ProtocolError> { - hkdf_expand_label_extracted::( - hkdf, - label, - hashed_derivation_transcript, - ::OutputSize::USIZE, - ) +) -> Result, ProtocolError> { + hkdf_expand_label_extracted::(hkdf, label, hashed_derivation_transcript) } // Generate a random nonce up to NonceLen::USIZE bytes. fn generate_nonce(rng: &mut R) -> GenericArray { - let mut nonce_bytes = vec![0u8; NonceLen::USIZE]; + let mut nonce_bytes = GenericArray::default(); rng.fill_bytes(&mut nonce_bytes); - GenericArray::clone_from_slice(&nonce_bytes) + nonce_bytes } // Serialization and deserialization implementations impl FromBytes for Ke1State { - fn from_bytes(bytes: &[u8]) -> Result { - let key_len = ::PkLen::USIZE; + fn from_bytes(bytes: &[u8]) -> Result { + let key_len = KG::SkLen::USIZE; let nonce_len = NonceLen::USIZE; let checked_bytes = check_slice_size_atleast(bytes, key_len + nonce_len, "ke1_state")?; @@ -449,15 +472,21 @@ impl FromBytes 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 +impl ToBytes for Ke1State +where + // Ke1State: KeSk + Nonce + KG::SkLen: Add, + Sum: ArrayLength, +{ + type Len = Sum; + + fn to_bytes(&self) -> GenericArray { + self.client_e_sk.to_arr().concat(self.client_nonce) } } impl FromBytes for Ke1Message { - fn from_bytes(ke1_message_bytes: &[u8]) -> Result { + fn from_bytes(ke1_message_bytes: &[u8]) -> Result { let nonce_len = NonceLen::USIZE; let checked_nonce = check_slice_size( ke1_message_bytes, @@ -472,15 +501,22 @@ impl FromBytes for Ke1Message { } } -impl ToBytes for Ke1Message { - fn to_bytes(&self) -> Vec { - [&self.client_nonce[..], &self.client_e_pk.to_arr()].concat() +impl ToBytes for Ke1Message +where + // Ke1Message: Nonce + KePk + NonceLen: Add, + Sum: ArrayLength, +{ + type Len = Sum; + + fn to_bytes(&self) -> GenericArray { + self.client_nonce.concat(self.client_e_pk.to_arr()) } } -impl> FromBytes for Ke2State { - fn from_bytes(input: &[u8]) -> Result { - let hash_len = HashLen::USIZE; +impl FromBytes for Ke2State { + fn from_bytes(input: &[u8]) -> Result { + let hash_len = D::OutputSize::USIZE; let checked_bytes = check_slice_size(input, 3 * hash_len, "ke2_state")?; Ok(Self { @@ -493,19 +529,25 @@ impl> FromBytes for Ke2State { } } -impl> ToBytes for Ke2State { - fn to_bytes(&self) -> Vec { - [ - &self.km3[..], - &self.hashed_transcript[..], - &self.session_key[..], - ] - .concat() +impl ToBytes for Ke2State +where + // Ke2State: (Hash + Hash) + Hash + D::OutputSize: Add, + Sum: ArrayLength + Add, + Sum, D::OutputSize>: ArrayLength, +{ + type Len = Sum, D::OutputSize>; + + fn to_bytes(&self) -> GenericArray { + self.km3 + .clone() + .concat(self.hashed_transcript.clone()) + .concat(self.session_key.clone()) } } -impl> FromBytes for Ke2Message { - fn from_bytes(input: &[u8]) -> Result { +impl FromBytes for Ke2Message { + fn from_bytes(input: &[u8]) -> Result { let key_len = ::PkLen::USIZE; let nonce_len = NonceLen::USIZE; let checked_nonce = check_slice_size_atleast(input, nonce_len, "ke2_message nonce")?; @@ -517,12 +559,12 @@ impl> FromBytes for Ke2Message::check_public_key(PublicKey::from_bytes( + let server_e_pk = KeyPair::::check_public_key(PublicKey::from_bytes( &unchecked_server_e_pk[..key_len], )?)?; @@ -534,21 +576,33 @@ impl> FromBytes for Ke2Message> ToBytes for Ke2Message { - fn to_bytes(&self) -> Vec { - [&self.to_bytes_without_info_or_mac(), &self.mac[..]].concat() +impl ToBytes for Ke2Message +where + // Ke2Message: (Nonce + KePk) + Hash + NonceLen: Add, + Sum: ArrayLength + Add, + Sum, D::OutputSize>: ArrayLength, +{ + type Len = Sum, D::OutputSize>; + + fn to_bytes(&self) -> GenericArray { + self.server_nonce + .concat(self.server_e_pk.to_arr()) + .concat(self.mac.clone()) } } -impl> Ke2Message { - fn to_bytes_without_info_or_mac(&self) -> Vec { - [&self.server_nonce[..], &self.server_e_pk.to_arr()].concat() +impl Ke2Message { + fn to_bytes_without_info_or_mac(&self) -> impl Iterator { + // MSRV: array `into_iter` isn't available in 1.51 + #[allow(deprecated)] + IntoIter::new([self.server_nonce.as_slice(), self.server_e_pk.as_slice()]) } } -impl> FromBytes for Ke3Message { - fn from_bytes(bytes: &[u8]) -> Result { - let checked_bytes = check_slice_size(bytes, HashLen::USIZE, "ke3_message")?; +impl FromBytes for Ke3Message { + fn from_bytes(bytes: &[u8]) -> Result { + let checked_bytes = check_slice_size(bytes, D::OutputSize::USIZE, "ke3_message")?; Ok(Self { mac: GenericArray::clone_from_slice(checked_bytes), @@ -556,39 +610,10 @@ impl> FromBytes for Ke3Message { } } -impl> ToBytes for Ke3Message { - fn to_bytes(&self) -> Vec { - self.mac.to_vec() - } -} - -// Zeroize on drop implementations - -// This can't be derived because of the use of a generic parameter -impl Zeroize for Ke1State { - fn zeroize(&mut self) { - self.client_e_sk.zeroize(); - self.client_nonce.zeroize(); - } -} - -impl Drop for Ke1State { - fn drop(&mut self) { - self.zeroize(); - } -} - -// This can't be derived because of the use of a phantom parameter -impl> Zeroize for Ke2State { - fn zeroize(&mut self) { - self.km3.zeroize(); - self.hashed_transcript.zeroize(); - self.session_key.zeroize(); - } -} - -impl> Drop for Ke2State { - fn drop(&mut self) { - self.zeroize(); +impl ToBytes for Ke3Message { + type Len = D::OutputSize; + + fn to_bytes(&self) -> GenericArray { + self.mac.clone() } } diff --git a/src/keypair.rs b/src/keypair.rs index 3b3633e..9f880f1 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -11,9 +11,8 @@ use crate::errors::{InternalError, ProtocolError}; use crate::key_exchange::group::KeGroup; -use alloc::vec::Vec; -use core::fmt::Debug; use core::ops::Deref; +use derive_where::DeriveWhere; use generic_array::typenum::Unsigned; use generic_array::{ArrayLength, GenericArray}; use rand::{CryptoRng, RngCore}; @@ -21,65 +20,24 @@ use zeroize::Zeroize; /// A Keypair trait with public-private verification #[cfg_attr( - feature = "serialize", - derive(serde::Deserialize, serde::Serialize), - serde(bound( - deserialize = "S: serde::Deserialize<'de>", - serialize = "S: serde::Serialize" - )) + feature = "serde", + derive(serde_::Deserialize, serde_::Serialize), + serde( + bound( + deserialize = "S: serde_::Deserialize<'de>", + serialize = "S: serde_::Serialize" + ), + crate = "serde_" + ) )] +#[derive(DeriveWhere)] +#[derive_where(Clone, Zeroize(drop))] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; S)] pub struct KeyPair = PrivateKey> { pk: PublicKey, sk: S, } -impl> Clone for KeyPair { - fn clone(&self) -> Self { - Self { - pk: self.pk.clone(), - sk: self.sk.clone(), - } - } -} - -impl + Debug> Debug for KeyPair { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("KeyPair") - .field("pk", &self.pk) - .field("sk", &self.sk) - .finish() - } -} - -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 + core::hash::Hash> core::hash::Hash for KeyPair { - fn hash(&self, state: &mut H) { - self.pk.hash(state); - self.sk.hash(state); - } -} - -// This can't be derived because of the use of a generic parameter -impl> Zeroize for KeyPair { - fn zeroize(&mut self) { - self.pk.zeroize(); - self.sk.zeroize(); - } -} - -impl> Drop for KeyPair { - fn drop(&mut self) { - self.zeroize(); - } -} - impl> KeyPair { /// The public key component pub fn public(&self) -> &PublicKey { @@ -113,20 +71,18 @@ impl> KeyPair { impl KeyPair { /// Generating a random key pair given a cryptographic rng - pub(crate) fn generate_random( - rng: &mut R, - ) -> Result { + pub(crate) fn generate_random(rng: &mut R) -> Self { let sk = KG::random_sk(rng); let pk = KG::public_key(&sk); - Ok(Self { + Self { pk: PublicKey(Key(pk.to_arr())), 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 { @@ -138,7 +94,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).unwrap()) + Some(Self::generate_random(&mut rng)) }) .no_shrink() .boxed() @@ -147,52 +103,14 @@ impl KeyPair { /// A minimalist key type built around a \[u8; 32\] #[cfg_attr( - feature = "serialize", - derive(serde::Deserialize, serde::Serialize), - serde(bound = "") + feature = "serde", + derive(serde_::Deserialize, serde_::Serialize), + serde(bound = "", crate = "serde_") )] -#[repr(transparent)] +#[derive(DeriveWhere)] +#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))] pub struct Key>(GenericArray); -impl> Clone for Key { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - -impl> Debug for Key { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_tuple("Key").field(&self.0).finish() - } -} - -impl> Eq for Key {} - -impl> PartialEq for Key { - fn eq(&self, other: &Self) -> bool { - self.0.eq(&other.0) - } -} - -impl> core::hash::Hash for Key { - fn hash(&self, state: &mut H) { - self.0.hash(state); - } -} - -// This can't be derived because of the use of a generic parameter -impl> Zeroize for Key { - fn zeroize(&mut self) { - self.0.zeroize(); - } -} - -impl> Drop for Key { - fn drop(&mut self) { - self.zeroize(); - } -} - impl> Deref for Key { type Target = GenericArray; @@ -210,32 +128,16 @@ 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)] +#[cfg_attr( + feature = "serde", + derive(serde_::Deserialize, serde_::Serialize), + serde(bound = "", crate = "serde_") +)] +#[derive(DeriveWhere)] +#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))] pub struct PrivateKey(Key); -impl_clone_for!( - tuple PrivateKey, - [0], -); -impl_debug_eq_hash_for!( - tuple PrivateKey, - [0], -); - // This can't be derived because of the use of a generic parameter -impl Zeroize for PrivateKey { - fn zeroize(&mut self) { - self.0.zeroize(); - } -} - -impl Drop for PrivateKey { - fn drop(&mut self) { - self.zeroize(); - } -} - impl Deref for PrivateKey { type Target = Key; @@ -264,15 +166,20 @@ impl PrivateKey { pub trait SecretKey: Clone + Sized + Zeroize { /// Custom error type that can be passed down to `InternalError::Custom` type Error; + /// Serialization size in bytes. + type Len: ArrayLength; /// 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>; /// Serialization into bytes - fn serialize(&self) -> Vec; + fn serialize(&self) -> GenericArray; /// Deserialization from bytes fn deserialize(input: &[u8]) -> Result>; @@ -280,18 +187,22 @@ pub trait SecretKey: Clone + Sized + Zeroize { impl SecretKey for PrivateKey { type Error = core::convert::Infallible; + type Len = KG::SkLen; - fn diffie_hellman(&self, pk: PublicKey) -> Result, InternalError> { + fn diffie_hellman( + &self, + pk: PublicKey, + ) -> Result, InternalError> { let pk = KG::from_pk_slice(&pk)?; - Ok(pk.diffie_hellman(self).to_vec()) + Ok(pk.diffie_hellman(self)) } fn public_key(&self) -> Result, InternalError> { Ok(PublicKey(Key(KG::public_key(&self.0).to_arr()))) } - fn serialize(&self) -> Vec { - self.to_vec() + fn serialize(&self) -> GenericArray { + self.to_arr() } fn deserialize(input: &[u8]) -> Result { @@ -300,32 +211,15 @@ 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)] +#[cfg_attr( + feature = "serde", + derive(serde_::Deserialize, serde_::Serialize), + serde(bound = "", crate = "serde_") +)] +#[derive(DeriveWhere)] +#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))] pub struct PublicKey(Key); -impl_clone_for!( - tuple PublicKey, - [0], -); -impl_debug_eq_hash_for!( - tuple PublicKey, - [0], -); - -// This can't be derived because of the use of a generic parameter -impl Zeroize for PublicKey { - fn zeroize(&mut self) { - self.0.zeroize(); - } -} - -impl Drop for PublicKey { - fn drop(&mut self) { - self.zeroize(); - } -} - impl Deref for PublicKey { type Target = Key; @@ -355,85 +249,109 @@ mod tests { use super::*; use crate::errors::*; use core::slice::from_raw_parts; - use curve25519_dalek::ristretto::RistrettoPoint; use generic_array::typenum::Unsigned; - use proptest::prelude::*; use rand::rngs::OsRng; #[test] fn test_zeroize_key() -> Result<(), ProtocolError> { - let key_len = ::PkLen::USIZE; - let mut key = Key::<::PkLen>(GenericArray::clone_from_slice( - &alloc::vec![ + fn inner() -> Result<(), ProtocolError> { + let key_len = G::PkLen::USIZE; + let mut key = Key::(GenericArray::clone_from_slice(&alloc::vec![ 1u8; key_len - ], - )); - let ptr = key.as_ptr(); + ])); + let ptr = key.as_ptr(); - Zeroize::zeroize(&mut key); + Zeroize::zeroize(&mut key); - let bytes = unsafe { from_raw_parts(ptr, key_len) }; - assert!(bytes.iter().all(|&x| x == 0)); + let bytes = unsafe { from_raw_parts(ptr, key_len) }; + assert!(bytes.iter().all(|&x| x == 0)); + + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; Ok(()) } #[test] - fn test_zeroize_keypair() -> Result<(), ProtocolError> { - let mut rng = OsRng; - 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; + fn test_zeroize_keypair() { + fn inner() { + let mut rng = OsRng; + 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 = G::PkLen::USIZE; + let sk_len = G::SkLen::USIZE; - Zeroize::zeroize(&mut keypair); + Zeroize::zeroize(&mut keypair); - let pk_bytes = unsafe { from_raw_parts(pk_ptr, pk_len) }; - let sk_bytes = unsafe { from_raw_parts(sk_ptr, sk_len) }; + 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)); + assert!(pk_bytes.iter().all(|&x| x == 0)); + assert!(sk_bytes.iter().all(|&x| x == 0)); + } - Ok(()) + #[cfg(feature = "ristretto255")] + inner::(); + #[cfg(feature = "p256")] + inner::(); } - proptest! { - #[test] - fn test_ristretto_check(kp in KeyPair::::uniform_keypair_strategy()) { - let pk = kp.public(); - prop_assert!(KeyPair::::check_public_key(pk.clone()).is_ok()); - } + macro_rules! test { + ($mod:ident, $point:ty) => { + mod $mod { + use super::*; + use proptest::prelude::*; - #[test] - fn test_ristretto_pub_from_priv(kp in KeyPair::::uniform_keypair_strategy()) { - let pk = kp.public(); - let sk = kp.private(); - prop_assert_eq!(&sk.public_key()?, pk); - } + proptest! { + #[test] + fn check(kp in KeyPair::<$point>::uniform_keypair_strategy()) { + let pk = kp.public(); + prop_assert!(KeyPair::<$point>::check_public_key(pk.clone()).is_ok()); + } - #[test] - fn test_ristretto_dh(kp1 in KeyPair::::uniform_keypair_strategy(), - kp2 in KeyPair::::uniform_keypair_strategy()) { + #[test] + fn pub_from_priv(kp in KeyPair::<$point>::uniform_keypair_strategy()) { + let pk = kp.public(); + let sk = kp.private(); + prop_assert_eq!(&sk.public_key()?, pk); + } - let dh1 = kp2.private().diffie_hellman(kp1.public().clone())?; - let dh2 = kp1.private().diffie_hellman(kp2.public().clone())?; + #[test] + fn dh(kp1 in KeyPair::<$point>::uniform_keypair_strategy(), + kp2 in KeyPair::<$point>::uniform_keypair_strategy()) { - prop_assert_eq!(dh1, dh2); - } + let dh1 = kp2.private().diffie_hellman(kp1.public().clone())?; + let dh2 = kp1.private().diffie_hellman(kp2.public().clone())?; - #[test] - fn test_private_key_slice(kp in KeyPair::::uniform_keypair_strategy()) { - let sk_bytes = kp.private().to_vec(); + prop_assert_eq!(dh1, dh2); + } - let kp2 = KeyPair::::from_private_key_slice(&sk_bytes)?; - let kp2_private_bytes = kp2.private().to_vec(); + #[test] + fn private_key_slice(kp in KeyPair::<$point>::uniform_keypair_strategy()) { + let sk_bytes = kp.private().to_vec(); - prop_assert_eq!(sk_bytes, kp2_private_bytes); - } + let kp2 = KeyPair::<$point>::from_private_key_slice(&sk_bytes)?; + let kp2_private_bytes = kp2.private().to_vec(); + + prop_assert_eq!(sk_bytes, kp2_private_bytes); + } + } + } + }; } + #[cfg(feature = "ristretto255")] + test!(ristretto, curve25519_dalek::ristretto::RistrettoPoint); + #[cfg(feature = "p256")] + test!(p256, p256_::PublicKey); + #[test] fn remote_key() { use crate::{ @@ -443,37 +361,48 @@ mod tests { ServerLoginStartParameters, ServerLoginStartResult, ServerRegistration, ServerRegistrationStartResult, ServerSetup, }; - use curve25519_dalek::ristretto::RistrettoPoint; + #[cfg(feature = "ristretto255")] + use curve25519_dalek::ristretto::RistrettoPoint as KeCurve; + #[cfg(not(feature = "ristretto255"))] + use p256_::PublicKey as KeCurve; use rand::rngs::OsRng; struct Default; impl CipherSuite for Default { - type OprfGroup = RistrettoPoint; - type KeGroup = RistrettoPoint; + #[cfg(feature = "ristretto255")] + type OprfGroup = KeCurve; + #[cfg(not(feature = "ristretto255"))] + type OprfGroup = p256_::ProjectivePoint; + type KeGroup = KeCurve; type KeyExchange = crate::key_exchange::tripledh::TripleDH; + #[cfg(feature = "ristretto255")] type Hash = sha2::Sha512; + #[cfg(not(feature = "ristretto255"))] + type Hash = sha2::Sha256; type SlowHash = crate::slow_hash::NoOpHash; } #[derive(Clone, Zeroize)] - struct RemoteKey(PrivateKey); + struct RemoteKey(PrivateKey); - impl SecretKey for RemoteKey { + impl SecretKey for RemoteKey { type Error = core::convert::Infallible; + type Len = ::SkLen; fn diffie_hellman( &self, - pk: PublicKey, - ) -> Result, InternalError> { + pk: PublicKey, + ) -> Result::PkLen>, InternalError> + { self.0.diffie_hellman(pk) } - fn public_key(&self) -> Result, InternalError> { + fn public_key(&self) -> Result, InternalError> { self.0.public_key() } - fn serialize(&self) -> Vec { + fn serialize(&self) -> GenericArray { self.0.serialize() } @@ -484,12 +413,11 @@ mod tests { const PASSWORD: &str = "password"; - let sk = RistrettoPoint::random_sk(&mut OsRng); + let sk = KeCurve::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).unwrap(); + let server_setup = ServerSetup::::new_with_key(&mut OsRng, keypair); let ClientRegistrationStartResult { message, diff --git a/src/lib.rs b/src/lib.rs index cbf68f1..49788b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,7 @@ //! * a slow hashing function. //! //! We will use the following choices in this example: -//! ``` +//! ```ignore //! use opaque_ke::CipherSuite; //! struct Default; //! impl CipherSuite for Default { @@ -51,6 +51,7 @@ //! # use opaque_ke::CipherSuite; //! # use opaque_ke::ServerSetup; //! # struct Default; +//! # #[cfg(feature = "ristretto255")] //! # impl CipherSuite for Default { //! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; @@ -58,6 +59,14 @@ //! # type Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } +//! # #[cfg(not(feature = "ristretto255"))] +//! # impl CipherSuite for Default { +//! # type OprfGroup = p256_::ProjectivePoint; +//! # type KeGroup = p256_::PublicKey; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; +//! # type Hash = sha2::Sha256; +//! # type SlowHash = opaque_ke::slow_hash::NoOpHash; +//! # } //! use rand::{rngs::OsRng, RngCore}; //! let mut rng = OsRng; //! let server_setup = ServerSetup::::new(&mut rng); @@ -84,6 +93,7 @@ //! # }; //! # use opaque_ke::CipherSuite; //! # struct Default; +//! # #[cfg(feature = "ristretto255")] //! # impl CipherSuite for Default { //! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; @@ -91,6 +101,14 @@ //! # type Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } +//! # #[cfg(not(feature = "ristretto255"))] +//! # impl CipherSuite for Default { +//! # type OprfGroup = p256_::ProjectivePoint; +//! # type KeGroup = p256_::PublicKey; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; +//! # type Hash = sha2::Sha256; +//! # type SlowHash = opaque_ke::slow_hash::NoOpHash; +//! # } //! use opaque_ke::ClientRegistration; //! use rand::{rngs::OsRng, RngCore}; //! let mut client_rng = OsRng; @@ -115,6 +133,7 @@ //! # }; //! # use opaque_ke::CipherSuite; //! # struct Default; +//! # #[cfg(feature = "ristretto255")] //! # impl CipherSuite for Default { //! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; @@ -122,6 +141,14 @@ //! # type Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } +//! # #[cfg(not(feature = "ristretto255"))] +//! # impl CipherSuite for Default { +//! # type OprfGroup = p256_::ProjectivePoint; +//! # type KeGroup = p256_::PublicKey; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; +//! # type Hash = sha2::Sha256; +//! # type SlowHash = opaque_ke::slow_hash::NoOpHash; +//! # } //! # use rand::{rngs::OsRng, RngCore}; //! # let mut client_rng = OsRng; //! # let client_registration_start_result = ClientRegistration::::start( @@ -130,7 +157,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, @@ -153,6 +180,7 @@ //! # }; //! # use opaque_ke::CipherSuite; //! # struct Default; +//! # #[cfg(feature = "ristretto255")] //! # impl CipherSuite for Default { //! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; @@ -160,6 +188,14 @@ //! # type Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } +//! # #[cfg(not(feature = "ristretto255"))] +//! # impl CipherSuite for Default { +//! # type OprfGroup = p256_::ProjectivePoint; +//! # type KeGroup = p256_::PublicKey; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; +//! # type Hash = sha2::Sha256; +//! # type SlowHash = opaque_ke::slow_hash::NoOpHash; +//! # } //! # use rand::{rngs::OsRng, RngCore}; //! # let mut client_rng = OsRng; //! # let client_registration_start_result = ClientRegistration::::start( @@ -167,7 +203,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, @@ -192,6 +228,7 @@ //! # }; //! # use opaque_ke::CipherSuite; //! # struct Default; +//! # #[cfg(feature = "ristretto255")] //! # impl CipherSuite for Default { //! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; @@ -199,6 +236,14 @@ //! # type Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } +//! # #[cfg(not(feature = "ristretto255"))] +//! # impl CipherSuite for Default { +//! # type OprfGroup = p256_::ProjectivePoint; +//! # type KeGroup = p256_::PublicKey; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; +//! # type Hash = sha2::Sha256; +//! # type SlowHash = opaque_ke::slow_hash::NoOpHash; +//! # } //! # use rand::{rngs::OsRng, RngCore}; //! # let mut client_rng = OsRng; //! # let client_registration_start_result = ClientRegistration::::start( @@ -206,7 +251,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( @@ -235,6 +280,7 @@ //! # }; //! # use opaque_ke::CipherSuite; //! # struct Default; +//! # #[cfg(feature = "ristretto255")] //! # impl CipherSuite for Default { //! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; @@ -242,6 +288,14 @@ //! # type Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } +//! # #[cfg(not(feature = "ristretto255"))] +//! # impl CipherSuite for Default { +//! # type OprfGroup = p256_::ProjectivePoint; +//! # type KeGroup = p256_::PublicKey; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; +//! # type Hash = sha2::Sha256; +//! # type SlowHash = opaque_ke::slow_hash::NoOpHash; +//! # } //! # use rand::{rngs::OsRng, RngCore}; //! use opaque_ke::ClientLogin; //! let mut client_rng = OsRng; @@ -269,6 +323,7 @@ //! # }; //! # use opaque_ke::CipherSuite; //! # struct Default; +//! # #[cfg(feature = "ristretto255")] //! # impl CipherSuite for Default { //! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; @@ -276,6 +331,14 @@ //! # type Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } +//! # #[cfg(not(feature = "ristretto255"))] +//! # impl CipherSuite for Default { +//! # type OprfGroup = p256_::ProjectivePoint; +//! # type KeGroup = p256_::PublicKey; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; +//! # type Hash = sha2::Sha256; +//! # type SlowHash = opaque_ke::slow_hash::NoOpHash; +//! # } //! # use rand::{rngs::OsRng, RngCore}; //! # let mut client_rng = OsRng; //! # let client_registration_start_result = ClientRegistration::::start( @@ -283,16 +346,16 @@ //! # 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", //! # )?; //! use opaque_ke::{ServerLogin, ServerLoginStartParameters}; -//! let password_file = ServerRegistration::::deserialize(&password_file_bytes[..])?; +//! let password_file = ServerRegistration::::deserialize(&password_file_bytes)?; //! let mut server_rng = OsRng; //! let server_login_start_result = ServerLogin::start( //! &mut server_rng, @@ -323,6 +386,7 @@ //! # }; //! # use opaque_ke::CipherSuite; //! # struct Default; +//! # #[cfg(feature = "ristretto255")] //! # impl CipherSuite for Default { //! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; @@ -330,6 +394,14 @@ //! # type Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } +//! # #[cfg(not(feature = "ristretto255"))] +//! # impl CipherSuite for Default { +//! # type OprfGroup = p256_::ProjectivePoint; +//! # type KeGroup = p256_::PublicKey; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; +//! # type Hash = sha2::Sha256; +//! # type SlowHash = opaque_ke::slow_hash::NoOpHash; +//! # } //! # use rand::{rngs::OsRng, RngCore}; //! # let mut client_rng = OsRng; //! # let client_registration_start_result = ClientRegistration::::start( @@ -337,17 +409,17 @@ //! # 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", //! # )?; //! # let password_file = //! # ServerRegistration::::deserialize( -//! # &password_file_bytes[..], +//! # &password_file_bytes, //! # )?; //! # let server_login_start_result = //! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"alice@example.com", ServerLoginStartParameters::default())?; @@ -369,6 +441,7 @@ //! # }; //! # use opaque_ke::CipherSuite; //! # struct Default; +//! # #[cfg(feature = "ristretto255")] //! # impl CipherSuite for Default { //! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; @@ -376,6 +449,14 @@ //! # type Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } +//! # #[cfg(not(feature = "ristretto255"))] +//! # impl CipherSuite for Default { +//! # type OprfGroup = p256_::ProjectivePoint; +//! # type KeGroup = p256_::PublicKey; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; +//! # type Hash = sha2::Sha256; +//! # type SlowHash = opaque_ke::slow_hash::NoOpHash; +//! # } //! # use rand::{rngs::OsRng, RngCore}; //! # let mut client_rng = OsRng; //! # let client_registration_start_result = ClientRegistration::::start( @@ -383,17 +464,17 @@ //! # 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", //! # )?; //! # let password_file = //! # ServerRegistration::::deserialize( -//! # &password_file_bytes[..], +//! # &password_file_bytes, //! # )?; //! # let server_login_start_result = //! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"alice@example.com", ServerLoginStartParameters::default())?; @@ -445,6 +526,7 @@ //! # }; //! # use opaque_ke::CipherSuite; //! # struct Default; +//! # #[cfg(feature = "ristretto255")] //! # impl CipherSuite for Default { //! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; @@ -452,6 +534,14 @@ //! # type Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } +//! # #[cfg(not(feature = "ristretto255"))] +//! # impl CipherSuite for Default { +//! # type OprfGroup = p256_::ProjectivePoint; +//! # type KeGroup = p256_::PublicKey; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; +//! # type Hash = sha2::Sha256; +//! # type SlowHash = opaque_ke::slow_hash::NoOpHash; +//! # } //! # use rand::{rngs::OsRng, RngCore}; //! # let mut client_rng = OsRng; //! # let client_registration_start_result = ClientRegistration::::start( @@ -459,7 +549,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 @@ -468,14 +558,14 @@ //! 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", //! # )?; //! # let password_file = //! # ServerRegistration::::deserialize( -//! # &password_file_bytes[..], +//! # &password_file_bytes, //! # )?; //! # let server_login_start_result = //! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"alice@example.com", ServerLoginStartParameters::default())?; @@ -523,6 +613,7 @@ //! # }; //! # use opaque_ke::CipherSuite; //! # struct Default; +//! # #[cfg(feature = "ristretto255")] //! # impl CipherSuite for Default { //! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; @@ -530,6 +621,14 @@ //! # type Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } +//! # #[cfg(not(feature = "ristretto255"))] +//! # impl CipherSuite for Default { +//! # type OprfGroup = p256_::ProjectivePoint; +//! # type KeGroup = p256_::PublicKey; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; +//! # type Hash = sha2::Sha256; +//! # type SlowHash = opaque_ke::slow_hash::NoOpHash; +//! # } //! # use rand::{rngs::OsRng, RngCore}; //! # let mut client_rng = OsRng; //! # let client_registration_start_result = ClientRegistration::::start( @@ -537,7 +636,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( @@ -545,14 +644,14 @@ //! 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", //! # )?; //! # let password_file = //! # ServerRegistration::::deserialize( -//! # &password_file_bytes[..], +//! # &password_file_bytes, //! # )?; //! # let server_login_start_result = //! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"alice@example.com", ServerLoginStartParameters::default())?; @@ -589,6 +688,7 @@ //! # }; //! # use opaque_ke::CipherSuite; //! # struct Default; +//! # #[cfg(feature = "ristretto255")] //! # impl CipherSuite for Default { //! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; @@ -596,6 +696,14 @@ //! # type Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } +//! # #[cfg(not(feature = "ristretto255"))] +//! # impl CipherSuite for Default { +//! # type OprfGroup = p256_::ProjectivePoint; +//! # type KeGroup = p256_::PublicKey; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; +//! # type Hash = sha2::Sha256; +//! # type SlowHash = opaque_ke::slow_hash::NoOpHash; +//! # } //! # use rand::{rngs::OsRng, RngCore}; //! # let mut client_rng = OsRng; //! # let client_registration_start_result = ClientRegistration::::start( @@ -603,23 +711,23 @@ //! # 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(), -//! )), +//! Identifiers { +//! client: Some(b"Alice_the_Cryptographer"), +//! server: Some(b"Facebook"), +//! }, //! None, //! ), //! )?; //! # Ok::<(), ProtocolError>(()) //! ``` //! -//! The same identifiers must also be supplied using [ServerLoginStartParameters::WithIdentifiers] in [Server Login Start](#server-login-start): +//! The same identifiers must also be supplied using [ServerLoginStartParameters] in [Server Login Start](#server-login-start): //! ``` //! # use opaque_ke::{ //! # errors::ProtocolError, @@ -628,6 +736,7 @@ //! # }; //! # use opaque_ke::CipherSuite; //! # struct Default; +//! # #[cfg(feature = "ristretto255")] //! # impl CipherSuite for Default { //! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; @@ -635,6 +744,14 @@ //! # type Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } +//! # #[cfg(not(feature = "ristretto255"))] +//! # impl CipherSuite for Default { +//! # type OprfGroup = p256_::ProjectivePoint; +//! # type KeGroup = p256_::PublicKey; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; +//! # type Hash = sha2::Sha256; +//! # type SlowHash = opaque_ke::slow_hash::NoOpHash; +//! # } //! # use rand::{rngs::OsRng, RngCore}; //! # let mut client_rng = OsRng; //! # let client_registration_start_result = ClientRegistration::::start( @@ -642,16 +759,16 @@ //! # 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 client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::new(Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") }, None))?; +//! # let password_file_bytes = ServerRegistration::::finish(client_registration_finish_result.message).serialize(); //! # let client_login_start_result = ClientLogin::::start( //! # &mut client_rng, //! # b"password", //! # )?; //! # use opaque_ke::{ServerLogin, ServerLoginStartParameters}; -//! # let password_file = ServerRegistration::::deserialize(&password_file_bytes[..])?; +//! # let password_file = ServerRegistration::::deserialize(&password_file_bytes)?; //! # let mut server_rng = OsRng; //! let server_login_start_result = ServerLogin::start( //! &mut server_rng, @@ -659,12 +776,13 @@ //! Some(password_file), //! client_login_start_result.message, //! b"alice@example.com", -//! ServerLoginStartParameters::WithIdentifiers( -//! Identifiers::ClientAndServerIdentifiers( -//! b"Alice_the_Cryptographer".to_vec(), -//! b"Facebook".to_vec(), -//! ), -//! ), +//! ServerLoginStartParameters { +//! context: None, +//! identifiers: Identifiers { +//! client: Some(b"Alice_the_Cryptographer"), +//! server: Some(b"Facebook"), +//! }, +//! }, //! )?; //! # Ok::<(), ProtocolError>(()) //! ``` @@ -678,6 +796,7 @@ //! # }; //! # use opaque_ke::CipherSuite; //! # struct Default; +//! # #[cfg(feature = "ristretto255")] //! # impl CipherSuite for Default { //! # type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; //! # type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; @@ -685,6 +804,14 @@ //! # type Hash = sha2::Sha512; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # } +//! # #[cfg(not(feature = "ristretto255"))] +//! # impl CipherSuite for Default { +//! # type OprfGroup = p256_::ProjectivePoint; +//! # type KeGroup = p256_::PublicKey; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; +//! # type Hash = sha2::Sha256; +//! # type SlowHash = opaque_ke::slow_hash::NoOpHash; +//! # } //! # use rand::{rngs::OsRng, RngCore}; //! # let mut client_rng = OsRng; //! # let client_registration_start_result = ClientRegistration::::start( @@ -692,28 +819,28 @@ //! # 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 client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::new(Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") }, None))?; +//! # let password_file_bytes = ServerRegistration::::finish(client_registration_finish_result.message).serialize(); //! # let client_login_start_result = ClientLogin::::start( //! # &mut client_rng, //! # b"password", //! # )?; //! # let password_file = //! # ServerRegistration::::deserialize( -//! # &password_file_bytes[..], +//! # &password_file_bytes, //! # )?; //! # let server_login_start_result = -//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"alice@example.com", ServerLoginStartParameters::WithIdentifiers(Identifiers::ClientAndServerIdentifiers(b"Alice_the_Cryptographer".to_vec(), b"Facebook".to_vec())))?; +//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"alice@example.com", ServerLoginStartParameters { context: None, identifiers: Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") } })?; //! let client_login_finish_result = client_login_start_result.state.finish( //! server_login_start_result.message, //! ClientLoginFinishParameters::new( //! None, -//! Some(Identifiers::ClientAndServerIdentifiers( -//! b"Alice_the_Cryptographer".to_vec(), -//! b"Facebook".to_vec(), -//! )), +//! Identifiers { +//! client: Some(b"Alice_the_Cryptographer"), +//! server: Some(b"Facebook"), +//! }, //! None, //! ), //! )?; @@ -723,7 +850,7 @@ //! Failing to supply the same pair of custom identifiers in any of the three steps above will result in an error in attempting to complete //! the protocol! //! -//! Note that if only one of the client and server identifiers are present, then [Identifiers::ClientIdentifier] and [Identifiers::ServerIdentifier] can be +//! Note that if only one of the client and server identifiers are present, then [Identifiers] can be //! used to specify them individually. //! //! ## Key Exchange Context @@ -731,7 +858,7 @@ //! A key exchange protocol typically allows for the specifying of shared "context" information between the two parties before the exchange is complete, //! so as to bind the integrity of application-specific data or configuration parameters to the security of the key exchange. //! During the login phase, the client and server can specify this context using: -//! - The second login message, where the server can populate [ServerLoginStartParameters::WithContext], and +//! - The second login message, where the server can populate [ServerLoginStartParameters], and //! - The third login message, where the client can populate [ClientLoginFinishParameters]. //! //! For both of these messages, the `WithContextAndIdentifiers` variant can be used to specify these fields in addition to @@ -751,51 +878,60 @@ //! [`SecretKey`](keypair::SecretKey`) trait. This allows [`ServerSetup`] to be constructed using an existing keypair //! without exposing the bytes of the private key to this library. //! ``` -//! # use curve25519_dalek::ristretto::RistrettoPoint; -//! # use generic_array::{GenericArray, typenum::U32}; -//! # use opaque_ke::{CipherSuite, errors::{InternalError}, keypair::{KeyPair, PrivateKey, PublicKey, SecretKey}, ServerSetup}; +//! # use generic_array::{GenericArray, typenum::U0}; +//! # use opaque_ke::{CipherSuite, errors::{InternalError}, key_exchange::group::KeGroup, keypair::{KeyPair, PrivateKey, PublicKey, SecretKey}, ServerSetup}; //! # use rand::rngs::OsRng; //! # use zeroize::Zeroize; //! # struct Default; +//! # #[cfg(feature = "ristretto255")] //! # impl CipherSuite for Default { -//! # type OprfGroup = RistrettoPoint; +//! # 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(not(feature = "ristretto255"))] +//! # impl CipherSuite for Default { +//! # type OprfGroup = p256_::ProjectivePoint; +//! # type KeGroup = p256_::PublicKey; +//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; +//! # type Hash = sha2::Sha256; +//! # type SlowHash = opaque_ke::slow_hash::NoOpHash; +//! # } //! # #[derive(Debug)] //! # struct YourRemoteKeyError; //! # #[derive(Clone, Zeroize)] -//! # struct YourRemoteKey(PrivateKey); +//! # struct YourRemoteKey(PrivateKey<::KeGroup>); //! # impl YourRemoteKey { -//! # fn diffie_hellman(&self, pk: &[u8]) -> Result, YourRemoteKeyError> { todo!() } -//! # fn public_key(&self) -> Result, YourRemoteKeyError> { Ok(GenericArray::default()) } +//! # fn diffie_hellman(&self, pk: &[u8]) -> Result::KeGroup as KeGroup>::PkLen>, YourRemoteKeyError> { todo!() } +//! # fn public_key(&self) -> Result::KeGroup as KeGroup>::PkLen>, YourRemoteKeyError> { Ok(GenericArray::default()) } //! # } -//! impl SecretKey for YourRemoteKey { +//! impl SecretKey<::KeGroup> for YourRemoteKey { //! type Error = YourRemoteKeyError; +//! type Len = U0; //! //! fn diffie_hellman( //! &self, -//! pk: PublicKey, -//! ) -> Result, InternalError> { +//! pk: PublicKey<::KeGroup>, +//! ) -> Result::KeGroup as KeGroup>::PkLen>, InternalError> { //! YourRemoteKey::diffie_hellman(self, &pk.to_arr()).map_err(InternalError::Custom) //! } //! //! fn public_key( //! &self -//! ) -> Result, InternalError> { +//! ) -> Result::KeGroup>, InternalError> { //! YourRemoteKey::public_key(self).map(PublicKey::from_arr) //! .map_err(InternalError::Custom) //! } //! -//! fn serialize(&self) -> Vec { -//! // if you use serde and the "serialize" crate feature, you won't need this +//! fn serialize(&self) -> GenericArray { +//! // if you use Serde and the "serde" crate feature, you won't need this //! todo!() //! } //! //! fn deserialize(input: &[u8]) -> Result> { -//! // if you use serde and the "serialize" crate feature, you won't need this +//! // if you use Serde and the "serde" crate feature, you won't need this //! todo!() //! } //! } @@ -814,28 +950,34 @@ //! password file records will be against offline dictionary and precomputation attacks; see //! [the OPAQUE paper](https://eprint.iacr.org/2018/163.pdf) for more details. //! -//! - The `serialize` feature, enabled by default, provides convenience functions for serializing and deserializing with +//! - The `serde` feature, enabled by default, provides convenience functions for serializing and deserializing with //! [serde](https://serde.rs/). //! -//! - The `u32_backend` and `u64_backend` features are re-exported from -//! [curve25519-dalek](https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) and allow for selecting -//! the corresponding backend for the curve arithmetic used. The `u64_backend` feature is included as the default. +//! - The backend features are re-exported from +//! [curve25519-dalek](https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) and allow for selecting +//! the corresponding backend for the curve arithmetic used. The `ristretto255_u64` feature is included as the default. +//! Other features are mapped as `ristretto255_u32`, `ristretto255_fiat_u64` and `ristretto255_fiat_u32`. Any `ristretto255_*` +//! backend feature will enable the `ristretto255` feature, which can be used too, but keep in mind that `curve25519-dalek` +//! will fail to compile without a selected backend. This enabled to use `curve25519_dalek::ristretto::RistrettoPoint` as a +//! `KeGroup` and `OprfGroup`. //! -//! - The `p256` feature enables the use of `p256::ProjectivePoint` as a `Group` for `CipherSuite`. Note that this -//! is currently an experimental feature ⚠️, and is not yet ready for production use. +//! - The `x25519` feature is similar to the `ristretto255` feature and requires to select a backend like `x25519_u64`, other +//! backends are the same as in `ristretto255_*`. This enables `x25519_dalek::PublicKey` as a `KeGroup`. +//! +//! - The `ristretto255_simd` feature is re-exported from +//! [curve25519-dalek](https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) and enables parallel formulas, +//! using either AVX2 or AVX512-IFMA. This will automatically enable the `ristretto255_u64` feature and requires Rust nightly. +//! +//! - The `p256` feature enables the use of `p256::PublicKey` as a `KeGroup` and `p256::ProjectivePoint` as a `OprfGroup` for +//! `CipherSuite`. Note that this is currently an experimental feature ⚠️, and is not yet ready for production use. //! //! - The `bench` feature is used only for running performance benchmarks for this implementation. //! -#![cfg_attr(not(feature = "bench"), deny(missing_docs))] #![deny(unsafe_code)] #![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(not(any(feature = "u64_backend", feature = "u32_backend",)))] -compile_error!( - "no dalek arithmetic backend cargo feature enabled! \ - please enable one of: u64_backend, u32_backend" -); +#![warn(clippy::cargo, missing_docs)] +#![allow(clippy::multiple_crate_versions)] extern crate alloc; @@ -868,6 +1010,11 @@ pub use crate::messages::{ CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, }; +pub use crate::messages::{ + CredentialFinalizationLen, CredentialRequestLen, CredentialResponseLen, RegistrationRequestLen, + RegistrationResponseLen, RegistrationUploadLen, +}; +pub use crate::opaque::ServerRegistrationLen; pub use crate::opaque::{ ClientLogin, ClientRegistration, ServerLogin, ServerRegistration, ServerSetup, }; diff --git a/src/messages.rs b/src/messages.rs old mode 100644 new mode 100755 index 0deeb05..468b898 --- a/src/messages.rs +++ b/src/messages.rs @@ -9,21 +9,28 @@ use crate::{ ciphersuite::CipherSuite, - envelope::Envelope, + envelope::{Envelope, EnvelopeLen}, errors::{ utils::{check_slice_size, check_slice_size_atleast}, ProtocolError, }, key_exchange::{ group::KeGroup, - traits::{FromBytes, KeyExchange, ToBytes}, + traits::{FromBytes, Ke1MessageLen, Ke2MessageLen, Ke3MessageLen, KeyExchange, ToBytes}, + tripledh::NonceLen, }, keypair::{KeyPair, PublicKey, SecretKey}, - opaque::ServerSetup, + opaque::{MaskedResponse, MaskedResponseLen, ServerSetup}, +}; +use core::array::IntoIter; +use core::ops::Add; +use derive_where::DeriveWhere; +use digest::{Digest, FixedOutput}; +use generic_array::sequence::Concat; +use generic_array::{ + typenum::{Sum, Unsigned}, + ArrayLength, GenericArray, }; -use alloc::vec::Vec; -use digest::Digest; -use generic_array::{typenum::Unsigned, GenericArray}; use rand::{CryptoRng, RngCore}; use voprf::group::Group; @@ -33,13 +40,21 @@ use voprf::group::Group; //////////////////////////// /// The message sent by the client to the server, to initiate registration +#[derive(DeriveWhere)] +#[derive_where(Clone)] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; CS::OprfGroup)] pub struct RegistrationRequest { /// blinded password information pub(crate) blinded_element: voprf::BlindedElement, } +impl_serialize_and_deserialize_for!(RegistrationRequest); + /// The answer sent by the server to the user, upon reception of the /// registration attempt +#[derive(DeriveWhere)] +#[derive_where(Clone)] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; CS::OprfGroup)] pub struct RegistrationResponse { /// The server's oprf output pub(crate) evaluation_element: voprf::EvaluationElement, @@ -47,8 +62,18 @@ pub struct RegistrationResponse { pub(crate) server_s_pk: PublicKey, } +impl_serialize_and_deserialize_for!( + RegistrationResponse + where + // RegistrationResponse: KgPk + KePk + ::ElemLen: Add<::PkLen>, + RegistrationResponseLen: ArrayLength, +); + /// The final message from the client, containing sealed cryptographic /// identifiers +#[derive(DeriveWhere)] +#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))] pub struct RegistrationUpload { /// The "envelope" generated by the user, containing sealed /// cryptographic identifiers @@ -59,33 +84,98 @@ pub struct RegistrationUpload { pub(crate) client_s_pk: PublicKey, } +impl_serialize_and_deserialize_for!( + RegistrationUpload + where + // Envelope: Nonce + Hash + NonceLen: Add<::OutputSize>, + EnvelopeLen: ArrayLength, + // RegistrationUpload: (KePk + Hash) + Envelope + ::PkLen: Add<::OutputSize>, + Sum<::PkLen, ::OutputSize>: + ArrayLength | Add>, + RegistrationUploadLen: ArrayLength, +); + /// The message sent by the user to the server, to initiate registration +#[derive(DeriveWhere)] +#[derive_where(Clone, Zeroize)] +#[derive_where( + Debug, Eq, Hash, PartialEq; + CS::OprfGroup, + >::KE1Message, +)] pub struct CredentialRequest { pub(crate) blinded_element: voprf::BlindedElement, pub(crate) ke1_message: >::KE1Message, } +impl_serialize_and_deserialize_for!( + CredentialRequest + where + // CredentialRequest: KgPk + Ke1Message + ::ElemLen: Add>, + CredentialRequestLen: ArrayLength, +); + /// The answer sent by the server to the user, upon reception of the /// login attempt +#[derive(DeriveWhere)] +#[derive_where(Clone)] +#[derive_where( + Debug, Eq, Hash, PartialEq; + CS::OprfGroup, + >::KE2Message, +)] pub struct CredentialResponse { /// the server's oprf output pub(crate) evaluation_element: voprf::EvaluationElement, - pub(crate) masking_nonce: Vec, - pub(crate) masked_response: Vec, + pub(crate) masking_nonce: GenericArray, + pub(crate) masked_response: MaskedResponse, pub(crate) ke2_message: >::KE2Message, } +impl_serialize_and_deserialize_for!( + CredentialResponse + where + // CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse + ::ElemLen: Add, + Sum<::ElemLen, NonceLen>: + ArrayLength | Add>, + CredentialResponseWithoutKeLen: ArrayLength, + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength | Add<::PkLen>, + MaskedResponseLen: ArrayLength, + // CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message + CredentialResponseWithoutKeLen: Add>, + CredentialResponseLen: ArrayLength, +); + /// The answer sent by the client to the server, upon reception of the /// sealed envelope +#[derive(DeriveWhere)] +#[derive_where(Clone)] +#[derive_where( + Debug, Eq, Hash, PartialEq; + >::KE3Message, +)] pub struct CredentialFinalization { pub(crate) ke3_message: >::KE3Message, } +impl_serialize_and_deserialize_for!(CredentialFinalization); + //////////////////////////////// // High-level Implementations // // ========================== // //////////////////////////////// +/// Length of [`RegistrationRequest`] in bytes for serialization. +#[allow(type_alias_bounds)] +pub type RegistrationRequestLen = ::ElemLen; + impl RegistrationRequest { /// Only used for testing purposes #[cfg(test)] @@ -96,8 +186,8 @@ impl RegistrationRequest { } /// Serialization into bytes - pub fn serialize(&self) -> Result, ProtocolError> { - Ok(self.blinded_element.serialize()) + pub fn serialize(&self) -> GenericArray> { + self.blinded_element.value().to_arr() } /// Deserialization from bytes @@ -108,14 +198,23 @@ impl RegistrationRequest { } } +/// Length of [`RegistrationResponse`] in bytes for serialization. +#[allow(type_alias_bounds)] +pub type RegistrationResponseLen = + Sum<::ElemLen, ::PkLen>; + impl RegistrationResponse { /// Serialization into bytes - pub fn serialize(&self) -> Result, ProtocolError> { - Ok([ - self.evaluation_element.serialize(), - self.server_s_pk.to_vec(), - ] - .concat()) + pub fn serialize(&self) -> GenericArray> + where + // RegistrationResponse: KgPk + KePk + ::ElemLen: Add<::PkLen>, + RegistrationResponseLen: ArrayLength, + { + self.evaluation_element + .value() + .to_arr() + .concat(self.server_s_pk.to_arr()) } /// Deserialization from bytes @@ -147,15 +246,30 @@ impl RegistrationResponse { } } +/// Length of [`RegistrationUpload`] in bytes for serialization. +#[allow(type_alias_bounds)] +pub type RegistrationUploadLen = Sum< + Sum<::PkLen, ::OutputSize>, + EnvelopeLen, +>; + impl RegistrationUpload { /// Serialization into bytes - pub fn serialize(&self) -> Result, ProtocolError> { - Ok([ - self.client_s_pk.to_arr().to_vec(), - self.masking_key.to_vec(), - self.envelope.serialize(), - ] - .concat()) + pub fn serialize(&self) -> GenericArray> + where + // Envelope: Nonce + Hash + NonceLen: Add<::OutputSize>, + EnvelopeLen: ArrayLength, + // RegistrationUpload: (KePk + Hash) + Envelope + ::PkLen: Add<::OutputSize>, + Sum<::PkLen, ::OutputSize>: + ArrayLength + Add>, + RegistrationUploadLen: ArrayLength, + { + self.client_s_pk + .to_arr() + .concat(self.masking_key.clone()) + .concat(self.envelope.serialize()) } /// Deserialization from bytes @@ -181,25 +295,43 @@ impl RegistrationUpload { rng: &mut R, server_setup: &ServerSetup, ) -> Self { - let mut masking_key = alloc::vec![0u8; ::OutputSize::USIZE]; + let mut masking_key = GenericArray::<_, ::OutputSize>::default(); rng.fill_bytes(&mut masking_key); Self { envelope: Envelope::::dummy(), - masking_key: GenericArray::clone_from_slice(&masking_key), + masking_key, client_s_pk: server_setup.fake_keypair.public().clone(), } } } +/// Length of [`CredentialRequest`] in bytes for serialization. +#[allow(type_alias_bounds)] +pub type CredentialRequestLen = + Sum<::ElemLen, Ke1MessageLen>; + impl CredentialRequest { /// Serialization into bytes - pub fn serialize(&self) -> Result, ProtocolError> { - Ok([ - self.blinded_element.serialize(), - self.ke1_message.to_bytes(), - ] - .concat()) + pub fn serialize(&self) -> GenericArray> + where + // CredentialRequest: KgPk + Ke1Message + ::ElemLen: Add>, + CredentialRequestLen: ArrayLength, + { + self.blinded_element + .value() + .to_arr() + .concat(self.ke1_message.to_bytes()) + } + + pub(crate) fn serialize_iter<'a>( + blinded_element: &'a GenericArray::ElemLen>, + ke1_message: &'a GenericArray>, + ) -> impl Iterator { + // MSRV: array `into_iter` isn't available in 1.51 + #[allow(deprecated)] + IntoIter::new([blinded_element.as_slice(), ke1_message]) } /// Deserialization from bytes @@ -220,7 +352,7 @@ impl CredentialRequest { } let ke1_message = - >::KE1Message::from_bytes::( + >::KE1Message::from_bytes( &checked_slice[elem_len..], )?; @@ -239,26 +371,51 @@ impl CredentialRequest { } } +/// Length of [`CredentialResponse`] in bytes for serialization. +#[allow(type_alias_bounds)] +pub type CredentialResponseLen = + Sum, Ke2MessageLen>; + +#[allow(type_alias_bounds)] +pub(crate) type CredentialResponseWithoutKeLen = + Sum::ElemLen, NonceLen>, MaskedResponseLen>; + impl CredentialResponse { /// Serialization into bytes - 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()) + pub fn serialize(&self) -> GenericArray> + where + // CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse + ::ElemLen: Add, + Sum<::ElemLen, NonceLen>: + ArrayLength + Add>, + CredentialResponseWithoutKeLen: ArrayLength, + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, + // CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message + CredentialResponseWithoutKeLen: Add>, + CredentialResponseLen: ArrayLength, + { + self.evaluation_element + .value() + .to_arr() + .concat(self.masking_nonce) + .concat(self.masked_response.serialize()) + .concat(self.ke2_message.to_bytes()) } - pub(crate) fn serialize_without_ke( - beta: &CS::OprfGroup, - masking_nonce: &[u8], - masked_response: &[u8], - ) -> Vec { - [&beta.to_arr(), masking_nonce, masked_response].concat() + pub(crate) fn serialize_without_ke<'a>( + beta: &'a GenericArray::ElemLen>, + masking_nonce: &'a GenericArray, + masked_response: &'a MaskedResponse, + ) -> impl Iterator { + // MSRV: array `into_iter` isn't available in 1.51 + #[allow(deprecated)] + IntoIter::new([beta.as_slice(), masking_nonce.as_slice()]) + .into_iter() + .chain(masked_response.iter()) } /// Deserialization from bytes @@ -287,12 +444,13 @@ impl CredentialResponse { return Err(ProtocolError::IdentityGroupElementError); } - let masking_nonce = checked_slice[elem_len..elem_len + nonce_len].to_vec(); - let masked_response = checked_slice - [elem_len + nonce_len..elem_len + nonce_len + masked_response_len] - .to_vec(); + let masking_nonce = + GenericArray::clone_from_slice(&checked_slice[elem_len..elem_len + nonce_len]); + let masked_response = MaskedResponse::deserialize( + &checked_slice[elem_len + nonce_len..elem_len + nonce_len + masked_response_len], + ); let ke2_message = - >::KE2Message::from_bytes::( + >::KE2Message::from_bytes( &checked_slice[elem_len + nonce_len + masked_response_len..], )?; @@ -310,94 +468,27 @@ impl CredentialResponse { pub fn set_evaluation_element_for_testing(&self, beta: CS::OprfGroup) -> Self { Self { evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta), - masking_nonce: self.masking_nonce.clone(), + masking_nonce: self.masking_nonce, masked_response: self.masked_response.clone(), ke2_message: self.ke2_message.clone(), } } } +/// Length of [`CredentialFinalization`] in bytes for serialization. +#[allow(type_alias_bounds)] +pub type CredentialFinalizationLen = Ke3MessageLen; + impl CredentialFinalization { /// Serialization into bytes - pub fn serialize(&self) -> Result, ProtocolError> { - Ok(self.ke3_message.to_bytes()) + pub fn serialize(&self) -> GenericArray> { + self.ke3_message.to_bytes() } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let ke3_message = - >::KE3Message::from_bytes::( - input, - )?; + >::KE3Message::from_bytes(input)?; Ok(Self { ke3_message }) } } - -/////////////////////////// -// Trait Implementations // -// ===================== // -/////////////////////////// - -impl_clone_for!( - struct RegistrationRequest, - [blinded_element], -); -impl_debug_eq_hash_for!(struct RegistrationRequest, [blinded_element], [CS::OprfGroup, CS::Hash]); -impl_serialize_and_deserialize_for!(RegistrationRequest); - -impl_clone_for!( - struct RegistrationResponse, - [evaluation_element, server_s_pk], -); -impl_debug_eq_hash_for!( - struct RegistrationResponse, - [evaluation_element, server_s_pk], - [CS::OprfGroup, CS::Hash], -); -impl_serialize_and_deserialize_for!(RegistrationResponse); - -impl_clone_for!( - struct RegistrationUpload, - [envelope, masking_key, client_s_pk], -); -impl_debug_eq_hash_for!( - struct RegistrationUpload, - [envelope, masking_key, client_s_pk], -); -impl_serialize_and_deserialize_for!(RegistrationUpload); - -impl_clone_for!( - struct CredentialRequest, - [blinded_element, ke1_message], -); -impl_debug_eq_hash_for!( - struct CredentialRequest, - [blinded_element, ke1_message], - [ - CS::OprfGroup, - >::KE1Message - ], -); -impl_serialize_and_deserialize_for!(CredentialRequest); - -impl_clone_for!( - struct CredentialResponse, - [evaluation_element, masking_nonce, masked_response, ke2_message], -); -impl_debug_eq_hash_for!( - struct CredentialResponse, - [evaluation_element, masking_nonce, masked_response, ke2_message], - [ - CS::OprfGroup, - >::KE2Message, - ], -); -impl_serialize_and_deserialize_for!(CredentialResponse); - -impl_clone_for!(struct CredentialFinalization, [ke3_message]); -impl_debug_eq_hash_for!( - struct CredentialFinalization, - [ke3_message], - [>::KE3Message], -); -impl_serialize_and_deserialize_for!(CredentialFinalization); diff --git a/src/opaque.rs b/src/opaque.rs old mode 100644 new mode 100755 index af8fa69..5811066 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -9,29 +9,36 @@ use crate::{ ciphersuite::CipherSuite, - envelope::Envelope, + envelope::{Envelope, EnvelopeLen}, errors::{utils::check_slice_size, InternalError, ProtocolError}, hash::Hash, key_exchange::{ group::KeGroup, - traits::{FromBytes, KeyExchange, ToBytes}, + traits::{FromBytes, Ke1MessageLen, Ke2StateLen, KeyExchange, ToBytes}, + tripledh::NonceLen, }, keypair::{KeyPair, PrivateKey, PublicKey, SecretKey}, - serialization::{serialize, tokenize}, + messages::{CredentialRequestLen, RegistrationUploadLen}, + serialization::{tokenize, Serialize}, slow_hash::SlowHash, CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, }; -use alloc::vec; use alloc::vec::Vec; +use core::array::IntoIter; use core::marker::PhantomData; -use digest::Digest; -use generic_array::{typenum::Unsigned, GenericArray}; -use hkdf::Hkdf; +use core::ops::Add; +use derive_where::DeriveWhere; +use digest::{Digest, FixedOutput}; +use generic_array::sequence::Concat; +use generic_array::{ + typenum::{Sum, Unsigned, U2}, + ArrayLength, GenericArray, +}; +use hkdf::{Hkdf, HkdfExtract}; use rand::{CryptoRng, RngCore}; use subtle::ConstantTimeEq; use voprf::group::Group; -use zeroize::Zeroize; /////////////// // Constants // @@ -50,13 +57,19 @@ const STR_OPAQUE_DERIVE_KEY_PAIR: &[u8; 20] = b"OPAQUE-DeriveKeyPair"; /// The state elements the server holds upon setup #[cfg_attr( - feature = "serialize", - derive(serde::Deserialize, serde::Serialize), - serde(bound( - deserialize = "KeyPair: serde::Deserialize<'de>", - serialize = "KeyPair: serde::Serialize" - )) + feature = "serde", + derive(serde_::Deserialize, serde_::Serialize), + serde( + bound( + deserialize = "KeyPair: serde_::Deserialize<'de>", + serialize = "KeyPair: serde_::Serialize" + ), + crate = "serde_" + ) )] +#[derive(DeriveWhere)] +#[derive_where(Clone)] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; S)] pub struct ServerSetup< CS: CipherSuite, S: SecretKey = PrivateKey<::KeGroup>, @@ -66,67 +79,77 @@ pub struct ServerSetup< pub(crate) fake_keypair: KeyPair, } -// Cannot be derived because it would require for CS to be bound. -impl_clone_for!( - struct ServerSetup, - [oprf_seed, keypair, fake_keypair], -); -impl_debug_eq_hash_for!( - struct ServerSetup, - [oprf_seed, oprf_seed, fake_keypair], -); - /// The state elements the client holds to register itself +#[derive(DeriveWhere)] +#[derive_where(Clone, Zeroize(drop))] +#[derive_where( + Debug, Eq, Hash, PartialEq; + voprf::NonVerifiableClient, + voprf::BlindedElement, +)] pub struct ClientRegistration { pub(crate) oprf_client: voprf::NonVerifiableClient, pub(crate) blinded_element: voprf::BlindedElement, } -impl_clone_for!(struct ClientRegistration, [oprf_client, blinded_element]); -impl_debug_eq_hash_for!( - struct ClientRegistration, - [oprf_client], - [voprf::NonVerifiableClient], -); -impl_serialize_and_deserialize_for!(ClientRegistration); +impl_serialize_and_deserialize_for!(ClientRegistration; serde_::ser::Error::custom); /// The state elements the server holds to record a registration +#[derive(DeriveWhere)] +#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize(drop))] pub struct ServerRegistration(RegistrationUpload); -impl_clone_for!(tuple ServerRegistration, [0]); -impl_debug_eq_hash_for!( - tuple ServerRegistration, - [0], +impl_serialize_and_deserialize_for!( + ServerRegistration + where + // Envelope: Nonce + Hash + NonceLen: Add<::OutputSize>, + EnvelopeLen: ArrayLength, + // RegistrationUpload: (KePk + Hash) + Envelope + ::PkLen: Add<::OutputSize>, + Sum<::PkLen, ::OutputSize>: + ArrayLength | Add>, + RegistrationUploadLen: ArrayLength, + // ServerRegistration = RegistrationUpload ); -impl_serialize_and_deserialize_for!(ServerRegistration); /// The state elements the client holds to perform a login +#[derive(DeriveWhere)] +#[derive_where(Clone, Zeroize(drop))] +#[derive_where( + Debug, Eq, Hash, PartialEq; + voprf::NonVerifiableClient, + >::KE1State, + CredentialRequest, +)] pub struct ClientLogin { oprf_client: voprf::NonVerifiableClient, ke1_state: >::KE1State, - serialized_credential_request: Vec, + credential_request: CredentialRequest, } -impl_clone_for!(struct ClientLogin, [oprf_client, ke1_state, serialized_credential_request]); -impl_debug_eq_hash_for!( - struct ClientLogin, - [oprf_client, ke1_state, serialized_credential_request], - [voprf::NonVerifiableClient, >::KE1State], +impl_serialize_and_deserialize_for!( + ClientLogin + where + // CredentialRequest: KgPk + Ke1Message + ::ElemLen: Add>, + CredentialRequestLen: ArrayLength; + serde_::ser::Error::custom ); -impl_serialize_and_deserialize_for!(ClientLogin); /// The state elements the server holds to record a login +#[derive(DeriveWhere)] +#[derive_where(Clone, Zeroize(drop))] +#[derive_where( + Debug, Eq, Hash, PartialEq; + >::KE2State, +)] pub struct ServerLogin { ke2_state: >::KE2State, + #[derive_where(skip(Zeroize))] _cs: PhantomData, } -impl_clone_for!(struct ServerLogin, [ke2_state, _cs]); -impl_debug_eq_hash_for!( - struct ServerLogin, - [ke2_state, _cs], - [>::KE2State], -); impl_serialize_and_deserialize_for!(ServerLogin); //////////////////////////////// @@ -139,36 +162,46 @@ impl_serialize_and_deserialize_for!(ServerLogin); impl ServerSetup> { /// Generate a new instance of server setup - pub fn new(rng: &mut R) -> Result { - let keypair = KeyPair::::generate_random(rng)?; + pub fn new(rng: &mut R) -> Self { + let keypair = KeyPair::generate_random(rng); Self::new_with_key(rng, keypair) } } +/// Length of [`ServerSetup`] in bytes for serialization. +#[allow(type_alias_bounds)] +pub type ServerSetupLen> = + Sum::OutputSize, S::Len>, ::SkLen>; + impl> ServerSetup { /// Create [`ServerSetup`] with the given keypair pub fn new_with_key( rng: &mut R, keypair: KeyPair, - ) -> Result { - let mut seed = vec![0u8; ::OutputSize::USIZE]; - rng.fill_bytes(&mut seed); + ) -> Self { + let mut oprf_seed = GenericArray::default(); + rng.fill_bytes(&mut oprf_seed); - Ok(Self { - oprf_seed: GenericArray::clone_from_slice(&seed[..]), + Self { + oprf_seed, keypair, - fake_keypair: KeyPair::::generate_random(rng)?, - }) + fake_keypair: KeyPair::::generate_random(rng), + } } /// Serialization into bytes - pub fn serialize(&self) -> Result, ProtocolError> { - Ok([ - self.oprf_seed.to_vec(), - self.keypair.private().serialize(), - self.fake_keypair.private().serialize(), - ] - .concat()) + pub fn serialize(&self) -> GenericArray> + where + // ServerSetup: Hash + KeSk + KeSk + ::OutputSize: Add, + Sum<::OutputSize, S::Len>: + ArrayLength + Add<::SkLen>, + ServerSetupLen: ArrayLength, + { + self.oprf_seed + .clone() + .concat(self.keypair.private().serialize()) + .concat(self.fake_keypair.private().to_arr()) } /// Deserialization from bytes @@ -197,36 +230,38 @@ impl> ServerSetup { impl ClientRegistration { /// Serialization into bytes pub fn serialize(&self) -> Result, ProtocolError> { - Ok([ - serialize(&self.oprf_client.serialize(), 2)?, - serialize(&self.blinded_element.serialize(), 2)?, - ] - .concat()) + Ok(chain!( + Serialize::::from(&self.oprf_client.serialize())?.iter(), + Serialize::::from(&self.blinded_element.serialize())?.iter(), + ) + .flatten() + .cloned() + .collect()) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let (serialized_oprf_client, remainder) = tokenize(input, 2)?; - let (serialized_blinded_element, remainder) = tokenize(&remainder, 2)?; + let (serialized_blinded_element, remainder) = tokenize(remainder, 2)?; if !remainder.is_empty() { return Err(ProtocolError::SerializationError); } Ok(Self { - oprf_client: voprf::NonVerifiableClient::deserialize(&serialized_oprf_client)?, - blinded_element: voprf::BlindedElement::deserialize(&serialized_blinded_element)?, + oprf_client: voprf::NonVerifiableClient::deserialize(serialized_oprf_client)?, + blinded_element: voprf::BlindedElement::deserialize(serialized_blinded_element)?, }) } - #[cfg(test)] /// Only used for testing zeroize - pub(crate) fn to_vec(&self) -> Result, ProtocolError> { - Ok([ + #[cfg(test)] + pub(crate) fn to_vec(&self) -> Vec { + [ self.oprf_client.serialize(), self.blinded_element.serialize(), ] - .concat()) + .concat() } /// Returns an initial "blinded" request to send to the server, as well as a ClientRegistration @@ -237,7 +272,7 @@ impl ClientRegistration { let blind_result = blind::(blinding_factor_rng, password)?; Ok(ClientRegistrationStartResult { - message: RegistrationRequest:: { + message: RegistrationRequest { blinded_element: blind_result.message.clone(), }, state: Self { @@ -272,7 +307,7 @@ impl ClientRegistration { params.slow_hash, )?; - let mut masking_key = vec![0u8; ::OutputSize::USIZE]; + let mut masking_key = GenericArray::<_, ::OutputSize>::default(); randomized_pwd_hasher .expand(STR_MASKING_KEY, &mut masking_key) .map_err(|_| InternalError::HkdfError)?; @@ -287,7 +322,7 @@ impl ClientRegistration { Ok(ClientRegistrationFinishResult { message: RegistrationUpload { envelope: result.0, - masking_key: GenericArray::clone_from_slice(&masking_key[..]), + masking_key, client_s_pk: result.1, }, export_key: result.2, @@ -302,9 +337,23 @@ impl ClientRegistration { } } +/// Length of [`ServerRegistration`] in bytes for serialization. +pub type ServerRegistrationLen = RegistrationUploadLen; + impl ServerRegistration { /// Serialization into bytes - pub fn serialize(&self) -> Result, ProtocolError> { + pub fn serialize(&self) -> GenericArray> + where + // Envelope: Nonce + Hash + NonceLen: Add<::OutputSize>, + EnvelopeLen: ArrayLength, + // RegistrationUpload: (KePk + Hash) + Envelope + ::PkLen: Add<::OutputSize>, + Sum<::PkLen, ::OutputSize>: + ArrayLength + Add>, + RegistrationUploadLen: ArrayLength, + // ServerRegistration = RegistrationUpload + { self.0.serialize() } @@ -334,7 +383,7 @@ impl ServerRegistration { server_s_pk: server_setup.keypair.public().clone(), }, #[cfg(test)] - oprf_key: GenericArray::clone_from_slice(&oprf_key), + oprf_key, }) } @@ -358,46 +407,57 @@ impl ServerRegistration { impl ClientLogin { /// Serialization into bytes - pub fn serialize(&self) -> Result, ProtocolError> { - let output: Vec = [ - serialize(&self.oprf_client.serialize(), 2)?, - serialize(&self.serialized_credential_request, 2)?, - serialize(&self.ke1_state.to_bytes(), 2)?, - ] - .concat(); - Ok(output) + pub fn serialize(&self) -> Result, ProtocolError> + where + // CredentialRequest: KgPk + Ke1Message + ::ElemLen: Add>, + CredentialRequestLen: ArrayLength, + { + Ok(chain!( + Serialize::::from(&self.oprf_client.serialize())?.iter(), + Serialize::::from(&self.credential_request.serialize())?.iter(), + Serialize::::from(&self.ke1_state.to_bytes())?.iter(), + ) + .flatten() + .cloned() + .collect()) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { 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 (serialized_credential_request, remainder) = tokenize(remainder, 2)?; + let (ke1_state_bytes, remainder) = tokenize(remainder, 2)?; if !remainder.is_empty() { return Err(ProtocolError::SerializationError); } let ke1_state = - >::KE1State::from_bytes::( - &ke1_state_bytes[..], + >::KE1State::from_bytes( + ke1_state_bytes, )?; Ok(Self { - oprf_client: voprf::NonVerifiableClient::deserialize(&serialized_oprf_client)?, + oprf_client: voprf::NonVerifiableClient::deserialize(serialized_oprf_client)?, ke1_state, - serialized_credential_request, + credential_request: CredentialRequest::deserialize(serialized_credential_request)?, }) } - #[cfg(test)] /// Only used for testing zeroize - pub(crate) fn to_vec(&self) -> Result, ProtocolError> { - Ok([ + #[cfg(test)] + pub(crate) fn to_vec(&self) -> Vec + where + // CredentialRequest: KgPk + Ke1Message + ::ElemLen: Add>, + CredentialRequestLen: ArrayLength, + { + [ self.oprf_client.serialize(), - self.serialized_credential_request.clone(), - self.ke1_state.to_bytes(), + self.credential_request.serialize().to_vec(), + self.ke1_state.to_bytes().to_vec(), ] - .concat()) + .concat() } } @@ -414,14 +474,13 @@ impl ClientLogin { blinded_element: blind_result.message, ke1_message, }; - let serialized_credential_request = credential_request.serialize()?; Ok(ClientLoginStartResult { - message: credential_request, + message: credential_request.clone(), state: Self { oprf_client: blind_result.state, ke1_state, - serialized_credential_request, + credential_request, }, }) } @@ -432,11 +491,17 @@ impl ClientLogin { self, credential_response: CredentialResponse, params: ClientLoginFinishParameters, - ) -> Result, ProtocolError> { + ) -> Result, ProtocolError> + where + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, + { // 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 + if self + .credential_request .blinded_element .value() .ct_eq(&credential_response.evaluation_element.value()) @@ -451,7 +516,7 @@ impl ClientLogin { params.slow_hash, )?; - let mut masking_key = vec![0u8; ::OutputSize::USIZE]; + let mut masking_key = GenericArray::<_, ::OutputSize>::default(); randomized_pwd_hasher .expand(STR_MASKING_KEY, &mut masking_key) .map_err(|_| InternalError::HkdfError)?; @@ -465,13 +530,12 @@ impl ClientLogin { ProtocolError::SerializationError => ProtocolError::InvalidLoginError, err => err, })?; - let server_s_pk_bytes = server_s_pk.to_arr().to_vec(); - let opened_envelope = &envelope + let opened_envelope = envelope .open( randomized_pwd_hasher, - &server_s_pk_bytes, - ¶ms.identifiers, + server_s_pk.clone(), + params.identifiers, ) .map_err(|e| match e { ProtocolError::LibraryError(InternalError::SealOpenHmacError) => { @@ -480,22 +544,28 @@ impl ClientLogin { err => err, })?; + let beta = credential_response.evaluation_element.value().to_arr(); let credential_response_component = CredentialResponse::::serialize_without_ke( - &credential_response.evaluation_element.value(), + &beta, &credential_response.masking_nonce, &credential_response.masked_response, ); + let blinded_element = self.credential_request.blinded_element.value().to_arr(); + let ke1_message = self.credential_request.ke1_message.to_bytes(); + let serialized_credential_request = + CredentialRequest::::serialize_iter(&blinded_element, &ke1_message); + let result = CS::KeyExchange::generate_ke3( credential_response_component, credential_response.ke2_message, &self.ke1_state, - &self.serialized_credential_request, + serialized_credential_request, server_s_pk.clone(), opened_envelope.client_static_keypair.private().clone(), - opened_envelope.id_u.clone(), - opened_envelope.id_s.clone(), - params.context.unwrap_or_default(), + opened_envelope.id_u.iter(), + opened_envelope.id_s.iter(), + params.context.unwrap_or(&[]), )?; Ok(ClientLoginFinishResult { @@ -503,7 +573,7 @@ impl ClientLogin { ke3_message: result.1, }, session_key: result.0, - export_key: opened_envelope.export_key.clone(), + export_key: opened_envelope.export_key, server_s_pk, #[cfg(test)] state: self, @@ -517,8 +587,8 @@ impl ClientLogin { impl ServerLogin { /// Serialization into bytes - pub fn serialize(&self) -> Result, ProtocolError> { - Ok(self.ke2_state.to_bytes()) + pub fn serialize(&self) -> GenericArray> { + self.ke2_state.to_bytes() } /// Deserialization from bytes @@ -526,7 +596,7 @@ impl ServerLogin { Ok(Self { _cs: PhantomData, ke2_state: - >::KE2State::from_bytes::( + >::KE2State::from_bytes( bytes, )?, }) @@ -540,8 +610,18 @@ impl ServerLogin { password_file: Option>, credential_request: CredentialRequest, credential_identifier: &[u8], - params: ServerLoginStartParameters, - ) -> Result, ProtocolError> { + ServerLoginStartParameters { + context, + identifiers, + }: ServerLoginStartParameters, + ) -> Result, ProtocolError> + where + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, + { let record = match password_file { Some(x) => x, None => ServerRegistration::dummy(rng, server_setup), @@ -549,38 +629,37 @@ impl ServerLogin { let client_s_pk = record.0.client_s_pk.clone(); - let (context, optional_ids) = match params { - ServerLoginStartParameters::WithContext(context) => (context, None), - ServerLoginStartParameters::WithIdentifiers(ids) => (Vec::new(), Some(ids)), - ServerLoginStartParameters::WithContextAndIdentifiers(context, ids) => { - (context, Some(ids)) - } + let context = if let Some(context) = context { + context + } else { + &[] }; let server_s_sk = server_setup.keypair.private(); let server_s_pk = server_s_sk.public_key()?; - let mut masking_nonce = vec![0u8; 32]; + let mut masking_nonce = GenericArray::<_, NonceLen>::default(); rng.fill_bytes(&mut masking_nonce); let masked_response = mask_response( &record.0.masking_key, - &masking_nonce, + masking_nonce.as_slice(), &server_s_pk, &record.0.envelope, ) .map_err(ProtocolError::into_custom)?; - let (id_u, id_s) = bytestrings_from_identifiers( - &optional_ids, - &client_s_pk.to_arr(), - &server_s_pk.to_arr(), + let (id_u, id_s) = bytestrings_from_identifiers::( + identifiers, + client_s_pk.to_arr(), + server_s_pk.to_arr(), ) .map_err(ProtocolError::into_custom)?; - let credential_request_bytes = credential_request - .serialize() - .map_err(ProtocolError::into_custom)?; + let blinded_element = credential_request.blinded_element.value().to_arr(); + let ke1_message = credential_request.ke1_message.to_bytes(); + let credential_request_bytes = + CredentialRequest::::serialize_iter(&blinded_element, &ke1_message); let oprf_key = oprf_key_from_seed::( &server_setup.oprf_seed, @@ -594,11 +673,9 @@ impl ServerLogin { .map_err(|e| ProtocolError::into_custom(e.into()))?; let evaluation_element = evaluate_result.message; - let credential_response_component = CredentialResponse::::serialize_without_ke( - &evaluation_element.value(), - &masking_nonce, - &masked_response, - ); + let beta = evaluation_element.value().to_arr(); + let credential_response_component = + CredentialResponse::::serialize_without_ke(&beta, &masking_nonce, &masked_response); let result = CS::KeyExchange::generate_ke2( rng, @@ -607,8 +684,8 @@ impl ServerLogin { credential_request.ke1_message, client_s_pk, server_s_sk.clone(), - id_u, - id_s, + id_u.iter(), + id_s.iter(), context, )?; @@ -660,37 +737,27 @@ impl ServerLogin { ///////////////////////// /// Options for specifying custom identifiers -#[derive(Clone)] -pub enum Identifiers { - /// Supply only a client identifier - ClientIdentifier(Vec), - /// Supply only a server identifier - ServerIdentifier(Vec), - /// Supply a client and server identifier - ClientAndServerIdentifiers(Vec, Vec), +#[derive(Clone, Copy, Debug, Default)] +pub struct Identifiers<'a> { + /// Client identifier + pub client: Option<&'a [u8]>, + /// Server identifier + pub server: Option<&'a [u8]>, } /// Optional parameters for client registration finish -#[derive(Clone)] -pub struct ClientRegistrationFinishParameters<'h, CS: CipherSuite> { +#[derive(DeriveWhere)] +#[derive_where(Clone, Default)] +pub struct ClientRegistrationFinishParameters<'i, 'h, CS: CipherSuite> { /// Specifying the identifiers idU and idS - pub identifiers: Option, + pub identifiers: Identifiers<'i>, /// Specifying a configuration for the slow hash pub slow_hash: Option<&'h CS::SlowHash>, } -impl<'h, CS: CipherSuite> Default for ClientRegistrationFinishParameters<'h, CS> { - fn default() -> Self { - Self { - identifiers: None, - slow_hash: None, - } - } -} - -impl<'h, CS: CipherSuite> ClientRegistrationFinishParameters<'h, CS> { +impl<'i, 'h, CS: CipherSuite> ClientRegistrationFinishParameters<'i, 'h, CS> { /// Create a new [`ClientRegistrationFinishParameters`] - pub fn new(identifiers: Option, slow_hash: Option<&'h CS::SlowHash>) -> Self { + pub fn new(identifiers: Identifiers<'i>, slow_hash: Option<&'h CS::SlowHash>) -> Self { Self { identifiers, slow_hash, @@ -699,6 +766,8 @@ impl<'h, CS: CipherSuite> ClientRegistrationFinishParameters<'h, CS> { } /// Contains the fields that are returned by a client registration start +#[derive(DeriveWhere)] +#[derive_where(Clone)] pub struct ClientRegistrationStartResult { /// The registration request message to be sent to the server pub message: RegistrationRequest, @@ -706,17 +775,9 @@ pub struct ClientRegistrationStartResult { pub state: ClientRegistration, } -// Cannot be derived because it would require for CS to be Clone. -impl Clone for ClientRegistrationStartResult { - fn clone(&self) -> Self { - Self { - message: self.message.clone(), - state: self.state.clone(), - } - } -} - /// Contains the fields that are returned by a client registration finish +#[derive(DeriveWhere)] +#[derive_where(Clone)] pub struct ClientRegistrationFinishResult { /// The registration upload message to be sent to the server pub message: RegistrationUpload, @@ -729,31 +790,16 @@ pub struct ClientRegistrationFinishResult { pub state: ClientRegistration, /// AuthKey, only used in tests #[cfg(test)] - pub auth_key: Vec, + pub auth_key: GenericArray::OutputSize>, /// Password derived key, only used in tests #[cfg(test)] pub randomized_pwd: GenericArray::OutputSize>, } -// Cannot be derived because it would require for CS to be Clone. -impl Clone for ClientRegistrationFinishResult { - fn clone(&self) -> Self { - Self { - message: self.message.clone(), - export_key: self.export_key.clone(), - server_s_pk: self.server_s_pk.clone(), - #[cfg(test)] - state: self.state.clone(), - #[cfg(test)] - auth_key: self.auth_key.clone(), - #[cfg(test)] - randomized_pwd: self.randomized_pwd.clone(), - } - } -} - /// Contains the fields that are returned by a server registration start. /// Note that there is no state output in this step +#[derive(DeriveWhere)] +#[derive_where(Clone)] pub struct ServerRegistrationStartResult { /// The registration resposne message to send to the client pub message: RegistrationResponse, @@ -762,18 +808,9 @@ pub struct ServerRegistrationStartResult { pub oprf_key: GenericArray::ScalarLen>, } -// Cannot be derived because it would require for CS to be Clone. -impl Clone for ServerRegistrationStartResult { - fn clone(&self) -> Self { - Self { - message: self.message.clone(), - #[cfg(test)] - oprf_key: self.oprf_key.clone(), - } - } -} - /// Contains the fields that are returned by a client login start +#[derive(DeriveWhere)] +#[derive_where(Clone)] pub struct ClientLoginStartResult { /// The message to send to the server to begin the login protocol pub message: CredentialRequest, @@ -781,42 +818,23 @@ pub struct ClientLoginStartResult { pub state: ClientLogin, } -// Cannot be derived because it would require for CS to be Clone. -impl Clone for ClientLoginStartResult { - fn clone(&self) -> Self { - Self { - message: self.message.clone(), - state: self.state.clone(), - } - } -} - /// Optional parameters for client login finish -#[derive(Clone)] -pub struct ClientLoginFinishParameters<'h, CS: CipherSuite> { +#[derive(DeriveWhere)] +#[derive_where(Clone, Default)] +pub struct ClientLoginFinishParameters<'c, 'i, 'h, CS: CipherSuite> { /// Specifying a context field that the server must agree on - pub context: Option>, + pub context: Option<&'c [u8]>, /// Specifying a user identifier and server identifier that will be matched against the server - pub identifiers: Option, + pub identifiers: Identifiers<'i>, /// Specifying a configuration for the slow hash pub slow_hash: Option<&'h CS::SlowHash>, } -impl<'h, CS: CipherSuite> Default for ClientLoginFinishParameters<'h, CS> { - fn default() -> Self { - Self { - context: None, - identifiers: None, - slow_hash: None, - } - } -} - -impl<'h, CS: CipherSuite> ClientLoginFinishParameters<'h, CS> { +impl<'c, 'i, 'h, CS: CipherSuite> ClientLoginFinishParameters<'c, 'i, 'h, CS> { /// Create a new [`ClientLoginFinishParameters`] pub fn new( - context: Option>, - identifiers: Option, + context: Option<&'c [u8]>, + identifiers: Identifiers<'i>, slow_hash: Option<&'h CS::SlowHash>, ) -> Self { Self { @@ -828,11 +846,13 @@ impl<'h, CS: CipherSuite> ClientLoginFinishParameters<'h, CS> { } /// Contains the fields that are returned by a client login finish +#[derive(DeriveWhere)] +#[derive_where(Clone)] pub struct ClientLoginFinishResult { /// The message to send to the server to complete the protocol pub message: CredentialFinalization, /// The session key - pub session_key: Vec, + pub session_key: GenericArray::OutputSize>, /// The client-side export key pub export_key: GenericArray::OutputSize>, /// The server's static public key @@ -842,72 +862,44 @@ pub struct ClientLoginFinishResult { pub state: ClientLogin, /// Handshake secret, only used in tests #[cfg(test)] - pub handshake_secret: Vec, + pub handshake_secret: GenericArray::OutputSize>, /// Client MAC key, only used in tests #[cfg(test)] pub client_mac_key: GenericArray::OutputSize>, } -// Cannot be derived because it would require for CS to be Clone. -impl Clone for ClientLoginFinishResult { - fn clone(&self) -> Self { - Self { - message: self.message.clone(), - session_key: self.session_key.clone(), - export_key: self.export_key.clone(), - server_s_pk: self.server_s_pk.clone(), - #[cfg(test)] - state: self.state.clone(), - #[cfg(test)] - handshake_secret: self.handshake_secret.clone(), - #[cfg(test)] - client_mac_key: self.client_mac_key.clone(), - } - } -} - /// Contains the fields that are returned by a server login finish +#[derive(DeriveWhere)] +#[derive_where(Clone)] +#[cfg_attr(not(test), derive_where(Debug))] +#[cfg_attr(test, derive_where(Debug; ServerLogin))] pub struct ServerLoginFinishResult { /// The session key between client and server - pub session_key: Vec, + pub session_key: GenericArray::OutputSize>, _cs: PhantomData, /// Instance of the ClientRegistration, only used in tests for checking zeroize #[cfg(test)] pub state: ServerLogin, } -// Cannot be derived because it would require for CS to be Clone. -impl Clone for ServerLoginFinishResult { - fn clone(&self) -> Self { - Self { - session_key: self.session_key.clone(), - _cs: PhantomData, - #[cfg(test)] - state: self.state.clone(), - } - } -} - /// Optional parameters for server login start -#[derive(Clone)] -pub enum ServerLoginStartParameters { +#[derive(Clone, Debug, Default)] +pub struct ServerLoginStartParameters<'c, 'i> { /// Specifying a context field that the client must agree on - WithContext(Vec), + pub context: Option<&'c [u8]>, /// Specifying a user identifier and server identifier that will be matched against the client - WithIdentifiers(Identifiers), - /// Specifying a context field that the client must agree on, - /// along with a user identifier and and server identifier that will be matched against the client - /// (in that order) - WithContextAndIdentifiers(Vec, Identifiers), -} - -impl Default for ServerLoginStartParameters { - fn default() -> Self { - Self::WithContext(Vec::new()) - } + pub identifiers: Identifiers<'i>, } /// Contains the fields that are returned by a server login start +#[derive(DeriveWhere)] +#[derive_where(Clone)] +#[derive_where( + Debug; + CS::OprfGroup, + >::KE2Message, + >::KE2State, +)] pub struct ServerLoginStartResult { /// The message to send back to the client pub message: CredentialResponse, @@ -915,7 +907,7 @@ pub struct ServerLoginStartResult { pub state: ServerLogin, /// Handshake secret, only used in tests #[cfg(test)] - pub handshake_secret: Vec, + pub handshake_secret: GenericArray::OutputSize>, /// Server MAC key, only used in tests #[cfg(test)] pub server_mac_key: GenericArray::OutputSize>, @@ -924,22 +916,6 @@ pub struct ServerLoginStartResult { pub oprf_key: GenericArray::ScalarLen>, } -// Cannot be derived because it would require for CS to be Clone. -impl Clone for ServerLoginStartResult { - fn clone(&self) -> Self { - Self { - message: self.message.clone(), - state: self.state.clone(), - #[cfg(test)] - handshake_secret: self.handshake_secret.clone(), - #[cfg(test)] - server_mac_key: self.server_mac_key.clone(), - #[cfg(test)] - oprf_key: self.oprf_key.clone(), - } - } -} - //////////////////////////////////////////////// // Helper functions and Trait Implementations // // ========================================== // @@ -968,26 +944,72 @@ fn get_password_derived_key( } .map_err(ProtocolError::from)?; - Ok(Hkdf::::extract( - None, - &[oprf_output.to_vec(), hardened_output].concat(), - )) + let mut hkdf = HkdfExtract::::new(None); + hkdf.input_ikm(&oprf_output); + hkdf.input_ikm(&hardened_output); + Ok(hkdf.finalize()) } fn oprf_key_from_seed( oprf_seed: &GenericArray, credential_identifier: &[u8], -) -> Result, ProtocolError> { - let mut ikm = vec![0u8; G::ScalarLen::USIZE]; +) -> Result, ProtocolError> { + let mut ikm = GenericArray::<_, G::ScalarLen>::default(); Hkdf::::from_prk(oprf_seed) - .map_err(|_| InternalError::HkdfError)? - .expand(&[credential_identifier, STR_OPRF_KEY].concat(), &mut ikm) - .map_err(|_| InternalError::HkdfError)?; + .ok() + .and_then(|hkdf| { + hkdf.expand_multi_info(&[credential_identifier, STR_OPRF_KEY], &mut ikm) + .ok() + }) + .ok_or(InternalError::HkdfError)?; Ok(G::scalar_as_bytes(G::hash_to_scalar::( - Some(&ikm[..]), + Some(ikm.as_slice()), GenericArray::from(*STR_OPAQUE_DERIVE_KEY_PAIR), - )?) - .to_vec()) + )?)) +} + +#[derive(DeriveWhere)] +#[derive_where(Clone)] +#[derive_where(Debug, Eq, Hash, PartialEq)] +pub(crate) struct MaskedResponse { + pub(crate) nonce: GenericArray, + pub(crate) hash: GenericArray::OutputSize>, + pub(crate) pk: GenericArray::PkLen>, +} + +#[allow(type_alias_bounds)] +pub(crate) type MaskedResponseLen = + Sum::OutputSize>, ::PkLen>; + +impl MaskedResponse { + pub(crate) fn serialize(&self) -> GenericArray> + where + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, + { + self.nonce.concat(self.hash.clone()).concat(self.pk.clone()) + } + + pub(crate) fn deserialize(bytes: &[u8]) -> Self { + let nonce = NonceLen::USIZE; + let hash = nonce + ::OutputSize::USIZE; + let pk = hash + ::PkLen::USIZE; + + Self { + nonce: GenericArray::clone_from_slice(&bytes[..nonce]), + hash: GenericArray::clone_from_slice(&bytes[nonce..hash]), + pk: GenericArray::clone_from_slice(&bytes[hash..pk]), + } + } + + pub(crate) fn iter(&self) -> impl Iterator { + // MSRV: array `into_iter` isn't available in 1.51 + #[allow(deprecated)] + IntoIter::new([self.nonce.as_slice(), &self.hash, &self.pk]) + } } fn mask_response( @@ -995,46 +1017,60 @@ fn mask_response( masking_nonce: &[u8], server_s_pk: &PublicKey, envelope: &Envelope, -) -> Result, ProtocolError> { - let mut xor_pad = vec![0u8; ::PkLen::USIZE + Envelope::::len()]; +) -> Result, ProtocolError> +where + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, +{ + let mut xor_pad = GenericArray::<_, MaskedResponseLen>::default(); + Hkdf::::from_prk(masking_key) .map_err(|_| InternalError::HkdfError)? - .expand( - &[masking_nonce, STR_CREDENTIAL_RESPONSE_PAD].concat(), - &mut xor_pad, - ) + .expand_multi_info(&[masking_nonce, STR_CREDENTIAL_RESPONSE_PAD], &mut xor_pad) .map_err(|_| InternalError::HkdfError)?; - let plaintext = [&server_s_pk.to_arr()[..], &envelope.serialize()].concat(); + for (x1, x2) in xor_pad.iter_mut().zip( + server_s_pk + .to_arr() + .as_slice() + .iter() + .chain(envelope.serialize().iter()), + ) { + *x1 ^= x2 + } - Ok(xor_pad - .iter() - .zip(plaintext.iter()) - .map(|(&x1, &x2)| x1 ^ x2) - .collect()) + Ok(MaskedResponse::deserialize(&xor_pad)) } fn unmask_response( masking_key: &[u8], masking_nonce: &[u8], - masked_response: &[u8], -) -> Result<(PublicKey, Envelope), ProtocolError> { - let mut xor_pad = vec![0u8; ::PkLen::USIZE + Envelope::::len()]; + masked_response: &MaskedResponse, +) -> Result<(PublicKey, Envelope), ProtocolError> +where + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, +{ + let mut xor_pad = GenericArray::<_, MaskedResponseLen>::default(); + Hkdf::::from_prk(masking_key) .map_err(|_| InternalError::HkdfError)? - .expand( - &[masking_nonce, STR_CREDENTIAL_RESPONSE_PAD].concat(), - &mut xor_pad, - ) + .expand_multi_info(&[masking_nonce, STR_CREDENTIAL_RESPONSE_PAD], &mut xor_pad) .map_err(|_| InternalError::HkdfError)?; - let plaintext: Vec = xor_pad - .iter() - .zip(masked_response.iter()) - .map(|(&x1, &x2)| x1 ^ x2) - .collect(); + + for (x1, x2) in xor_pad.iter_mut().zip(masked_response.iter().flatten()) { + *x1 ^= x2 + } + let key_len = ::PkLen::USIZE; - let unchecked_server_s_pk = PublicKey::from_bytes(&plaintext[..key_len])?; - let envelope = Envelope::deserialize(&plaintext[key_len..])?; + let unchecked_server_s_pk = PublicKey::from_bytes(&xor_pad[..key_len])?; + let envelope = Envelope::deserialize(&xor_pad[key_len..])?; // Ensure that public key is valid let server_s_pk = KeyPair::::check_public_key(unchecked_server_s_pk) @@ -1043,21 +1079,24 @@ fn unmask_response( Ok((server_s_pk, envelope)) } -pub(crate) fn bytestrings_from_identifiers( - ids: &Option, - client_s_pk: &[u8], - server_s_pk: &[u8], -) -> Result<(Vec, Vec), ProtocolError> { - let (client_identity, server_identity): (Vec, Vec) = match ids { - None => (client_s_pk.to_vec(), server_s_pk.to_vec()), - Some(Identifiers::ClientIdentifier(id_u)) => (id_u.clone(), server_s_pk.to_vec()), - Some(Identifiers::ServerIdentifier(id_s)) => (client_s_pk.to_vec(), id_s.clone()), - Some(Identifiers::ClientAndServerIdentifiers(id_u, id_s)) => (id_u.clone(), id_s.clone()), +#[allow(clippy::type_complexity)] +pub(crate) fn bytestrings_from_identifiers( + ids: Identifiers, + client_s_pk: GenericArray, + server_s_pk: GenericArray, +) -> Result<(Serialize, Serialize), ProtocolError> { + let client_identity = if let Some(client) = ids.client { + Serialize::::from(client)? + } else { + Serialize::::from_owned(client_s_pk)? }; - Ok(( - serialize(&client_identity, 2)?, - serialize(&server_identity, 2)?, - )) + let server_identity = if let Some(server) = ids.server { + Serialize::::from(server)? + } else { + Serialize::::from_owned(server_s_pk)? + }; + + Ok((client_identity, server_identity)) } /// Internal function for computing the blind result by calling the @@ -1076,12 +1115,10 @@ fn blind( #[cfg(test)] let result = { - let mut blind_bytes = vec![0u8; ::ScalarLen::USIZE]; + let mut blind_bytes = GenericArray::default(); let blind = loop { rng.fill_bytes(&mut blind_bytes); - let scalar = ::from_scalar_slice_unchecked( - &GenericArray::clone_from_slice(&blind_bytes), - )?; + let scalar = ::from_scalar_slice_unchecked(&blind_bytes)?; match scalar .ct_eq(&::scalar_zero()) .into() @@ -1095,62 +1132,3 @@ fn 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.oprf_client.zeroize(); - self.blinded_element.zeroize(); - } -} - -impl Drop for ClientRegistration { - fn drop(&mut self) { - self.zeroize(); - } -} - -// This can't be derived because of the use of a phantom parameter -impl Zeroize for ServerRegistration { - fn zeroize(&mut self) { - self.0.envelope.zeroize(); - self.0.masking_key.zeroize(); - self.0.client_s_pk.zeroize(); - } -} - -impl Drop for ServerRegistration { - fn drop(&mut self) { - self.zeroize(); - } -} - -// This can't be derived because of the use of a phantom parameter -impl Zeroize for ClientLogin { - fn zeroize(&mut self) { - self.oprf_client.zeroize(); - self.ke1_state.zeroize(); - self.serialized_credential_request.zeroize(); - } -} - -impl Drop for ClientLogin { - fn drop(&mut self) { - self.zeroize(); - } -} - -// This can't be derived because of the use of a phantom parameter -impl Zeroize for ServerLogin { - fn zeroize(&mut self) { - self.ke2_state.zeroize(); - } -} - -impl Drop for ServerLogin { - fn drop(&mut self) { - self.zeroize(); - } -} diff --git a/src/serialization/mod.rs b/src/serialization/mod.rs index be5d288..4e4af82 100644 --- a/src/serialization/mod.rs +++ b/src/serialization/mod.rs @@ -6,26 +6,33 @@ // of this source tree. use crate::errors::ProtocolError; -use alloc::vec::Vec; +use core::marker::PhantomData; +use digest::Update; +use generic_array::{ + typenum::{U0, U2}, + ArrayLength, GenericArray, +}; +use hmac::Mac; // Corresponds to the I2OSP() function from RFC8017 -pub(crate) fn i2osp(input: usize, length: usize) -> Result, ProtocolError> { - let sizeof_usize = core::mem::size_of::(); +pub(crate) fn i2osp>( + input: usize, +) -> Result, ProtocolError> { + const SIZEOF_USIZE: usize = core::mem::size_of::(); // Check if input >= 256^length - if (sizeof_usize as u32 - input.leading_zeros() / 8) > length as u32 { + if (SIZEOF_USIZE as u32 - input.leading_zeros() / 8) > L::U32 { return Err(ProtocolError::SerializationError); } - if length <= sizeof_usize { - return Ok((&input.to_be_bytes()[sizeof_usize - length..]).to_vec()); + if L::USIZE <= SIZEOF_USIZE { + return Ok(GenericArray::clone_from_slice( + &input.to_be_bytes()[SIZEOF_USIZE - L::USIZE..], + )); } - let mut output = alloc::vec![0u8; length]; - output.splice( - length - sizeof_usize..length, - input.to_be_bytes().iter().cloned(), - ); + let mut output = GenericArray::default(); + output[L::USIZE - SIZEOF_USIZE..L::USIZE].copy_from_slice(&input.to_be_bytes()); Ok(output) } @@ -40,17 +47,94 @@ pub(crate) fn os2ip(input: &[u8]) -> Result { Ok(usize::from_be_bytes(output_array)) } -// Computes I2OSP(len(input), max_bytes) || input -pub(crate) fn serialize(input: &[u8], max_bytes: usize) -> Result, ProtocolError> { - Ok([&i2osp(input.len(), max_bytes)?, input].concat()) +/// Computes `I2OSP(len(input), max_bytes) || input` and helps hold output without allocation. +pub(crate) struct Serialize< + 'a, + L1: ArrayLength, + L2: ArrayLength = U0, + L3: ArrayLength = U0, +> { + octet: GenericArray, + input: Input<'a, L2, L3>, +} + +enum Input<'a, L1: ArrayLength, L2: ArrayLength> { + Owned(GenericArray), + Borrowed(&'a [u8]), + Label(([&'a [u8]; 2], PhantomData)), +} + +impl<'a, L1: ArrayLength, L2: ArrayLength, L3: ArrayLength> Serialize<'a, L1, L2, L3> { + // Variation of `serialize` that takes a borrowed `input + pub(crate) fn from(input: &'a [u8]) -> Result, ProtocolError> { + Ok(Serialize { + octet: i2osp::(input.len())?, + input: Input::Borrowed(input), + }) + } + + // Variation of `serialize` that takes an owned `input` + pub(crate) fn from_owned( + input: GenericArray, + ) -> Result, ProtocolError> { + Ok(Serialize { + octet: i2osp::(input.len())?, + input: Input::Owned(input), + }) + } + + // Variation of `serialize` that takes a label + pub(crate) fn from_label( + opaque: &'a [u8], + label: &'a [u8], + ) -> Result, ProtocolError> { + Ok(Serialize { + octet: i2osp::(opaque.len() + label.len())?, + input: Input::Label(([opaque, label], PhantomData)), + }) + } + + pub(crate) fn iter(&self) -> impl Iterator { + // Some magic to make it output the same type in all branches. + Some(self.octet.as_slice()) + .into_iter() + .chain(match &self.input { + Input::Owned(bytes) => Some(bytes.as_slice()), + Input::Borrowed(bytes) => Some(*bytes), + Input::Label(_) => None, + }) + .chain(if let Input::Label((iter, _)) = &self.input { + Some(iter[0]).into_iter().chain(Some(iter[1]).into_iter()) + } else { + None.into_iter().chain(None) + }) + } +} + +impl<'a, L1: ArrayLength, L2: ArrayLength> Serialize<'a, L1, L2, U0> { + pub(crate) fn to_array_2(&self) -> [&[u8]; 2] { + let input = match &self.input { + Input::Borrowed(value) => value, + Input::Owned(value) => value.as_slice(), + _ => unreachable!("unexpected `Serialize` constructed with wrong generics"), + }; + + [self.octet.as_slice(), input] + } +} + +impl<'a, L1: ArrayLength, L2: ArrayLength> Serialize<'a, L1, L2, U2> { + pub(crate) fn to_array_3(&self) -> [&[u8]; 3] { + match self.input { + Input::Label((label, _)) => [self.octet.as_slice(), label[0], label[1]], + _ => unreachable!("unexpected `Serialize` constructed with wrong generics"), + } + } } // Tokenizes an input of the format I2OSP(len(input), max_bytes) || input, outputting // (input, remainder) -pub(crate) fn tokenize( - input: &[u8], - size_bytes: usize, -) -> Result<(Vec, Vec), ProtocolError> { +pub(crate) fn tokenize(input: &[u8], size_bytes: usize) -> Result<(&[u8], &[u8]), ProtocolError> { if size_bytes > core::mem::size_of::() || input.len() < size_bytes { return Err(ProtocolError::SerializationError); } @@ -61,29 +145,69 @@ pub(crate) fn tokenize( } Ok(( - input[size_bytes..size_bytes + size].to_vec(), - input[size_bytes + size..].to_vec(), + &input[size_bytes..size_bytes + size], + &input[size_bytes + size..], )) } +pub(crate) trait UpdateExt { + fn chain_iter<'a>(self, iter: impl Iterator) -> Self; +} + +impl UpdateExt for T { + fn chain_iter<'a>(self, iter: impl Iterator) -> Self { + let mut self_ = self; + + for bytes in iter { + self_ = self_.chain(bytes); + } + + self_ + } +} + +pub(crate) trait MacExt { + fn update_iter<'a>(&mut self, iter: impl Iterator); +} + +impl MacExt for T { + fn update_iter<'a>(&mut self, iter: impl Iterator) { + for bytes in iter { + self.update(bytes); + } + } +} + +/// The purpose of this macro is to simplify [`concat`](alloc::slice::Concat::concat)ing +/// slices into an [`Iterator`] to avoid allocation +macro_rules! chain { + ( + $item1:expr, + $($item2:expr),+$(,)? + ) => { + $item1$(.chain($item2))+ + }; +} + #[cfg(test)] mod tests; #[cfg(test)] mod unit_tests { use super::*; + use generic_array::typenum::{U1, U2}; // Test the error condition for I2OSP #[test] fn test_i2osp_err_check() { - assert!(i2osp(0, 1).is_ok()); + assert!(i2osp::(0).is_ok()); - assert!(i2osp(255, 1).is_ok()); - assert!(i2osp(256, 1).is_err()); - assert!(i2osp(257, 1).is_err()); + assert!(i2osp::(255).is_ok()); + assert!(i2osp::(256).is_err()); + assert!(i2osp::(257).is_err()); - assert!(i2osp(256 * 256 - 1, 2).is_ok()); - assert!(i2osp(256 * 256, 2).is_err()); - assert!(i2osp(256 * 256 + 1, 2).is_err()); + assert!(i2osp::(256 * 256 - 1).is_ok()); + assert!(i2osp::(256 * 256).is_err()); + assert!(i2osp::(256 * 256 + 1).is_err()); } } diff --git a/src/serialization/tests.rs b/src/serialization/tests.rs old mode 100644 new mode 100755 index cce4808..5507703 --- a/src/serialization/tests.rs +++ b/src/serialization/tests.rs @@ -7,441 +7,650 @@ use crate::{ ciphersuite::CipherSuite, - envelope::{Envelope, InnerEnvelopeMode}, + envelope::{Envelope, EnvelopeLen, InnerEnvelopeMode}, errors::*, + key_exchange::{ + group::KeGroup, + traits::{Ke1MessageLen, Ke2MessageLen}, + }, key_exchange::{ traits::{FromBytes, KeyExchange, ToBytes}, tripledh::{NonceLen, TripleDH}, }, keypair::KeyPair, - serialization::{i2osp, os2ip, serialize}, + messages::CredentialResponseWithoutKeLen, + opaque::MaskedResponseLen, + serialization::{i2osp, os2ip, Serialize}, *, }; #[cfg(test)] use alloc::vec; #[cfg(test)] use alloc::vec::Vec; +use core::ops::Add; -use curve25519_dalek::ristretto::RistrettoPoint; -use generic_array::typenum::Unsigned; +use digest::FixedOutput; +use generic_array::{ + typenum::{Sum, Unsigned, U2}, + ArrayLength, GenericArray, +}; use proptest::{collection::vec, prelude::*}; use rand::{rngs::OsRng, RngCore}; use voprf::group::Group; use sha2::Digest; -struct Default; -impl CipherSuite for Default { - type OprfGroup = RistrettoPoint; - type KeGroup = RistrettoPoint; +#[cfg(feature = "ristretto255")] +struct Ristretto255; +#[cfg(feature = "ristretto255")] +impl CipherSuite for Ristretto255 { + type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; + type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; type KeyExchange = TripleDH; type Hash = sha2::Sha512; type SlowHash = crate::slow_hash::NoOpHash; } -const HASH_SIZE: usize = 64; // Because of SHA512 -const MAC_SIZE: usize = 64; // Because of SHA512 +#[cfg(feature = "p256")] +struct P256; +#[cfg(feature = "p256")] +impl CipherSuite for P256 { + type OprfGroup = p256_::ProjectivePoint; + type KeGroup = p256_::PublicKey; + type KeyExchange = TripleDH; + type Hash = sha2::Sha256; + type SlowHash = crate::slow_hash::NoOpHash; +} -fn random_ristretto_point() -> RistrettoPoint { +fn random_point() -> CS::KeGroup { let mut rng = OsRng; - let mut random_bits = [0u8; 64]; - rng.fill_bytes(&mut random_bits); - - // This is because RistrettoPoint is on an obsolete sha2 version - let mut bits = [0u8; 64]; - let mut hasher = sha2::Sha512::new(); - hasher.update(&random_bits[..]); - bits.copy_from_slice(&hasher.finalize()); - - RistrettoPoint::from_uniform_bytes(&bits) + let sk = CS::KeGroup::random_sk(&mut rng); + CS::KeGroup::public_key(&sk) } #[test] fn client_registration_roundtrip() -> Result<(), ProtocolError> { - let pw = b"hunter2"; - let mut rng = OsRng; + fn inner() -> Result<(), ProtocolError> { + let pw = b"hunter2"; + let mut rng = OsRng; - let blind_result = - &voprf::NonVerifiableClient::::blind(pw.to_vec(), &mut rng)?; + 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 bytes: Vec = chain!( + Serialize::::from(&blind_result.state.serialize())?.iter(), + Serialize::::from(&blind_result.message.serialize())?.iter(), + ) + .flatten() + .cloned() + .collect(); + + let reg = ClientRegistration::::deserialize(&bytes)?; + let reg_bytes = reg.serialize()?; + assert_eq!(reg_bytes, bytes); + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; - let reg = ClientRegistration::::deserialize(&bytes[..])?; - let reg_bytes = reg.serialize()?; - assert_eq!(reg_bytes, bytes); Ok(()) } #[test] 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; - let mut masking_key = [0u8; HASH_SIZE]; - rng.fill_bytes(&mut masking_key); + fn inner() -> Result<(), ProtocolError> + where + // Envelope: Nonce + Hash + NonceLen: Add<::OutputSize>, + EnvelopeLen: ArrayLength, + // RegistrationUpload: (KePk + Hash) + Envelope + ::PkLen: Add<::OutputSize>, + Sum<::PkLen, ::OutputSize>: + ArrayLength + Add>, + RegistrationUploadLen: ArrayLength, + // ServerRegistration = RegistrationUpload + { + // If we don't have envelope and client_pk, the server registration just + // contains the prf key + let mut rng = OsRng; + let mut masking_key = GenericArray::<_, ::OutputSize>::default(); + rng.fill_bytes(&mut masking_key); - // Construct a mock envelope - let mut mock_envelope_bytes = Vec::new(); - mock_envelope_bytes.extend_from_slice(&vec![0; NonceLen::USIZE]); // empty nonce + // Construct a mock envelope + let mut mock_envelope_bytes = Vec::new(); + mock_envelope_bytes.extend_from_slice(&[0; NonceLen::USIZE]); // empty nonce // mock_envelope_bytes.extend_from_slice(&ciphertext); // ciphertext which is an encrypted private key - mock_envelope_bytes.extend_from_slice(&[0; MAC_SIZE]); // length-MAC_SIZE hmac + mock_envelope_bytes + .extend_from_slice(&GenericArray::<_, ::OutputSize>::default()); // length-MAC_SIZE hmac + + let mock_client_kp = KeyPair::::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)?; + let reg_bytes = reg.serialize(); + assert_eq!(*reg_bytes, bytes); + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; - 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[..])?; - let reg_bytes = reg.serialize()?; - assert_eq!(reg_bytes, bytes); Ok(()) } #[test] fn registration_request_roundtrip() -> Result<(), ProtocolError> { - let pt = random_ristretto_point(); - let pt_bytes = pt.to_arr().to_vec(); + fn inner() -> Result<(), ProtocolError> { + let pt = random_point::(); + let pt_bytes = pt.to_arr().to_vec(); - let mut input = Vec::new(); - input.extend_from_slice(pt_bytes.as_slice()); + let mut input = Vec::new(); + input.extend_from_slice(&pt_bytes); - let r1 = RegistrationRequest::::deserialize(input.as_slice())?; - let r1_bytes = r1.serialize()?; - assert_eq!(input, r1_bytes); + let r1 = RegistrationRequest::::deserialize(&input)?; + let r1_bytes = r1.serialize(); + assert_eq!(input, *r1_bytes); - // Assert that identity group element is rejected - let identity = RistrettoPoint::identity(); - let identity_bytes = identity.to_arr().to_vec(); + // Assert that identity group element is rejected + let identity = CS::OprfGroup::identity(); + let identity_bytes = identity.to_arr().to_vec(); - assert!( - match RegistrationRequest::::deserialize(identity_bytes.as_slice()) { + assert!(matches!( + RegistrationRequest::::deserialize(&identity_bytes), Err(ProtocolError::LibraryError(InternalError::OprfError( voprf::errors::InternalError::PointError, - ))) => true, - _ => false, - } - ); + ))) + )); + + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; Ok(()) } #[test] 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 pubkey_bytes = skp.public().to_arr(); + fn inner() -> Result<(), ProtocolError> + where + // RegistrationResponse: KgPk + KePk + ::ElemLen: Add<::PkLen>, + RegistrationResponseLen: ArrayLength, + { + let pt = random_point::(); + let beta_bytes = pt.to_arr(); + let mut rng = OsRng; + let skp = KeyPair::::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 mut input = Vec::new(); + input.extend_from_slice(&beta_bytes); + input.extend_from_slice(&pubkey_bytes); - let r2 = RegistrationResponse::::deserialize(input.as_slice())?; - let r2_bytes = r2.serialize()?; - assert_eq!(input, r2_bytes); + let r2 = RegistrationResponse::::deserialize(&input)?; + let r2_bytes = r2.serialize(); + assert_eq!(input, *r2_bytes); - // Assert that identity group element is rejected - let identity = RistrettoPoint::identity(); - let identity_bytes = identity.to_arr().to_vec(); + // Assert that identity group element is rejected + let identity = CS::OprfGroup::identity(); + let identity_bytes = identity.to_arr().to_vec(); - assert!(match RegistrationResponse::::deserialize( - &[identity_bytes, pubkey_bytes.to_vec()].concat() - ) { - Err(ProtocolError::LibraryError(InternalError::OprfError( - voprf::errors::InternalError::PointError, - ))) => true, - _ => false, - }); + assert!(matches!( + RegistrationResponse::::deserialize( + &[identity_bytes, pubkey_bytes.to_vec()].concat() + ), + Err(ProtocolError::LibraryError(InternalError::OprfError( + voprf::errors::InternalError::PointError, + ))) + )); + + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; Ok(()) } #[test] fn registration_upload_roundtrip() -> Result<(), ProtocolError> { - let mut rng = OsRng; - let skp = KeyPair::<::OprfGroup>::generate_random(&mut rng)?; - let pubkey_bytes = skp.public().to_arr(); + fn inner() -> Result<(), ProtocolError> + where + // Envelope: Nonce + Hash + NonceLen: Add<::OutputSize>, + EnvelopeLen: ArrayLength, + // RegistrationUpload: (KePk + Hash) + Envelope + ::PkLen: Add<::OutputSize>, + Sum<::PkLen, ::OutputSize>: + ArrayLength + Add>, + RegistrationUploadLen: ArrayLength, + { + let mut rng = OsRng; + let skp = KeyPair::::generate_random(&mut rng); + let pubkey_bytes = skp.public().to_arr(); - let mut key = [0u8; 32]; - rng.fill_bytes(&mut key); - let mut nonce = [0u8; 32]; - rng.fill_bytes(&mut nonce); + let mut key = [0u8; 32]; + rng.fill_bytes(&mut key); + let mut nonce = [0u8; NonceLen::USIZE]; + rng.fill_bytes(&mut nonce); - let mut masking_key = vec![0u8; ::OutputSize::USIZE]; - rng.fill_bytes(&mut masking_key); + let mut masking_key = GenericArray::<_, ::OutputSize>::default(); + rng.fill_bytes(&mut masking_key); - let randomized_pwd_hasher = hkdf::Hkdf::new(None, &key); + 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 (envelope, _, _) = Envelope::::seal_raw( + randomized_pwd_hasher, + nonce.into(), + Some(pubkey_bytes.as_slice()).into_iter(), + InnerEnvelopeMode::Internal, + ) + .unwrap(); + let envelope_bytes = envelope.serialize(); - let mut input = Vec::new(); - input.extend_from_slice(&pubkey_bytes[..]); - input.extend_from_slice(&masking_key[..]); - input.extend_from_slice(&envelope_bytes); + let mut input = Vec::new(); + input.extend_from_slice(&pubkey_bytes); + input.extend_from_slice(&masking_key); + input.extend_from_slice(&envelope_bytes); - let r3 = RegistrationUpload::::deserialize(&input[..])?; - let r3_bytes = r3.serialize()?; - assert_eq!(input, r3_bytes); + let r3 = RegistrationUpload::::deserialize(&input)?; + let r3_bytes = r3.serialize(); + assert_eq!(input, *r3_bytes); + + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; Ok(()) } #[test] fn credential_request_roundtrip() -> Result<(), ProtocolError> { - let mut rng = OsRng; - let alpha = random_ristretto_point(); - let alpha_bytes = alpha.to_arr().to_vec(); + fn inner() -> Result<(), ProtocolError> + where + // CredentialRequest: KgPk + Ke1Message + ::ElemLen: Add>, + CredentialRequestLen: ArrayLength, + { + let mut rng = OsRng; + let alpha = random_point::(); + let alpha_bytes = alpha.to_arr(); - 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 client_e_kp = KeyPair::::generate_random(&mut rng); + let mut client_nonce = [0u8; NonceLen::USIZE]; + rng.fill_bytes(&mut client_nonce); - let ke1m: Vec = [&client_nonce[..], &client_e_kp.public()].concat(); + let ke1m: Vec = [client_nonce.as_ref(), client_e_kp.public()].concat(); - let mut input = Vec::new(); - input.extend_from_slice(&alpha_bytes); - input.extend_from_slice(&ke1m[..]); + let mut input = Vec::new(); + input.extend_from_slice(&alpha_bytes); + input.extend_from_slice(&ke1m); - let l1 = CredentialRequest::::deserialize(input.as_slice())?; - let l1_bytes = l1.serialize()?; - assert_eq!(input, l1_bytes); + let l1 = CredentialRequest::::deserialize(&input)?; + let l1_bytes = l1.serialize(); + assert_eq!(input, *l1_bytes); - // Assert that identity group element is rejected - let identity = RistrettoPoint::identity(); - let identity_bytes = identity.to_arr().to_vec(); + // Assert that identity group element is rejected + let identity = CS::OprfGroup::identity(); + let identity_bytes = identity.to_arr().to_vec(); - assert!(match CredentialRequest::::deserialize( - &[identity_bytes, ke1m.to_vec()].concat() - ) { - Err(ProtocolError::LibraryError(InternalError::OprfError( - voprf::errors::InternalError::PointError, - ))) => true, - _ => false, - }); + assert!(matches!( + CredentialRequest::::deserialize(&[identity_bytes, ke1m.to_vec()].concat()), + Err(ProtocolError::LibraryError(InternalError::OprfError( + voprf::errors::InternalError::PointError, + ))) + )); + + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; Ok(()) } #[test] fn credential_response_roundtrip() -> Result<(), ProtocolError> { - let pt = random_ristretto_point(); - let pt_bytes = pt.to_arr().to_vec(); + fn inner() -> Result<(), ProtocolError> + where + // CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse + ::ElemLen: Add, + Sum<::ElemLen, NonceLen>: + ArrayLength + Add>, + CredentialResponseWithoutKeLen: ArrayLength, + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, + // CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message + CredentialResponseWithoutKeLen: Add>, + CredentialResponseLen: ArrayLength, + { + let pt = random_point::(); + let pt_bytes = pt.to_arr(); - let mut rng = OsRng; + let mut rng = OsRng; - let mut masking_nonce = vec![0u8; 32]; - rng.fill_bytes(&mut masking_nonce); + let mut masking_nonce = [0u8; 32]; + rng.fill_bytes(&mut masking_nonce); - let mut masked_response = - vec![0u8; ::ElemLen::USIZE + Envelope::::len()]; - rng.fill_bytes(&mut masked_response); + let mut masked_response = + vec![0u8; ::ElemLen::USIZE + Envelope::::len()]; + rng.fill_bytes(&mut masked_response); - 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]; - rng.fill_bytes(&mut server_nonce); + let server_e_kp = KeyPair::::generate_random(&mut rng); + let mut mac = GenericArray::<_, ::OutputSize>::default(); + rng.fill_bytes(&mut mac); + let mut server_nonce = [0u8; NonceLen::USIZE]; + rng.fill_bytes(&mut server_nonce); - let ke2m: Vec = [&server_nonce[..], &server_e_kp.public(), &mac[..]].concat(); + let ke2m: Vec = [server_nonce.as_ref(), server_e_kp.public(), &mac].concat(); - let mut input = Vec::new(); - input.extend_from_slice(pt_bytes.as_slice()); - input.extend_from_slice(&masking_nonce); - input.extend_from_slice(&masked_response); - input.extend_from_slice(&ke2m[..]); + let mut input = Vec::new(); + input.extend_from_slice(&pt_bytes); + input.extend_from_slice(&masking_nonce); + input.extend_from_slice(&masked_response); + input.extend_from_slice(&ke2m); - let l2 = CredentialResponse::::deserialize(&input)?; - let l2_bytes = l2.serialize()?; - assert_eq!(input, l2_bytes); + let l2 = CredentialResponse::::deserialize(&input)?; + let l2_bytes = l2.serialize(); + assert_eq!(input, *l2_bytes); - // Assert that identity group element is rejected - let identity = RistrettoPoint::identity(); - let identity_bytes = identity.to_arr().to_vec(); + // Assert that identity group element is rejected + let identity = CS::OprfGroup::identity(); + let identity_bytes = identity.to_arr().to_vec(); - assert!(match CredentialResponse::::deserialize( - &[ - identity_bytes, - masking_nonce.to_vec(), - masked_response, - ke2m.to_vec() - ] - .concat() - ) { - Err(ProtocolError::LibraryError(InternalError::OprfError( - voprf::errors::InternalError::PointError, - ))) => true, - _ => false, - }); + assert!(matches!( + CredentialResponse::::deserialize( + &[ + identity_bytes, + masking_nonce.to_vec(), + masked_response, + ke2m.to_vec() + ] + .concat() + ), + Err(ProtocolError::LibraryError(InternalError::OprfError( + voprf::errors::InternalError::PointError, + ))) + )); + + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; Ok(()) } #[test] fn credential_finalization_roundtrip() -> Result<(), ProtocolError> { - let mut rng = OsRng; - let mut mac = [0u8; MAC_SIZE]; - rng.fill_bytes(&mut mac); + fn inner() -> Result<(), ProtocolError> { + let mut rng = OsRng; + let mut mac = GenericArray::<_, ::OutputSize>::default(); + rng.fill_bytes(&mut mac); - let input: Vec = [&mac[..]].concat(); + let input = mac; - let l3 = CredentialFinalization::::deserialize(&input)?; - let l3_bytes = l3.serialize()?; - assert_eq!(input, l3_bytes); + let l3 = CredentialFinalization::::deserialize(&input)?; + let l3_bytes = l3.serialize(); + assert_eq!(input.as_slice(), l3_bytes.as_slice()); + + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; Ok(()) } #[test] fn client_login_roundtrip() -> Result<(), ProtocolError> { - let pw = b"hunter2"; - let mut rng = OsRng; + fn inner() -> Result<(), ProtocolError> + where + // CredentialRequest: KgPk + Ke1Message + ::ElemLen: Add>, + CredentialRequestLen: ArrayLength, + { + let pw = b"hunter2"; + let mut rng = OsRng; - 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 client_e_kp = KeyPair::::generate_random(&mut rng); + let mut client_nonce = [0; 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(); + let l1_data = [ + client_e_kp.private().to_arr().to_vec(), + client_nonce.to_vec(), + ] + .concat(); - let blind_result = - &voprf::NonVerifiableClient::::blind(pw.to_vec(), &mut rng)?; + let blind_result = + voprf::NonVerifiableClient::::blind(pw.to_vec(), &mut rng)?; + + let credential_request = CredentialRequest:: { + blinded_element: blind_result.message, + ke1_message: + >::KE1Message::from_bytes( + &[client_nonce.as_ref(), client_e_kp.public()].concat(), + )?, + }; + + let bytes: Vec = chain!( + Serialize::::from(&blind_result.state.serialize())?.iter(), + Serialize::::from(&credential_request.serialize())?.iter(), + Serialize::::from(&l1_data)?.iter(), + ) + .flatten() + .cloned() + .collect(); + let reg = ClientLogin::::deserialize(&bytes)?; + let reg_bytes = reg.serialize()?; + assert_eq!(reg_bytes, bytes); + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; - let bytes: Vec = [ - serialize(&blind_result.state.serialize(), 2)?, - serialize(&serialized_credential_request, 2)?, - serialize(&l1_data, 2)?, - ] - .concat(); - let reg = ClientLogin::::deserialize(&bytes[..])?; - let reg_bytes = reg.serialize()?; - assert_eq!(reg_bytes, bytes); Ok(()) } #[test] fn ke1_message_roundtrip() -> Result<(), ProtocolError> { - let mut rng = OsRng; + fn inner() -> Result<(), ProtocolError> { + let mut rng = OsRng; - 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 client_e_kp = KeyPair::::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[..])?; - let reg_bytes = reg.to_bytes(); - assert_eq!(reg_bytes, ke1m); + let ke1m = [client_nonce.as_slice(), client_e_kp.public()].concat(); + let reg = + >::KE1Message::from_bytes(&ke1m)?; + let reg_bytes = reg.to_bytes(); + assert_eq!(*reg_bytes, ke1m); + + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; Ok(()) } #[test] fn ke2_message_roundtrip() -> Result<(), ProtocolError> { - let mut rng = OsRng; + fn inner() -> Result<(), ProtocolError> { + let mut rng = OsRng; - 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]; - rng.fill_bytes(&mut server_nonce); + let server_e_kp = KeyPair::::generate_random(&mut rng); + let mut mac = GenericArray::<_, ::OutputSize>::default(); + rng.fill_bytes(&mut mac); + let mut server_nonce = vec![0u8; NonceLen::USIZE]; + rng.fill_bytes(&mut server_nonce); - let ke2m: Vec = [&server_nonce[..], &server_e_kp.public(), &mac[..]].concat(); + let ke2m: Vec = [server_nonce.as_slice(), server_e_kp.public(), &mac].concat(); - let reg = >::KE2Message::from_bytes::< - Default, - >(&ke2m[..])?; - let reg_bytes = reg.to_bytes(); - assert_eq!(reg_bytes, ke2m); + let reg = + >::KE2Message::from_bytes(&ke2m)?; + let reg_bytes = reg.to_bytes(); + assert_eq!(*reg_bytes, ke2m); + + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; Ok(()) } #[test] fn ke3_message_roundtrip() -> Result<(), ProtocolError> { - let mut rng = OsRng; - let mut mac = [0u8; MAC_SIZE]; - rng.fill_bytes(&mut mac); + fn inner() -> Result<(), ProtocolError> { + let mut rng = OsRng; + let mut mac = GenericArray::<_, ::OutputSize>::default(); + rng.fill_bytes(&mut mac); - let ke3m: Vec = [&mac[..]].concat(); + let ke3m: Vec = [mac].concat(); - let reg = >::KE3Message::from_bytes::< - Default, - >(&ke3m[..])?; - let reg_bytes = reg.to_bytes(); - assert_eq!(reg_bytes, ke3m); + let reg = + >::KE3Message::from_bytes(&ke3m)?; + let reg_bytes = reg.to_bytes(); + assert_eq!(*reg_bytes, ke3m); + + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; Ok(()) } proptest! { + #[test] + fn test_i2osp_os2ip(bytes in vec(any::(), 0..core::mem::size_of::())) { + use generic_array::typenum::{U0, U1, U2, U3, U4, U5, U6, U7}; -#[test] -fn test_i2osp_os2ip(bytes in vec(any::(), 0..core::mem::size_of::())) { - assert_eq!(i2osp(os2ip(&bytes).unwrap(), bytes.len()).unwrap(), bytes); + let input = os2ip(&bytes).unwrap(); + + let output = match bytes.len() { + 0 => i2osp::(input).unwrap().to_vec(), + 1 => i2osp::(input).unwrap().to_vec(), + 2 => i2osp::(input).unwrap().to_vec(), + 3 => i2osp::(input).unwrap().to_vec(), + 4 => i2osp::(input).unwrap().to_vec(), + 5 => i2osp::(input).unwrap().to_vec(), + 6 => i2osp::(input).unwrap().to_vec(), + 7 => i2osp::(input).unwrap().to_vec(), + _ => unreachable!("unexpected size") + }; + + assert_eq!(output, bytes); + } } -#[test] -fn test_nocrash_registration_request(bytes in vec(any::(), 0..200)) { - RegistrationRequest::::deserialize(&bytes[..]).map_or(true, |_| true); +macro_rules! test { + ($mod:ident, $CS:ty) => { + mod $mod { + use super::*; + + proptest! { + #[test] + fn test_nocrash_registration_request(bytes in vec(any::(), 0..200)) { + RegistrationRequest::<$CS>::deserialize(&bytes).map_or(true, |_| true); + } + + #[test] + fn test_nocrash_registration_response(bytes in vec(any::(), 0..200)) { + RegistrationResponse::<$CS>::deserialize(&bytes).map_or(true, |_| true); + } + + #[test] + fn test_nocrash_registration_upload(bytes in vec(any::(), 0..200)) { + RegistrationUpload::<$CS>::deserialize(&bytes).map_or(true, |_| true); + } + + #[test] + fn test_nocrash_credential_request(bytes in vec(any::(), 0..500)) { + CredentialRequest::<$CS>::deserialize(&bytes).map_or(true, |_| true); + } + + #[test] + fn test_nocrash_credential_response(bytes in vec(any::(), 0..500)) { + CredentialResponse::<$CS>::deserialize(&bytes).map_or(true, |_| true); + } + + #[test] + fn test_nocrash_credential_finalization(bytes in vec(any::(), 0..500)) { + CredentialFinalization::<$CS>::deserialize(&bytes).map_or(true, |_| true); + } + + #[test] + fn test_nocrash_client_registration(bytes in vec(any::(), 0..700)) { + ClientRegistration::<$CS>::deserialize(&bytes).map_or(true, |_| true); + } + + #[test] + fn test_nocrash_server_registration(bytes in vec(any::(), 0..700)) { + ServerRegistration::<$CS>::deserialize(&bytes).map_or(true, |_| true); + } + + #[test] + fn test_nocrash_client_login(bytes in vec(any::(), 0..700)) { + ClientLogin::<$CS>::deserialize(&bytes).map_or(true, |_| true); + } + + #[test] + fn test_nocrash_server_login(bytes in vec(any::(), 0..700)) { + ServerLogin::<$CS>::deserialize(&bytes).map_or(true, |_| true); + } + } + } + }; } -#[test] -fn test_nocrash_registration_response(bytes in vec(any::(), 0..200)) { - RegistrationResponse::::deserialize(&bytes[..]).map_or(true, |_| true); -} - -#[test] -fn test_nocrash_registration_upload(bytes in vec(any::(), 0..200)) { - RegistrationUpload::::deserialize(&bytes[..]).map_or(true, |_| true); -} - -#[test] -fn test_nocrash_credential_request(bytes in vec(any::(), 0..500)) { - CredentialRequest::::deserialize(&bytes[..]).map_or(true, |_| true); -} - -#[test] -fn test_nocrash_credential_response(bytes in vec(any::(), 0..500)) { - CredentialResponse::::deserialize(&bytes[..]).map_or(true, |_| true); -} - -#[test] -fn test_nocrash_credential_finalization(bytes in vec(any::(), 0..500)) { - CredentialFinalization::::deserialize(&bytes[..]).map_or(true, |_| true); -} - -#[test] -fn test_nocrash_client_registration(bytes in vec(any::(), 0..700)) { - ClientRegistration::::deserialize(&bytes[..]).map_or(true, |_| true); -} - -#[test] -fn test_nocrash_server_registration(bytes in vec(any::(), 0..700)) { - ServerRegistration::::deserialize(&bytes[..]).map_or(true, |_| true); -} - -#[test] -fn test_nocrash_client_login(bytes in vec(any::(), 0..700)) { - ClientLogin::::deserialize(&bytes[..]).map_or(true, |_| true); -} - -#[test] -fn test_nocrash_server_login(bytes in vec(any::(), 0..700)) { - ServerLogin::::deserialize(&bytes[..]).map_or(true, |_| true); -} - -} +#[cfg(feature = "ristretto255")] +test!(ristretto255, Ristretto255); +#[cfg(feature = "p256")] +test!(p256, P256); diff --git a/src/slow_hash.rs b/src/slow_hash.rs index dccc05c..eb8c071 100644 --- a/src/slow_hash.rs +++ b/src/slow_hash.rs @@ -8,10 +8,7 @@ //! Trait specifying a slow hashing function use crate::{errors::InternalError, hash::Hash}; -use alloc::vec::Vec; use digest::Digest; -#[cfg(feature = "slow-hash")] -use generic_array::typenum::Unsigned; use generic_array::GenericArray; /// Used for the slow hashing function in OPAQUE @@ -20,7 +17,7 @@ pub trait SlowHash: Default { fn hash( &self, input: GenericArray::OutputSize>, - ) -> Result, InternalError>; + ) -> Result::OutputSize>, InternalError>; } /// A no-op hash which simply returns its input @@ -31,8 +28,8 @@ impl SlowHash for NoOpHash { fn hash( &self, input: GenericArray::OutputSize>, - ) -> Result, InternalError> { - Ok(input.to_vec()) + ) -> Result::OutputSize>, InternalError> { + Ok(input) } } @@ -41,8 +38,8 @@ impl SlowHash for argon2::Argon2<'_> { fn hash( &self, input: GenericArray::OutputSize>, - ) -> Result, InternalError> { - let mut output = alloc::vec![0u8; ::OutputSize::USIZE]; + ) -> Result::OutputSize>, InternalError> { + let mut output = GenericArray::default(); self.hash_password_into(&input, &[0; argon2::MIN_SALT_LEN], &mut output) .map_err(|_| InternalError::SlowHashError)?; Ok(output) diff --git a/src/tests/full_test.rs b/src/tests/full_test.rs old mode 100644 new mode 100755 index c418158..fb7ba79 --- a/src/tests/full_test.rs +++ b/src/tests/full_test.rs @@ -8,29 +8,82 @@ #![allow(unsafe_code)] use crate::{ - ciphersuite::CipherSuite, errors::*, key_exchange::tripledh::TripleDH, opaque::*, - slow_hash::NoOpHash, tests::mock_rng::CycleRng, *, + ciphersuite::CipherSuite, + envelope::EnvelopeLen, + errors::*, + key_exchange::{ + group::KeGroup, + traits::{Ke1MessageLen, Ke2MessageLen}, + tripledh::{NonceLen, TripleDH}, + }, + messages::{ + CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen, + RegistrationResponseLen, RegistrationUploadLen, + }, + opaque::*, + slow_hash::NoOpHash, + tests::mock_rng::CycleRng, + *, }; use alloc::string::ToString; -use alloc::vec; use alloc::vec::Vec; -use curve25519_dalek::{ristretto::RistrettoPoint, traits::Identity}; +use core::ops::Add; +use digest::{Digest, FixedOutput}; +use generic_array::typenum::{Sum, Unsigned}; +use generic_array::{ArrayLength, GenericArray}; use rand::rngs::OsRng; use serde_json::Value; +use subtle::ConstantTimeEq; +use voprf::group::Group; use zeroize::Zeroize; // Tests // ===== -struct RistrettoSha5123dhNoSlowHash; -impl CipherSuite for RistrettoSha5123dhNoSlowHash { - type OprfGroup = RistrettoPoint; - type KeGroup = RistrettoPoint; +#[cfg(feature = "ristretto255")] +struct Ristretto255; +#[cfg(feature = "ristretto255")] +impl CipherSuite for Ristretto255 { + type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; + type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; type KeyExchange = TripleDH; type Hash = sha2::Sha512; type SlowHash = NoOpHash; } +#[cfg(feature = "p256")] +struct P256; +#[cfg(feature = "p256")] +impl CipherSuite for P256 { + type OprfGroup = p256_::ProjectivePoint; + type KeGroup = p256_::PublicKey; + type KeyExchange = TripleDH; + type Hash = sha2::Sha256; + type SlowHash = NoOpHash; +} + +#[cfg(all(feature = "x25519", feature = "ristretto255"))] +struct X25519Ristretto255; +#[cfg(all(feature = "x25519", feature = "ristretto255"))] +impl CipherSuite for X25519Ristretto255 { + type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; + type KeGroup = x25519_dalek::PublicKey; + type KeyExchange = TripleDH; + type Hash = sha2::Sha512; + type SlowHash = NoOpHash; +} + +#[cfg(all(feature = "x25519", feature = "p256"))] +struct X25519P256; +#[cfg(all(feature = "x25519", feature = "p256"))] +impl CipherSuite for X25519P256 { + type OprfGroup = p256_::ProjectivePoint; + type KeGroup = x25519_dalek::PublicKey; + type KeyExchange = TripleDH; + type Hash = sha2::Sha256; + type SlowHash = NoOpHash; +} + pub struct TestVectorParameters { pub client_s_pk: Vec, pub client_s_sk: Vec, @@ -70,7 +123,8 @@ static STR_PASSWORD: &str = "password"; static STR_CREDENTIAL_IDENTIFIER: &str = "credential_identifier"; // To regenerate, run: cargo test -- --nocapture generate_test_vectors -static TEST_VECTOR: &str = r#" +#[cfg(feature = "ristretto255")] +static TEST_VECTOR_RISTRETTO255: &str = r#" { "client_s_pk": "181bbea01a5e444390c4b335f8bcb9a846a1c60042669ce6d731af4587960c06", "client_s_sk": "2f21529b9fb27c8c12770b765dc36750c4a51c5ccaf2f83d0182504a85a22c0b", @@ -107,6 +161,120 @@ static TEST_VECTOR: &str = r#" } "#; +#[cfg(feature = "p256")] +static TEST_VECTOR_P256: &str = r#" +{ + "client_s_pk": "022bb70342affe88f4f3c5d5fc4991bfc1f4758651d59d50c25815ffc4d13eeae3", + "client_s_sk": "ee7beaaed8110b155efac3af2bb97a7a45262fa5702de4721c90ebcfb098b596", + "client_e_pk": "02faf2a785a7de0d59c240b235ab7559820f682a7930fb546ecabddee2de091043", + "client_e_sk": "755577ce43627c5201af2bd35bc17bb7a4c9945acbadbb08962a0236a860ce80", + "server_s_pk": "025e6c524abf252eadf812d3ef46dd7afbeb2f65d76269d1a1288fb0be82b378cb", + "server_s_sk": "5f044f77db085dd5ebeb0ffbe69166057c586719f5ff277a4488b3202c720258", + "server_e_pk": "039e7fc9dcf8e9f50405d228a70c1d9bf5eb283b7e156774125b876819d0349630", + "server_e_sk": "5158264e39bd1234ab785701b47d697b07db92eb4f5dc0f206607edf69f66476", + "fake_sk": "402eaf9bf6d4b1501a2eae8ebb76d4970060decd43944c4bd601602c23a23093", + "credential_identifier": "637265644964656e746966696572", + "id_u": "696455", + "id_s": "696453", + "password": "70617373776f7264", + "blinding_factor": "a383673cc3fc95652d0fd6fdfaaff8c2db97c0cb55706499a7e719a28f93ba49", + "oprf_seed": "84618864bc307f9c178cb5c156865094c8f3737e6ea4e46dc965ddbd4b2332f2", + "masking_nonce": "ec5bb47a34e050136fb97a513ddf182ccc498ffb7d70d94954cc013db934c2716f35c5a1adb5c220194bbb1e8159bbfbcabeb7d94215476bdf29e5dad3919b2c", + "envelope_nonce": "a9a9de9d77fae996ffa597928b12c83ff44e56b2e7d4f79dd561132800d63a2c", + "client_nonce": "51710b892007ef555ffd08452d9f9078165c2e7fd3695ad8020d74a8c20bb8b1", + "server_nonce": "68f4bc84db8af9940f41e8e91a5d39800e1eacdecd124918d24dc5eb8d5ed840", + "context": "636f6e74657874", + "registration_request": "0397d002bed42dfd7a104348c29e82c0bab8a5871846d8c6159e511d3c681fc2be", + "registration_response": "0357c5ca3794429f3111026c79925ffa597c7e518ac787ed49fe152d083d07c846025e6c524abf252eadf812d3ef46dd7afbeb2f65d76269d1a1288fb0be82b378cb", + "registration_upload": "03dd5bbddab150cf7cd793d6702741e529ee13ab4ce4cfad731dd77fc13c2310e5d82ca5e29fa03deff3ed1d8eb1353389b02a78bd48fa256915314dac55cf5e74ee7beaaed8110b155efac3af2bb97a7a45262fa5702de4721c90ebcfb098b5964788cdaf2a92a5a161a819c2aa84985f5a8ea6fbedf01c87ddaa8be23fc16721", + "credential_request": "0397d002bed42dfd7a104348c29e82c0bab8a5871846d8c6159e511d3c681fc2be51710b892007ef555ffd08452d9f9078165c2e7fd3695ad8020d74a8c20bb8b102faf2a785a7de0d59c240b235ab7559820f682a7930fb546ecabddee2de091043", + "credential_response": "0357c5ca3794429f3111026c79925ffa597c7e518ac787ed49fe152d083d07c846ec5bb47a34e050136fb97a513ddf182ccc498ffb7d70d94954cc013db934c27117a3467e180aa4144edd4122e53e2f9fe4d1c1363796e5820d92489e94c5fcabda33f5e886cfee0da967b28cc2a06674cf5d050125ac1136a44284520fcde7f7de6af0ab5f26f57d682c5d389298d5ebd38e7ca7f8bf5456ed0ac9f9ac6f4b7cbe5158264e39bd1234ab785701b47d697b07db92eb4f5dc0f206607edf69f66476039cb032462240ab63f406935a398ef593655ed0617147a952a249ca81bfda7d4ad872b9829967987a74514454b1ee3776debcae819c526ac33f838a81edb234b3", + "credential_finalization": "35395199a2e317c5f08f55f67f9f5565d1d0c856572876b1b4e447925a36eb60", + "client_registration_state": "0028a383673cc3fc95652d0fd6fdfaaff8c2db97c0cb55706499a7e719a28f93ba4970617373776f726400210397d002bed42dfd7a104348c29e82c0bab8a5871846d8c6159e511d3c681fc2be", + "client_login_state": "0028a383673cc3fc95652d0fd6fdfaaff8c2db97c0cb55706499a7e719a28f93ba4970617373776f726400620397d002bed42dfd7a104348c29e82c0bab8a5871846d8c6159e511d3c681fc2be51710b892007ef555ffd08452d9f9078165c2e7fd3695ad8020d74a8c20bb8b102faf2a785a7de0d59c240b235ab7559820f682a7930fb546ecabddee2de0910430040755577ce43627c5201af2bd35bc17bb7a4c9945acbadbb08962a0236a860ce8051710b892007ef555ffd08452d9f9078165c2e7fd3695ad8020d74a8c20bb8b1", + "server_login_state": "5c2ec839d40694328ed6133cc12b8da7ec4300589849eb193eb673d35e4645c8bce86cbd880012967dae7c0f1ac2a90a4d6ea9f3ee5c521c77a20600de4528ee89699ffdb9e8ba62442184bd4a696c6c5832801a425446869aefa2544260d618", + "password_file": "03dd5bbddab150cf7cd793d6702741e529ee13ab4ce4cfad731dd77fc13c2310e5d82ca5e29fa03deff3ed1d8eb1353389b02a78bd48fa256915314dac55cf5e74ee7beaaed8110b155efac3af2bb97a7a45262fa5702de4721c90ebcfb098b5964788cdaf2a92a5a161a819c2aa84985f5a8ea6fbedf01c87ddaa8be23fc16721", + "export_key": "35a93c215dc618dc3acbacc08d16e4879bf2054349facf2a33bc061dee57d787", + "session_key": "89699ffdb9e8ba62442184bd4a696c6c5832801a425446869aefa2544260d618" +} +"#; + +#[cfg(all(feature = "x25519", feature = "ristretto255"))] +static TEST_VECTOR_X25519_RISTRETTO255: &str = r#" +{ + "client_s_pk": "9b1f31c4e1a456d140ea5ae0f683c13785b4ecf473019aca643461afa09ccf1b", + "client_s_sk": "88e61aca2e4715cbbfa2bc3058c9ff388fc9c5a89178624d28ff14ce232e495c", + "client_e_pk": "3e532c63ccb8aabb87b1a724eda76b94083d7cb15174acda91635245618cde3d", + "client_e_sk": "18e9a925e2a1dd150f20322783a935bdfccb488478c6befcc31cce1a27643164", + "server_s_pk": "afe934c6742742e4f1389e42722ce080bb4c4963b1eeaeb8829ea22a11162941", + "server_s_sk": "f0f1348721d891985a3a92236fab57593fc3d997a649c6ce8858e8c20d7c1944", + "server_e_pk": "9a579563f40948693645f490ee3fb0b7ec3fd5de1331858fe95927f71a59a971", + "server_e_sk": "b81c82d7219bf318b75160dd010e96e83e0e080709c61edfbce206d6ec101a4a", + "fake_sk": "90f850aae07d8365e8d28d31ff87bbef1d50c9928c49a5b4ec7aabd4d69ddf75", + "credential_identifier": "637265644964656e746966696572", + "id_u": "696455", + "id_s": "696453", + "password": "70617373776f7264", + "blinding_factor": "5aa31e7d500431691fa3eb16a8a2e416b769ec3df66ace2c199e6b1cfb8a7e0e", + "oprf_seed": "ee0813a196ccc90a12de74c2d680eed39d6f6f16e55012881b32b4c02367f205fa5d7374a6c7119b28a586d59e9ea45760c011a3a81f064f07f80ffa23155e77", + "masking_nonce": "e3a3aecff193e9fbdd6677aeb1078bbf6d78f1893fd6f7acd77e9e05c4d6b35f9b267571d52e74a5b159e5ff55f93f31fa278e549802eb36b66f1ec8b77aa3be", + "envelope_nonce": "e2bd93bfcae01cc59e5e0d928923002682a291577b6e0e214c3a67c1ba94fd15", + "client_nonce": "e130bdb7b59020cd43a39fc588d5f05d33967c48b3e2a87488788897470797d5", + "server_nonce": "f1238020af1207007652c734b023758168c2156cc81b76a4f628f30a042e248f", + "context": "636f6e74657874", + "registration_request": "9ef8b4a7817e4932f4e9837dd54b31ce9209cad61d7ea4003283158e5566620d", + "registration_response": "823eb375fcea47b3b1023848dc7b159ea4b9925f725a45f9e7da0f28c04f717eafe934c6742742e4f1389e42722ce080bb4c4963b1eeaeb8829ea22a11162941", + "registration_upload": "fe576ba51ba994ef0cac45a5fd55f663b2fcb9377d5ea1141d24f6c1a840b71890bf61e8066f25e3ea4148a685aaa2345cfdf3cd9157765c104659fcf695cb76b43f34a46ca41f5e78ae4ac857d98c6f105902305e695bcdec10dc4eda526fac88e61aca2e4715cbbfa2bc3058c9ff388fc9c5a89178624d28ff14ce232e495c90ea3a5efe3b34d84610f458759a7864eed0773290f7a5e5115eef6e5a81164f6e6fc5d026bcbfe55195dfdaa55b13b3d7f177ab8e5e318ffcd7d2ac5daf42c4", + "credential_request": "9ef8b4a7817e4932f4e9837dd54b31ce9209cad61d7ea4003283158e5566620de130bdb7b59020cd43a39fc588d5f05d33967c48b3e2a87488788897470797d53e532c63ccb8aabb87b1a724eda76b94083d7cb15174acda91635245618cde3d", + "credential_response": "823eb375fcea47b3b1023848dc7b159ea4b9925f725a45f9e7da0f28c04f717ee3a3aecff193e9fbdd6677aeb1078bbf6d78f1893fd6f7acd77e9e05c4d6b35fc05b4023afa48dba2a9c78abfb9c93353be3ce2adabe0d4cc4f69d9c0b635cc2e5b14ccec2a9bbabb4ad3f1591a3ac3c92dd989322ad7753e7f5834186a67c94ab65c6a446dcf06f13cf5ef0502f265c3be9e03770c54fe2cee1486cddbf292fa0f44b6173faefe0c093a7797bee2b04927c009f3653b42c3319bbb817920fbdb81c82d7219bf318b75160dd010e96e83e0e080709c61edfbce206d6ec101a4ae21bc06da818572e3a7e77f0eb74bbf7375379771dd7ada27c0b9bdd584f5f142eb61b7fb2c5f7d164597ec148f74a6a168ec0d93b06fb45d48d0a0c3e92e1da80cfe439fccde489c74b801de4401fadf4dcbd61cfe559ad2cf38e83662e2b06", + "credential_finalization": "fd18bc8aa8d789e6d954f962b52cb700296e88efd3a26f0761ff3e367d12b94ef187b2cc250519a2193fbd3c78247d3e0121aacdcdc5b22dcc818cd964c25754", + "client_registration_state": "00285aa31e7d500431691fa3eb16a8a2e416b769ec3df66ace2c199e6b1cfb8a7e0e70617373776f726400209ef8b4a7817e4932f4e9837dd54b31ce9209cad61d7ea4003283158e5566620d", + "client_login_state": "00285aa31e7d500431691fa3eb16a8a2e416b769ec3df66ace2c199e6b1cfb8a7e0e70617373776f726400609ef8b4a7817e4932f4e9837dd54b31ce9209cad61d7ea4003283158e5566620de130bdb7b59020cd43a39fc588d5f05d33967c48b3e2a87488788897470797d53e532c63ccb8aabb87b1a724eda76b94083d7cb15174acda91635245618cde3d004018e9a925e2a1dd150f20322783a935bdfccb488478c6befcc31cce1a27643164e130bdb7b59020cd43a39fc588d5f05d33967c48b3e2a87488788897470797d5", + "server_login_state": "b8bb1a1ff45040bf016aeab52aceec195109233f4c0e2589d4370658bb07f1d57f65d5e3007d94ed36d974c298f21184041c08c3298c9d16fa33591c19b07b45bdaaaee9c95f286dc4ce250b684bf3c5248ca0382f682d9eddeb5bf8fa16488696f7df7dec0d5090c57153aa1b3da588469ca6be7dc25954147ad3c08367f1bb2d2e6bd1a311eb2f3960b80a72e77158fc7b072c85f134695735ffed8206d465029c3ce886fee4665e05dfca5ef778dfe851bc31a8980dae67f15672d8e3f1dd", + "password_file": "fe576ba51ba994ef0cac45a5fd55f663b2fcb9377d5ea1141d24f6c1a840b71890bf61e8066f25e3ea4148a685aaa2345cfdf3cd9157765c104659fcf695cb76b43f34a46ca41f5e78ae4ac857d98c6f105902305e695bcdec10dc4eda526fac88e61aca2e4715cbbfa2bc3058c9ff388fc9c5a89178624d28ff14ce232e495c90ea3a5efe3b34d84610f458759a7864eed0773290f7a5e5115eef6e5a81164f6e6fc5d026bcbfe55195dfdaa55b13b3d7f177ab8e5e318ffcd7d2ac5daf42c4", + "export_key": "aafb0c3bc3694314180212233e811fa44cd35896420d3f65c3696e305c177fca6850bb1b36ed5b6fa3fdca9483dd2013ad30bb84f2a94979fc1fec2e461c1515", + "session_key": "2d2e6bd1a311eb2f3960b80a72e77158fc7b072c85f134695735ffed8206d465029c3ce886fee4665e05dfca5ef778dfe851bc31a8980dae67f15672d8e3f1dd" +} +"#; + +#[cfg(all(feature = "x25519", feature = "p256"))] +static TEST_VECTOR_X25519_P256: &str = r#" +{ + "client_s_pk": "515850c2fb8fcf90378ba5baa2e5b05fd5244f90f49e4a4e8ded4553a696835a", + "client_s_sk": "2894850bbca99009c3a50e648011a57edda65bf88177197fff52378bde705878", + "client_e_pk": "8570fc35b68cf59e9c2d3d08a2452e9eaa9089b6d4cbee4053aedcd8eb4d3555", + "client_e_sk": "9001cb4337b57ca2a72e1a837ab72c5ee6f41348a4c77b5720a3fc6cd6f75561", + "server_s_pk": "583ef921ee685fe1a9d25492ed7221bf429dd8f3093cd78bf3de4b4822f11b56", + "server_s_sk": "f8c0872b11bebf21c83b300dedf222340c034a9831a3d21feaf7cb51c6f7805a", + "server_e_pk": "303d1d7ad0d6d466ee5a98a1407b1a05a891511cbdaa38695c63f9fb47a61c6a", + "server_e_sk": "c8ffba68071b11cabaae1f28bc5c816132dd2e1fc2d11ecf6286855f76e4b37f", + "fake_sk": "48aecca7847d09a5ba8ea1243d9a3527c16dc79852fd04eeb93083ef52e8f075", + "credential_identifier": "637265644964656e746966696572", + "id_u": "696455", + "id_s": "696453", + "password": "70617373776f7264", + "blinding_factor": "0e2fe2a1a193da4c6739a1265cd9a2df297ac7312f2770afa9c8d6de37ead907", + "oprf_seed": "048e281519d6d7548d03dccc8684d91e22025fc573e076c1c5885839cf42b8ad", + "masking_nonce": "59ded518f0215d108d4b0ba8a34911c1d4178318816ab964e67d8315c6803c1fe403684d504bdcbbde77fb90d3824390dd7d3f04b9203636c23399ffdacf9e62", + "envelope_nonce": "119acabcfa0d808d0ce82b7d3de2193deb5b0e71dc111d456c8ad4ee32fa7306", + "client_nonce": "9e34fd4a6900a3dcb0bfcf8b6df799871bb0a11178ee0d7dad6c0fb74921f302", + "server_nonce": "d62fbaec787648da7900d89fd007822e79407016d98a62333239892d49375a7d", + "context": "636f6e74657874", + "registration_request": "03cc7a78723430cbfa6f337c25d3ad586e5d20e2f8e9c2126a28c08f76493088f7", + "registration_response": "0247b0d70311fb623ee21236536cb5df543100b44abaacc3bf2627cca77ee80185583ef921ee685fe1a9d25492ed7221bf429dd8f3093cd78bf3de4b4822f11b56", + "registration_upload": "e3969807b3496f7a07afa9f2288e706f71125bbe1bc659e40f9c83eb3e428e430d9dc301ff73d3b95bf0fceab01ce66dc4c2f84dbea61526e6c1ee7c4adb8c912894850bbca99009c3a50e648011a57edda65bf88177197fff52378bde705878a6ede2210483b94ff0ca04b1eda09b841a735170f79d1674aacfbd3550bba356", + "credential_request": "03cc7a78723430cbfa6f337c25d3ad586e5d20e2f8e9c2126a28c08f76493088f79e34fd4a6900a3dcb0bfcf8b6df799871bb0a11178ee0d7dad6c0fb74921f3028570fc35b68cf59e9c2d3d08a2452e9eaa9089b6d4cbee4053aedcd8eb4d3555", + "credential_response": "0247b0d70311fb623ee21236536cb5df543100b44abaacc3bf2627cca77ee8018559ded518f0215d108d4b0ba8a34911c1d4178318816ab964e67d8315c6803c1fd513bf2f3cea8283a7ba98973d1f160213586e42a5e6ae5d6ac73f5deebc609e7481e84cd4f61b844db7457a5dc18121a0a6d8776d4e29d98cdf4892c18189439a3a7692a2be2910e0ba9fd8f7a28c10733ff781c4e36dabe86a819d35ce4745c8ffba68071b11cabaae1f28bc5c816132dd2e1fc2d11ecf6286855f76e4b37f2c2ffc6b9b2f6342231d5d3a9ea28023f543c7ccf3852413e411ab011b0c06523163f82d3cf9abb5e8517d0d47ef13071ac1001c6a1e39f2cbfe7440257d4db2", + "credential_finalization": "de00d81621b2394b201a0c9d731b6b96e9cdab29fedb14c749c51029446da74a", + "client_registration_state": "00280e2fe2a1a193da4c6739a1265cd9a2df297ac7312f2770afa9c8d6de37ead90770617373776f7264002103cc7a78723430cbfa6f337c25d3ad586e5d20e2f8e9c2126a28c08f76493088f7", + "client_login_state": "00280e2fe2a1a193da4c6739a1265cd9a2df297ac7312f2770afa9c8d6de37ead90770617373776f7264006103cc7a78723430cbfa6f337c25d3ad586e5d20e2f8e9c2126a28c08f76493088f79e34fd4a6900a3dcb0bfcf8b6df799871bb0a11178ee0d7dad6c0fb74921f3028570fc35b68cf59e9c2d3d08a2452e9eaa9089b6d4cbee4053aedcd8eb4d355500409001cb4337b57ca2a72e1a837ab72c5ee6f41348a4c77b5720a3fc6cd6f755619e34fd4a6900a3dcb0bfcf8b6df799871bb0a11178ee0d7dad6c0fb74921f302", + "server_login_state": "df965dfa291f57cfead138a43802798c270c481a35b2fbf3d6d0c1860ab73a8e5f9874bbe6a329b51e9015a551a7b49809de26f74ba2ff496782d94ae01468fc63b02cf977a143ebe1a1231a28367d1be1065d5c0273f959e1e97a08a74bf6f1", + "password_file": "e3969807b3496f7a07afa9f2288e706f71125bbe1bc659e40f9c83eb3e428e430d9dc301ff73d3b95bf0fceab01ce66dc4c2f84dbea61526e6c1ee7c4adb8c912894850bbca99009c3a50e648011a57edda65bf88177197fff52378bde705878a6ede2210483b94ff0ca04b1eda09b841a735170f79d1674aacfbd3550bba356", + "export_key": "6168b6786fbded7a888067b58e62035f0f1940c0fb6448fc69093d62597f365a", + "session_key": "63b02cf977a143ebe1a1231a28367d1be1065d5c0273f959e1e97a08a74bf6f1" +} +"#; + fn decode(values: &Value, key: &str) -> Option> { values[key] .as_str() @@ -115,38 +283,38 @@ fn decode(values: &Value, key: &str) -> Option> { fn populate_test_vectors(values: &Value) -> TestVectorParameters { TestVectorParameters { - client_s_pk: decode(&values, "client_s_pk").unwrap(), - client_s_sk: decode(&values, "client_s_sk").unwrap(), - client_e_pk: decode(&values, "client_e_pk").unwrap(), - client_e_sk: decode(&values, "client_e_sk").unwrap(), - server_s_pk: decode(&values, "server_s_pk").unwrap(), - server_s_sk: decode(&values, "server_s_sk").unwrap(), - server_e_pk: decode(&values, "server_e_pk").unwrap(), - server_e_sk: decode(&values, "server_e_sk").unwrap(), - fake_sk: decode(&values, "fake_sk").unwrap(), - credential_identifier: decode(&values, "credential_identifier").unwrap(), - id_u: decode(&values, "id_u").unwrap(), - id_s: decode(&values, "id_s").unwrap(), - password: decode(&values, "password").unwrap(), - blinding_factor: decode(&values, "blinding_factor").unwrap(), - oprf_seed: decode(&values, "oprf_seed").unwrap(), - masking_nonce: decode(&values, "masking_nonce").unwrap(), - envelope_nonce: decode(&values, "envelope_nonce").unwrap(), - client_nonce: decode(&values, "client_nonce").unwrap(), - server_nonce: decode(&values, "server_nonce").unwrap(), - context: decode(&values, "context").unwrap(), - registration_request: decode(&values, "registration_request").unwrap(), - registration_response: decode(&values, "registration_response").unwrap(), - registration_upload: decode(&values, "registration_upload").unwrap(), - credential_request: decode(&values, "credential_request").unwrap(), - credential_response: decode(&values, "credential_response").unwrap(), - credential_finalization: decode(&values, "credential_finalization").unwrap(), - client_registration_state: decode(&values, "client_registration_state").unwrap(), - client_login_state: decode(&values, "client_login_state").unwrap(), - server_login_state: decode(&values, "server_login_state").unwrap(), - password_file: decode(&values, "password_file").unwrap(), - export_key: decode(&values, "export_key").unwrap(), - session_key: decode(&values, "session_key").unwrap(), + client_s_pk: decode(values, "client_s_pk").unwrap(), + client_s_sk: decode(values, "client_s_sk").unwrap(), + client_e_pk: decode(values, "client_e_pk").unwrap(), + client_e_sk: decode(values, "client_e_sk").unwrap(), + server_s_pk: decode(values, "server_s_pk").unwrap(), + server_s_sk: decode(values, "server_s_sk").unwrap(), + server_e_pk: decode(values, "server_e_pk").unwrap(), + server_e_sk: decode(values, "server_e_sk").unwrap(), + fake_sk: decode(values, "fake_sk").unwrap(), + credential_identifier: decode(values, "credential_identifier").unwrap(), + id_u: decode(values, "id_u").unwrap(), + id_s: decode(values, "id_s").unwrap(), + password: decode(values, "password").unwrap(), + blinding_factor: decode(values, "blinding_factor").unwrap(), + oprf_seed: decode(values, "oprf_seed").unwrap(), + masking_nonce: decode(values, "masking_nonce").unwrap(), + envelope_nonce: decode(values, "envelope_nonce").unwrap(), + client_nonce: decode(values, "client_nonce").unwrap(), + server_nonce: decode(values, "server_nonce").unwrap(), + context: decode(values, "context").unwrap(), + registration_request: decode(values, "registration_request").unwrap(), + registration_response: decode(values, "registration_response").unwrap(), + registration_upload: decode(values, "registration_upload").unwrap(), + credential_request: decode(values, "credential_request").unwrap(), + credential_response: decode(values, "credential_response").unwrap(), + credential_finalization: decode(values, "credential_finalization").unwrap(), + client_registration_state: decode(values, "client_registration_state").unwrap(), + client_login_state: decode(values, "client_login_state").unwrap(), + server_login_state: decode(values, "server_login_state").unwrap(), + password_file: decode(values, "password_file").unwrap(), + export_key: decode(values, "export_key").unwrap(), + session_key: decode(values, "session_key").unwrap(), } } @@ -273,39 +441,79 @@ fn stringify_test_vectors(p: &TestVectorParameters) -> alloc::string::String { s } -fn generate_parameters() -> Result { - use crate::{key_exchange::tripledh::NonceLen, keypair::KeyPair}; - use generic_array::typenum::Unsigned; +fn generate_parameters() -> Result +where + // RegistrationResponse: KgPk + KePk + ::ElemLen: Add<::PkLen>, + RegistrationResponseLen: ArrayLength, + // Envelope: Nonce + Hash + NonceLen: Add<::OutputSize>, + EnvelopeLen: ArrayLength, + // RegistrationUpload: (KePk + Hash) + Envelope + ::PkLen: Add<::OutputSize>, + Sum<::PkLen, ::OutputSize>: + ArrayLength + Add>, + RegistrationUploadLen: ArrayLength, + // ServerRegistration = RegistrationUpload + // Ke1Message: Nonce + KePk + NonceLen: Add<::PkLen>, + Ke1MessageLen: ArrayLength, + // CredentialRequest: KgPk + Ke1Message + ::ElemLen: Add>, + CredentialRequestLen: ArrayLength, + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, + // CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse + ::ElemLen: Add, + Sum<::ElemLen, NonceLen>: ArrayLength + Add>, + CredentialResponseWithoutKeLen: ArrayLength, + // Ke2Message: (Nonce + KePk) + Hash + NonceLen: Add<::PkLen>, + Sum::PkLen>: + ArrayLength + Add<::OutputSize>, + Ke2MessageLen: ArrayLength, + // CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message + CredentialResponseWithoutKeLen: Add>, + CredentialResponseLen: ArrayLength, +{ + use crate::keypair::KeyPair; 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"; let password = b"password"; let context = b"context"; - let mut oprf_seed = [0u8; 64]; + let mut oprf_seed = GenericArray::<_, ::OutputSize>::default(); rng.fill_bytes(&mut oprf_seed); let mut masking_nonce = [0u8; 64]; rng.fill_bytes(&mut masking_nonce); let mut envelope_nonce = [0u8; 32]; rng.fill_bytes(&mut envelope_nonce); - let mut client_nonce = vec![0u8; NonceLen::USIZE]; + let mut client_nonce = [0u8; NonceLen::USIZE]; rng.fill_bytes(&mut client_nonce); - let mut server_nonce = vec![0u8; NonceLen::USIZE]; + let mut server_nonce = [0u8; NonceLen::USIZE]; rng.fill_bytes(&mut server_nonce); let fake_sk: Vec = fake_kp.private().to_vec(); let server_setup = ServerSetup::::deserialize( - &[&oprf_seed, &server_s_kp.private().to_arr()[..], &fake_sk].concat(), + &[ + oprf_seed.as_ref(), + &server_s_kp.private().to_arr(), + &fake_sk, + ] + .concat(), ) .unwrap(); @@ -326,16 +534,16 @@ fn generate_parameters() -> Result::start( &server_setup, client_registration_start_result.message, - &credential_identifier[..], + credential_identifier, ) .unwrap(); - let registration_response_bytes = server_registration_start_result.message.serialize()?; + 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()); @@ -348,18 +556,18 @@ fn generate_parameters() -> Result = Vec::new(); client_login_start.extend_from_slice(&blinding_factor_bytes); @@ -369,7 +577,7 @@ fn generate_parameters() -> Result::start(&mut client_login_start_rng, password).unwrap(); - let credential_request_bytes = client_login_start_result.message.serialize()?; + let credential_request_bytes = client_login_start_result.message.serialize(); let client_login_state = client_login_start_result .state .serialize() @@ -390,30 +598,33 @@ fn generate_parameters() -> Result() -> Result Result<(), ProtocolError> { - let parameters = generate_parameters::()?; - println!("{}", stringify_test_vectors(¶meters)); + #[cfg(feature = "ristretto255")] + { + let parameters = generate_parameters::()?; + println!("Ristretto255: {}", stringify_test_vectors(¶meters)); + } + #[cfg(feature = "p256")] + { + let parameters = generate_parameters::()?; + println!("P-256: {}", stringify_test_vectors(¶meters)); + } + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + { + let parameters = generate_parameters::()?; + println!( + "X25519 Ristretto255: {}", + stringify_test_vectors(¶meters) + ); + } + #[cfg(all(feature = "x25519", feature = "p256"))] + { + let parameters = generate_parameters::()?; + println!("X25519 P-256: {}", stringify_test_vectors(¶meters)); + } + Ok(()) } #[test] fn test_registration_request() -> Result<(), ProtocolError> { - let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); - let mut rng = CycleRng::new(parameters.blinding_factor.to_vec()); - let client_registration_start_result = - ClientRegistration::::start(&mut rng, ¶meters.password)?; - assert_eq!( - hex::encode(¶meters.registration_request), - hex::encode(client_registration_start_result.message.serialize()?) - ); - assert_eq!( - hex::encode(¶meters.client_registration_state), - hex::encode(client_registration_start_result.state.serialize()?) - ); + fn inner(test_vector: &str) -> Result<(), ProtocolError> { + let parameters = populate_test_vectors(&serde_json::from_str(test_vector).unwrap()); + let mut rng = CycleRng::new(parameters.blinding_factor.to_vec()); + let client_registration_start_result = + ClientRegistration::::start(&mut rng, ¶meters.password)?; + assert_eq!( + hex::encode(¶meters.registration_request), + hex::encode(client_registration_start_result.message.serialize()) + ); + assert_eq!( + hex::encode(¶meters.client_registration_state), + hex::encode(client_registration_start_result.state.serialize()?) + ); + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::(TEST_VECTOR_RISTRETTO255)?; + #[cfg(feature = "p256")] + inner::(TEST_VECTOR_P256)?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::(TEST_VECTOR_X25519_RISTRETTO255)?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::(TEST_VECTOR_X25519_P256)?; + Ok(()) } -#[cfg(feature = "serialize")] +#[cfg(feature = "serde")] #[test] fn test_serialization() -> Result<(), ProtocolError> { - let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); - let mut rng = CycleRng::new(parameters.blinding_factor.to_vec()); - let client_registration_start_result = - ClientRegistration::::start(&mut rng, ¶meters.password)?; - { - // Test the json serialization (human-readable, base64). - let registration_request_json = - serde_json::to_string(&client_registration_start_result.message).unwrap(); - assert_eq!( - registration_request_json, - 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()?), - ); - } - { + use core::mem; + + fn inner(test_vector: &str) -> Result<(), ProtocolError> { + let parameters = populate_test_vectors(&serde_json::from_str(test_vector).unwrap()); + let mut rng = CycleRng::new(parameters.blinding_factor.to_vec()); + let client_registration_start_result = + ClientRegistration::::start(&mut rng, ¶meters.password)?; + // Test the bincode serialization (binary). - let registration_request_bin = + let registration_request = bincode::serialize(&client_registration_start_result.message).unwrap(); - assert_eq!(registration_request_bin.len(), 40); - 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()?), + registration_request.len(), + RegistrationRequestLen::::USIZE + mem::size_of::() ); + let registration_request: RegistrationRequest = + bincode::deserialize(®istration_request).unwrap(); + assert_eq!( + hex::encode(client_registration_start_result.message.serialize()), + hex::encode(registration_request.serialize()), + ); + + Ok(()) } + + #[cfg(feature = "ristretto255")] + inner::(TEST_VECTOR_RISTRETTO255)?; + #[cfg(feature = "p256")] + inner::(TEST_VECTOR_P256)?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::(TEST_VECTOR_X25519_RISTRETTO255)?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::(TEST_VECTOR_X25519_P256)?; + Ok(()) } + #[test] fn test_registration_response() -> Result<(), ProtocolError> { - let parameters = populate_test_vectors( - &serde_json::from_str(TEST_VECTOR).map_err(|_| ProtocolError::SerializationError)?, - ); + fn inner(test_vector: &str) -> Result<(), ProtocolError> + where + // RegistrationResponse: KgPk + KePk + ::ElemLen: Add<::PkLen>, + RegistrationResponseLen: ArrayLength, + { + let parameters = populate_test_vectors( + &serde_json::from_str(test_vector).map_err(|_| ProtocolError::SerializationError)?, + ); - let server_setup = ServerSetup::::deserialize( - &[ - ¶meters.oprf_seed[..], - ¶meters.server_s_sk[..], - ¶meters.fake_sk[..], - ] - .concat(), - )?; + let server_setup = ServerSetup::::deserialize( + &[ + parameters.oprf_seed, + parameters.server_s_sk, + parameters.fake_sk, + ] + .concat(), + )?; - let server_registration_start_result = - ServerRegistration::::start( + let server_registration_start_result = ServerRegistration::::start( &server_setup, - RegistrationRequest::deserialize(¶meters.registration_request[..])?, + RegistrationRequest::deserialize(¶meters.registration_request)?, ¶meters.credential_identifier, )?; - assert_eq!( - hex::encode(parameters.registration_response), - hex::encode(server_registration_start_result.message.serialize()?) - ); + assert_eq!( + hex::encode(parameters.registration_response), + hex::encode(server_registration_start_result.message.serialize()) + ); + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::(TEST_VECTOR_RISTRETTO255)?; + #[cfg(feature = "p256")] + inner::(TEST_VECTOR_P256)?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::(TEST_VECTOR_X25519_RISTRETTO255)?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::(TEST_VECTOR_X25519_P256)?; + Ok(()) } #[test] fn test_registration_upload() -> Result<(), ProtocolError> { - let parameters = populate_test_vectors( - &serde_json::from_str(TEST_VECTOR).map_err(|_| ProtocolError::SerializationError)?, - ); + fn inner(test_vector: &str) -> Result<(), ProtocolError> + where + // Envelope: Nonce + Hash + NonceLen: Add<::OutputSize>, + EnvelopeLen: ArrayLength, + // RegistrationUpload: (KePk + Hash) + Envelope + ::PkLen: Add<::OutputSize>, + Sum<::PkLen, ::OutputSize>: + ArrayLength + Add>, + RegistrationUploadLen: ArrayLength, + { + 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(); - let mut finish_registration_rng = CycleRng::new(client_s_sk_and_nonce); - let result = ClientRegistration::::deserialize( - ¶meters.client_registration_state[..], - )? - .finish( - &mut finish_registration_rng, - RegistrationResponse::deserialize(¶meters.registration_response[..])?, - ClientRegistrationFinishParameters::new( - Some(Identifiers::ClientAndServerIdentifiers( - parameters.id_u, - parameters.id_s, - )), - None, - ), - )?; + let client_s_sk_and_nonce: Vec = + [parameters.client_s_sk, parameters.envelope_nonce].concat(); + let mut finish_registration_rng = CycleRng::new(client_s_sk_and_nonce); + let result = ClientRegistration::::deserialize(¶meters.client_registration_state)? + .finish( + &mut finish_registration_rng, + RegistrationResponse::deserialize(¶meters.registration_response)?, + ClientRegistrationFinishParameters::new( + Identifiers { + client: Some(¶meters.id_u), + server: Some(¶meters.id_s), + }, + None, + ), + )?; - assert_eq!( - hex::encode(parameters.registration_upload), - hex::encode(result.message.serialize()?) - ); - assert_eq!( - hex::encode(parameters.export_key), - hex::encode(result.export_key.to_vec()) - ); + assert_eq!( + hex::encode(parameters.registration_upload), + hex::encode(result.message.serialize()) + ); + assert_eq!( + hex::encode(parameters.export_key), + hex::encode(result.export_key.to_vec()) + ); + + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::(TEST_VECTOR_RISTRETTO255)?; + #[cfg(feature = "p256")] + inner::(TEST_VECTOR_P256)?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::(TEST_VECTOR_X25519_RISTRETTO255)?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::(TEST_VECTOR_X25519_P256)?; Ok(()) } #[test] fn test_password_file() -> Result<(), ProtocolError> { - let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); + fn inner(test_vector: &str) -> Result<(), ProtocolError> + where + // Envelope: Nonce + Hash + NonceLen: Add<::OutputSize>, + EnvelopeLen: ArrayLength, + // RegistrationUpload: (KePk + Hash) + Envelope + ::PkLen: Add<::OutputSize>, + Sum<::PkLen, ::OutputSize>: + ArrayLength + Add>, + RegistrationUploadLen: ArrayLength, + // ServerRegistration = RegistrationUpload + { + let parameters = populate_test_vectors(&serde_json::from_str(test_vector).unwrap()); - let password_file = ServerRegistration::finish(RegistrationUpload::< - RistrettoSha5123dhNoSlowHash, - >::deserialize( - ¶meters.registration_upload[..] - )?); + let password_file = ServerRegistration::finish(RegistrationUpload::::deserialize( + ¶meters.registration_upload, + )?); + + assert_eq!( + hex::encode(parameters.password_file), + hex::encode(password_file.serialize()) + ); + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::(TEST_VECTOR_RISTRETTO255)?; + #[cfg(feature = "p256")] + inner::(TEST_VECTOR_P256)?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::(TEST_VECTOR_X25519_RISTRETTO255)?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::(TEST_VECTOR_X25519_P256)?; - assert_eq!( - hex::encode(parameters.password_file), - hex::encode(password_file.serialize()?) - ); Ok(()) } #[test] fn test_credential_request() -> Result<(), ProtocolError> { - let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); + fn inner(test_vector: &str) -> Result<(), ProtocolError> + where + // CredentialRequest: KgPk + Ke1Message + ::ElemLen: Add>, + CredentialRequestLen: ArrayLength, + { + let parameters = populate_test_vectors(&serde_json::from_str(test_vector).unwrap()); + + let client_login_start_rng = [ + parameters.blinding_factor, + parameters.client_e_sk, + parameters.client_nonce, + ] + .concat(); + let mut client_login_start_rng = CycleRng::new(client_login_start_rng); + let client_login_start_result = + ClientLogin::::start(&mut client_login_start_rng, ¶meters.password)?; + assert_eq!( + hex::encode(¶meters.credential_request), + hex::encode(client_login_start_result.message.serialize()) + ); + assert_eq!( + hex::encode(¶meters.client_login_state), + hex::encode(client_login_start_result.state.serialize()?) + ); + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::(TEST_VECTOR_RISTRETTO255)?; + #[cfg(feature = "p256")] + inner::(TEST_VECTOR_P256)?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::(TEST_VECTOR_X25519_RISTRETTO255)?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::(TEST_VECTOR_X25519_P256)?; - let client_login_start_rng = [ - parameters.blinding_factor, - parameters.client_e_sk, - parameters.client_nonce, - ] - .concat(); - let mut client_login_start_rng = CycleRng::new(client_login_start_rng); - let client_login_start_result = ClientLogin::::start( - &mut client_login_start_rng, - ¶meters.password, - )?; - assert_eq!( - hex::encode(¶meters.credential_request), - hex::encode(client_login_start_result.message.serialize()?) - ); - assert_eq!( - hex::encode(¶meters.client_login_state), - hex::encode(client_login_start_result.state.serialize()?) - ); Ok(()) } #[test] fn test_credential_response() -> Result<(), ProtocolError> { - let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); + fn inner(test_vector: &str) -> Result<(), ProtocolError> + where + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, + // CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse + ::ElemLen: Add, + Sum<::ElemLen, NonceLen>: + ArrayLength + Add>, + CredentialResponseWithoutKeLen: ArrayLength, + // CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message + CredentialResponseWithoutKeLen: Add>, + CredentialResponseLen: ArrayLength, + { + let parameters = populate_test_vectors(&serde_json::from_str(test_vector).unwrap()); - let server_setup = ServerSetup::::deserialize( - &[ - ¶meters.oprf_seed[..], - ¶meters.server_s_sk[..], - ¶meters.fake_sk[..], - ] - .concat(), - )?; + let server_setup = ServerSetup::::deserialize( + &[ + parameters.oprf_seed, + parameters.server_s_sk, + parameters.fake_sk, + ] + .concat(), + )?; + + let mut server_e_sk_and_nonce_rng = CycleRng::new( + [ + parameters.masking_nonce, + parameters.server_e_sk, + parameters.server_nonce, + ] + .concat(), + ); + let server_login_start_result = ServerLogin::::start( + &mut server_e_sk_and_nonce_rng, + &server_setup, + Some(ServerRegistration::deserialize(¶meters.password_file)?), + CredentialRequest::::deserialize(¶meters.credential_request)?, + ¶meters.credential_identifier, + ServerLoginStartParameters { + context: Some(¶meters.context), + identifiers: Identifiers { + client: Some(¶meters.id_u), + server: Some(¶meters.id_s), + }, + }, + )?; + assert_eq!( + hex::encode(¶meters.credential_response), + hex::encode(server_login_start_result.message.serialize()) + ); + assert_eq!( + hex::encode(¶meters.server_login_state), + hex::encode(server_login_start_result.state.serialize()) + ); + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::(TEST_VECTOR_RISTRETTO255)?; + #[cfg(feature = "p256")] + inner::(TEST_VECTOR_P256)?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::(TEST_VECTOR_X25519_RISTRETTO255)?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::(TEST_VECTOR_X25519_P256)?; - let mut server_e_sk_and_nonce_rng = CycleRng::new( - [ - parameters.masking_nonce, - parameters.server_e_sk, - parameters.server_nonce, - ] - .concat(), - ); - let server_login_start_result = ServerLogin::::start( - &mut server_e_sk_and_nonce_rng, - &server_setup, - Some(ServerRegistration::deserialize( - ¶meters.password_file[..], - )?), - CredentialRequest::::deserialize( - ¶meters.credential_request[..], - )?, - ¶meters.credential_identifier, - ServerLoginStartParameters::WithContextAndIdentifiers( - parameters.context, - Identifiers::ClientAndServerIdentifiers(parameters.id_u, parameters.id_s), - ), - )?; - assert_eq!( - hex::encode(¶meters.credential_response), - hex::encode(server_login_start_result.message.serialize()?) - ); - assert_eq!( - hex::encode(¶meters.server_login_state), - hex::encode(server_login_start_result.state.serialize()?) - ); Ok(()) } #[test] fn test_credential_finalization() -> Result<(), ProtocolError> { - let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); + fn inner(test_vector: &str) -> Result<(), ProtocolError> + where + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, + { + let parameters = populate_test_vectors(&serde_json::from_str(test_vector).unwrap()); - let client_login_finish_result = ClientLogin::::deserialize( - ¶meters.client_login_state[..], - )? - .finish( - CredentialResponse::::deserialize( - ¶meters.credential_response[..], - )?, - ClientLoginFinishParameters::new( - Some(parameters.context), - Some(Identifiers::ClientAndServerIdentifiers( - parameters.id_u, - parameters.id_s, - )), - None, - ), - )?; + let client_login_finish_result = + ClientLogin::::deserialize(¶meters.client_login_state)?.finish( + CredentialResponse::::deserialize(¶meters.credential_response)?, + ClientLoginFinishParameters::new( + Some(¶meters.context), + Identifiers { + client: Some(¶meters.id_u), + server: Some(¶meters.id_s), + }, + None, + ), + )?; - assert_eq!( - hex::encode(¶meters.server_s_pk), - hex::encode(&client_login_finish_result.server_s_pk.to_arr().to_vec()) - ); - assert_eq!( - hex::encode(¶meters.session_key), - hex::encode(&client_login_finish_result.session_key) - ); - assert_eq!( - hex::encode(¶meters.credential_finalization), - hex::encode(client_login_finish_result.message.serialize()?) - ); - assert_eq!( - hex::encode(¶meters.export_key), - hex::encode(client_login_finish_result.export_key) - ); + assert_eq!( + hex::encode(¶meters.server_s_pk), + hex::encode(&client_login_finish_result.server_s_pk.to_arr().to_vec()) + ); + assert_eq!( + hex::encode(¶meters.session_key), + hex::encode(&client_login_finish_result.session_key) + ); + assert_eq!( + hex::encode(¶meters.credential_finalization), + hex::encode(client_login_finish_result.message.serialize()) + ); + assert_eq!( + hex::encode(¶meters.export_key), + hex::encode(client_login_finish_result.export_key) + ); + + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::(TEST_VECTOR_RISTRETTO255)?; + #[cfg(feature = "p256")] + inner::(TEST_VECTOR_P256)?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::(TEST_VECTOR_X25519_RISTRETTO255)?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::(TEST_VECTOR_X25519_P256)?; Ok(()) } #[test] fn test_server_login_finish() -> Result<(), ProtocolError> { - let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); + fn inner(test_vector: &str) -> Result<(), ProtocolError> { + let parameters = populate_test_vectors(&serde_json::from_str(test_vector).unwrap()); - let server_login_result = ServerLogin::::deserialize( - ¶meters.server_login_state[..], - )? - .finish(CredentialFinalization::deserialize( - ¶meters.credential_finalization[..], - )?)?; + let server_login_result = ServerLogin::::deserialize(¶meters.server_login_state)? + .finish(CredentialFinalization::deserialize( + ¶meters.credential_finalization, + )?)?; - assert_eq!( - hex::encode(parameters.session_key), - hex::encode(&server_login_result.session_key) - ); + assert_eq!( + hex::encode(parameters.session_key), + hex::encode(&server_login_result.session_key) + ); + + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::(TEST_VECTOR_RISTRETTO255)?; + #[cfg(feature = "p256")] + inner::(TEST_VECTOR_P256)?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::(TEST_VECTOR_X25519_RISTRETTO255)?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::(TEST_VECTOR_X25519_P256)?; Ok(()) } -fn test_complete_flow( +fn test_complete_flow( registration_password: &[u8], login_password: &[u8], -) -> Result<(), ProtocolError> { +) -> Result<(), ProtocolError> +where + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, +{ 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, - registration_password, - )?; - let server_registration_start_result = - ServerRegistration::::start( - &server_setup, - client_registration_start_result.message, - credential_identifier, - )?; + ClientRegistration::::start(&mut client_rng, registration_password)?; + let server_registration_start_result = ServerRegistration::::start( + &server_setup, + client_registration_start_result.message, + credential_identifier, + )?; let client_registration_finish_result = client_registration_start_result.state.finish( &mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default(), )?; let p_file = ServerRegistration::finish(client_registration_finish_result.message); - let client_login_start_result = - ClientLogin::::start(&mut client_rng, login_password)?; - let server_login_start_result = ServerLogin::::start( + let client_login_start_result = ClientLogin::::start(&mut client_rng, login_password)?; + let server_login_start_result = ServerLogin::::start( &mut server_rng, &server_setup, Some(p_file), @@ -781,10 +1163,10 @@ fn test_complete_flow( hex::encode(client_login_finish_result.export_key) ); } else { - assert!(match client_login_result { - Err(ProtocolError::InvalidLoginError) => true, - _ => false, - }); + assert!(matches!( + client_login_result, + Err(ProtocolError::InvalidLoginError) + )); } Ok(()) @@ -792,379 +1174,537 @@ fn test_complete_flow( #[test] fn test_complete_flow_success() -> Result<(), ProtocolError> { - test_complete_flow(b"good password", b"good password") + #[cfg(feature = "ristretto255")] + test_complete_flow::(b"good password", b"good password")?; + #[cfg(feature = "p256")] + test_complete_flow::(b"good password", b"good password")?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + test_complete_flow::(b"good password", b"good password")?; + #[cfg(all(feature = "x25519", feature = "p256"))] + test_complete_flow::(b"good password", b"good password")?; + + Ok(()) } #[test] fn test_complete_flow_fail() -> Result<(), ProtocolError> { - test_complete_flow(b"good password", b"bad password") + #[cfg(feature = "ristretto255")] + test_complete_flow::(b"good password", b"bad password")?; + #[cfg(feature = "p256")] + test_complete_flow::(b"good password", b"bad password")?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + test_complete_flow::(b"good password", b"bad password")?; + #[cfg(all(feature = "x25519", feature = "p256"))] + test_complete_flow::(b"good password", b"bad password")?; + + Ok(()) } // Zeroize tests #[test] fn test_zeroize_client_registration_start() -> Result<(), ProtocolError> { - let mut client_rng = OsRng; - let client_registration_start_result = - ClientRegistration::::start( - &mut client_rng, - STR_PASSWORD.as_bytes(), - )?; + fn inner() -> Result<(), ProtocolError> { + let mut client_rng = OsRng; + let client_registration_start_result = + ClientRegistration::::start(&mut client_rng, STR_PASSWORD.as_bytes())?; - let mut state = client_registration_start_result.state; - Zeroize::zeroize(&mut state); - for bytes in state.to_vec() { - assert!(bytes.iter().all(|&x| x == 0)); + let mut state = client_registration_start_result.state; + Zeroize::zeroize(&mut state); + for byte in state.to_vec() { + assert_eq!(byte, 0); + } + + Ok(()) } + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::()?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::()?; + Ok(()) } #[test] 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 client_registration_start_result = - ClientRegistration::::start( - &mut client_rng, - STR_PASSWORD.as_bytes(), - )?; - let server_registration_start_result = - ServerRegistration::::start( + fn inner() -> Result<(), ProtocolError> { + let mut client_rng = OsRng; + let mut server_rng = OsRng; + let server_setup = ServerSetup::::new(&mut server_rng); + let client_registration_start_result = + ClientRegistration::::start(&mut client_rng, STR_PASSWORD.as_bytes())?; + let server_registration_start_result = ServerRegistration::::start( &server_setup, client_registration_start_result.message, STR_CREDENTIAL_IDENTIFIER.as_bytes(), )?; - let client_registration_finish_result = client_registration_start_result.state.finish( - &mut client_rng, - server_registration_start_result.message, - ClientRegistrationFinishParameters::default(), - )?; + let client_registration_finish_result = client_registration_start_result.state.finish( + &mut client_rng, + server_registration_start_result.message, + ClientRegistrationFinishParameters::default(), + )?; - let mut state = client_registration_finish_result.state; - Zeroize::zeroize(&mut state); - for bytes in state.to_vec() { - assert!(bytes.iter().all(|&x| x == 0)); + let mut state = client_registration_finish_result.state; + Zeroize::zeroize(&mut state); + for byte in state.to_vec() { + assert_eq!(byte, 0); + } + + Ok(()) } + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::()?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::()?; + Ok(()) } #[test] 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 client_registration_start_result = - ClientRegistration::::start( - &mut client_rng, - STR_PASSWORD.as_bytes(), - )?; - let server_registration_start_result = - ServerRegistration::::start( + fn inner() -> Result<(), ProtocolError> + where + // Envelope: Nonce + Hash + NonceLen: Add<::OutputSize>, + EnvelopeLen: ArrayLength, + // RegistrationUpload: (KePk + Hash) + Envelope + ::PkLen: Add<::OutputSize>, + Sum<::PkLen, ::OutputSize>: + ArrayLength + Add>, + RegistrationUploadLen: ArrayLength, + // ServerRegistration = RegistrationUpload + { + let mut client_rng = OsRng; + let mut server_rng = OsRng; + let server_setup = ServerSetup::::new(&mut server_rng); + let client_registration_start_result = + ClientRegistration::::start(&mut client_rng, STR_PASSWORD.as_bytes())?; + let server_registration_start_result = ServerRegistration::::start( &server_setup, client_registration_start_result.message, STR_CREDENTIAL_IDENTIFIER.as_bytes(), )?; - let client_registration_finish_result = client_registration_start_result.state.finish( - &mut client_rng, - server_registration_start_result.message, - ClientRegistrationFinishParameters::default(), - )?; - let p_file = ServerRegistration::finish(client_registration_finish_result.message); + let client_registration_finish_result = client_registration_start_result.state.finish( + &mut client_rng, + server_registration_start_result.message, + ClientRegistrationFinishParameters::default(), + )?; + let p_file = ServerRegistration::finish(client_registration_finish_result.message); - let mut state = p_file; - Zeroize::zeroize(&mut state); - for bytes in state.serialize() { - assert!(bytes.iter().all(|&x| x == 0)); + let mut state = p_file; + Zeroize::zeroize(&mut state); + for byte in state.serialize() { + assert_eq!(byte, 0); + } + + Ok(()) } + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::()?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::()?; + Ok(()) } #[test] fn test_zeroize_client_login_start() -> Result<(), ProtocolError> { - let mut client_rng = OsRng; - let client_login_start_result = ClientLogin::::start( - &mut client_rng, - STR_PASSWORD.as_bytes(), - )?; + fn inner() -> Result<(), ProtocolError> + where + // CredentialRequest: KgPk + Ke1Message + ::ElemLen: Add>, + CredentialRequestLen: ArrayLength, + { + let mut client_rng = OsRng; + let client_login_start_result = + ClientLogin::::start(&mut client_rng, STR_PASSWORD.as_bytes())?; - let mut state = client_login_start_result.state; - Zeroize::zeroize(&mut state); - for bytes in state.to_vec() { - assert!(bytes.iter().all(|&x| x == 0)); + let mut state = client_login_start_result.state; + Zeroize::zeroize(&mut state); + for byte in state.to_vec() { + assert_eq!(byte, 0); + } + + Ok(()) } + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::()?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::()?; + Ok(()) } #[test] 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 client_registration_start_result = - ClientRegistration::::start( - &mut client_rng, - STR_PASSWORD.as_bytes(), - )?; - let server_registration_start_result = - ServerRegistration::::start( + fn inner() -> Result<(), ProtocolError> + where + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, + { + let mut client_rng = OsRng; + let mut server_rng = OsRng; + let server_setup = ServerSetup::::new(&mut server_rng); + let client_registration_start_result = + ClientRegistration::::start(&mut client_rng, STR_PASSWORD.as_bytes())?; + let server_registration_start_result = ServerRegistration::::start( &server_setup, client_registration_start_result.message, STR_CREDENTIAL_IDENTIFIER.as_bytes(), )?; - let client_registration_finish_result = client_registration_start_result.state.finish( - &mut client_rng, - server_registration_start_result.message, - ClientRegistrationFinishParameters::default(), - )?; - let p_file = ServerRegistration::finish(client_registration_finish_result.message); - let client_login_start_result = ClientLogin::::start( - &mut client_rng, - STR_PASSWORD.as_bytes(), - )?; - let server_login_start_result = ServerLogin::::start( - &mut server_rng, - &server_setup, - Some(p_file), - client_login_start_result.message, - STR_CREDENTIAL_IDENTIFIER.as_bytes(), - ServerLoginStartParameters::default(), - )?; + let client_registration_finish_result = client_registration_start_result.state.finish( + &mut client_rng, + server_registration_start_result.message, + ClientRegistrationFinishParameters::default(), + )?; + let p_file = ServerRegistration::finish(client_registration_finish_result.message); + let client_login_start_result = + ClientLogin::::start(&mut client_rng, STR_PASSWORD.as_bytes())?; + let server_login_start_result = ServerLogin::::start( + &mut server_rng, + &server_setup, + Some(p_file), + client_login_start_result.message, + STR_CREDENTIAL_IDENTIFIER.as_bytes(), + ServerLoginStartParameters::default(), + )?; - let mut state = server_login_start_result.state; - Zeroize::zeroize(&mut state); - for bytes in state.serialize() { - assert!(bytes.iter().all(|&x| x == 0)); + let mut state = server_login_start_result.state; + Zeroize::zeroize(&mut state); + for byte in state.serialize() { + assert_eq!(byte, 0); + } + + Ok(()) } + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::()?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::()?; + Ok(()) } #[test] 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 client_registration_start_result = - ClientRegistration::::start( - &mut client_rng, - STR_PASSWORD.as_bytes(), - )?; - let server_registration_start_result = - ServerRegistration::::start( + fn inner() -> Result<(), ProtocolError> + where + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, + // CredentialRequest: KgPk + Ke1Message + ::ElemLen: Add>, + CredentialRequestLen: ArrayLength, + { + let mut client_rng = OsRng; + let mut server_rng = OsRng; + let server_setup = ServerSetup::::new(&mut server_rng); + let client_registration_start_result = + ClientRegistration::::start(&mut client_rng, STR_PASSWORD.as_bytes())?; + let server_registration_start_result = ServerRegistration::::start( &server_setup, client_registration_start_result.message, STR_CREDENTIAL_IDENTIFIER.as_bytes(), )?; - let client_registration_finish_result = client_registration_start_result.state.finish( - &mut client_rng, - server_registration_start_result.message, - ClientRegistrationFinishParameters::default(), - )?; - let p_file = ServerRegistration::finish(client_registration_finish_result.message); - let client_login_start_result = ClientLogin::::start( - &mut client_rng, - STR_PASSWORD.as_bytes(), - )?; - let server_login_start_result = ServerLogin::::start( - &mut server_rng, - &server_setup, - Some(p_file), - client_login_start_result.message, - STR_CREDENTIAL_IDENTIFIER.as_bytes(), - ServerLoginStartParameters::default(), - )?; - let client_login_finish_result = client_login_start_result.state.finish( - server_login_start_result.message, - ClientLoginFinishParameters::default(), - )?; + let client_registration_finish_result = client_registration_start_result.state.finish( + &mut client_rng, + server_registration_start_result.message, + ClientRegistrationFinishParameters::default(), + )?; + let p_file = ServerRegistration::finish(client_registration_finish_result.message); + let client_login_start_result = + ClientLogin::::start(&mut client_rng, STR_PASSWORD.as_bytes())?; + let server_login_start_result = ServerLogin::::start( + &mut server_rng, + &server_setup, + Some(p_file), + client_login_start_result.message, + STR_CREDENTIAL_IDENTIFIER.as_bytes(), + ServerLoginStartParameters::default(), + )?; + let client_login_finish_result = client_login_start_result.state.finish( + server_login_start_result.message, + ClientLoginFinishParameters::default(), + )?; - let mut state = client_login_finish_result.state; - Zeroize::zeroize(&mut state); - for bytes in state.to_vec() { - assert!(bytes.iter().all(|&x| x == 0)); + let mut state = client_login_finish_result.state; + Zeroize::zeroize(&mut state); + for byte in state.to_vec() { + assert_eq!(byte, 0); + } + + Ok(()) } + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::()?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::()?; + Ok(()) } #[test] 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 client_registration_start_result = - ClientRegistration::::start( - &mut client_rng, - STR_PASSWORD.as_bytes(), - )?; - let server_registration_start_result = - ServerRegistration::::start( + fn inner() -> Result<(), ProtocolError> + where + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, + { + let mut client_rng = OsRng; + let mut server_rng = OsRng; + let server_setup = ServerSetup::::new(&mut server_rng); + let client_registration_start_result = + ClientRegistration::::start(&mut client_rng, STR_PASSWORD.as_bytes())?; + let server_registration_start_result = ServerRegistration::::start( &server_setup, client_registration_start_result.message, STR_CREDENTIAL_IDENTIFIER.as_bytes(), )?; - let client_registration_finish_result = client_registration_start_result.state.finish( - &mut client_rng, - server_registration_start_result.message, - ClientRegistrationFinishParameters::default(), - )?; - let p_file = ServerRegistration::finish(client_registration_finish_result.message); - let client_login_start_result = ClientLogin::::start( - &mut client_rng, - STR_PASSWORD.as_bytes(), - )?; - let server_login_start_result = ServerLogin::::start( - &mut server_rng, - &server_setup, - Some(p_file), - client_login_start_result.message, - STR_CREDENTIAL_IDENTIFIER.as_bytes(), - ServerLoginStartParameters::default(), - )?; - let client_login_finish_result = client_login_start_result.state.finish( - server_login_start_result.message, - ClientLoginFinishParameters::default(), - )?; - let server_login_finish_result = server_login_start_result - .state - .finish(client_login_finish_result.message)?; + let client_registration_finish_result = client_registration_start_result.state.finish( + &mut client_rng, + server_registration_start_result.message, + ClientRegistrationFinishParameters::default(), + )?; + let p_file = ServerRegistration::finish(client_registration_finish_result.message); + let client_login_start_result = + ClientLogin::::start(&mut client_rng, STR_PASSWORD.as_bytes())?; + let server_login_start_result = ServerLogin::::start( + &mut server_rng, + &server_setup, + Some(p_file), + client_login_start_result.message, + STR_CREDENTIAL_IDENTIFIER.as_bytes(), + ServerLoginStartParameters::default(), + )?; + let client_login_finish_result = client_login_start_result.state.finish( + server_login_start_result.message, + ClientLoginFinishParameters::default(), + )?; + let server_login_finish_result = server_login_start_result + .state + .finish(client_login_finish_result.message)?; - let mut state = server_login_finish_result.state; - Zeroize::zeroize(&mut state); - for bytes in state.serialize() { - assert!(bytes.iter().all(|&x| x == 0)); + let mut state = server_login_finish_result.state; + Zeroize::zeroize(&mut state); + for byte in state.serialize() { + assert_eq!(byte, 0); + } + + Ok(()) } + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::()?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::()?; + Ok(()) } #[test] fn test_scalar_always_nonzero() -> Result<(), ProtocolError> { - // Start out with a bunch of zeros to force resampling of scalar - let mut client_registration_rng = CycleRng::new([vec![0u8; 128], vec![1u8; 128]].concat()); - let client_registration_start_result = - ClientRegistration::::start( - &mut client_registration_rng, - STR_PASSWORD.as_bytes(), - )?; + fn inner() -> Result<(), ProtocolError> { + // Start out with a bunch of zeros to force resampling of scalar + let mut client_registration_rng = CycleRng::new([vec![0u8; 128], vec![1u8; 128]].concat()); + let client_registration_start_result = + ClientRegistration::::start(&mut client_registration_rng, STR_PASSWORD.as_bytes())?; - assert_ne!( - RistrettoPoint::identity(), - client_registration_start_result - .message - .get_blinded_element_for_testing() - .value(), - ); + assert!(!bool::from( + CS::OprfGroup::identity().ct_eq( + &client_registration_start_result + .message + .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(), - )?; + // 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(), - ); + assert!(!bool::from( + CS::OprfGroup::identity().ct_eq( + &client_login_start_result + .message + .get_blinded_element_for_testing() + .value(), + ) + )); + + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::()?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::()?; Ok(()) } #[test] fn test_reflected_value_error_registration() -> Result<(), ProtocolError> { - let credential_identifier = b"credentialIdentifier"; - let password = b"password"; - let mut client_rng = OsRng; - let mut server_rng = OsRng; - 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_blinded_element_for_testing() - .value(); - let server_registration_start_result = - ServerRegistration::::start( + fn inner() -> Result<(), ProtocolError> { + let credential_identifier = b"credentialIdentifier"; + let password = b"password"; + let mut client_rng = OsRng; + let mut server_rng = OsRng; + 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_blinded_element_for_testing() + .value(); + let server_registration_start_result = ServerRegistration::::start( &server_setup, client_registration_start_result.message, credential_identifier, )?; - let reflected_registration_response = server_registration_start_result - .message - .set_evaluation_element_for_testing(alpha); + let reflected_registration_response = server_registration_start_result + .message + .set_evaluation_element_for_testing(alpha); - let client_registration_finish_result = client_registration_start_result.state.finish( - &mut client_rng, - reflected_registration_response, - ClientRegistrationFinishParameters::default(), - ); + let client_registration_finish_result = client_registration_start_result.state.finish( + &mut client_rng, + reflected_registration_response, + ClientRegistrationFinishParameters::default(), + ); - assert!(match client_registration_finish_result { - Err(ProtocolError::ReflectedValueError) => true, - _ => false, - }); + assert!(matches!( + client_registration_finish_result, + Err(ProtocolError::ReflectedValueError) + )); + + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::()?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::()?; Ok(()) } #[test] fn test_reflected_value_error_login() -> Result<(), ProtocolError> { - let credential_identifier = b"credentialIdentifier"; - let password = b"password"; - let mut client_rng = OsRng; - let mut server_rng = OsRng; - let server_setup = ServerSetup::::new(&mut server_rng)?; - let client_registration_start_result = - ClientRegistration::::start(&mut client_rng, password)?; - let server_registration_start_result = - ServerRegistration::::start( + fn inner() -> Result<(), ProtocolError> + where + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, + { + let credential_identifier = b"credentialIdentifier"; + let password = b"password"; + let mut client_rng = OsRng; + let mut server_rng = OsRng; + let server_setup = ServerSetup::::new(&mut server_rng); + let client_registration_start_result = + ClientRegistration::::start(&mut client_rng, password)?; + let server_registration_start_result = ServerRegistration::::start( &server_setup, client_registration_start_result.message, credential_identifier, )?; - let client_registration_finish_result = client_registration_start_result.state.finish( - &mut client_rng, - server_registration_start_result.message, - ClientRegistrationFinishParameters::default(), - )?; - 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_blinded_element_for_testing() - .value(); - let server_login_start_result = ServerLogin::::start( - &mut server_rng, - &server_setup, - Some(p_file), - client_login_start_result.message, - credential_identifier, - ServerLoginStartParameters::default(), - )?; + let client_registration_finish_result = client_registration_start_result.state.finish( + &mut client_rng, + server_registration_start_result.message, + ClientRegistrationFinishParameters::default(), + )?; + 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_blinded_element_for_testing() + .value(); + let server_login_start_result = ServerLogin::::start( + &mut server_rng, + &server_setup, + Some(p_file), + client_login_start_result.message, + credential_identifier, + ServerLoginStartParameters::default(), + )?; - let reflected_credential_response = server_login_start_result - .message - .set_evaluation_element_for_testing(alpha); + let reflected_credential_response = server_login_start_result + .message + .set_evaluation_element_for_testing(alpha); - let client_login_result = client_login_start_result.state.finish( - reflected_credential_response, - ClientLoginFinishParameters::default(), - ); + let client_login_result = client_login_start_result.state.finish( + reflected_credential_response, + ClientLoginFinishParameters::default(), + ); + + assert!(matches!( + client_login_result, + Err(ProtocolError::ReflectedValueError) + )); + Ok(()) + } + + #[cfg(feature = "ristretto255")] + inner::()?; + #[cfg(feature = "p256")] + inner::()?; + #[cfg(all(feature = "x25519", feature = "ristretto255"))] + inner::()?; + #[cfg(all(feature = "x25519", feature = "p256"))] + inner::()?; - assert!(match client_login_result { - Err(ProtocolError::ReflectedValueError) => true, - _ => false, - }); Ok(()) } diff --git a/src/tests/mock_rng.rs b/src/tests/mock_rng.rs index 9f58b37..d646556 100644 --- a/src/tests/mock_rng.rs +++ b/src/tests/mock_rng.rs @@ -14,7 +14,7 @@ use rand::{CryptoRng, Error, RngCore}; /// This generates a cyclic sequence (i.e. cycles over an initial buffer) /// /// -#[derive(Debug, Clone)] +#[derive(Clone, Debug)] pub struct CycleRng { v: Vec, } diff --git a/src/tests/parser.rs b/src/tests/parser.rs index bbfaf9c..1472cf7 100644 --- a/src/tests/parser.rs +++ b/src/tests/parser.rs @@ -21,7 +21,7 @@ fn parse_vector_types(input: &str) -> String { for caps in re.captures_iter(input) { let vector_type = format!( "\"{}\": [\n {} \n]", - caps["type"].to_string(), + &caps["type"], parse_ciphersuites(chunks[count]) ); vector_types.push(vector_type); @@ -44,8 +44,8 @@ fn parse_ciphersuites(input: &str) -> String { for caps in re.captures_iter(input) { let ciphersuite = format!( "{{ \"{}, {}\": {{ {} }} }}", - caps["group"].to_string(), - caps["hash"].to_string(), + &caps["group"], + &caps["hash"], parse_params(chunks[count]) ); ciphersuites.push(ciphersuite); diff --git a/src/tests/test_opaque_vectors.rs b/src/tests/test_opaque_vectors.rs old mode 100644 new mode 100755 index 113f248..137b2be --- a/src/tests/test_opaque_vectors.rs +++ b/src/tests/test_opaque_vectors.rs @@ -6,11 +6,29 @@ // of this source tree. use crate::{ - ciphersuite::CipherSuite, errors::*, key_exchange::tripledh::TripleDH, opaque::*, - slow_hash::NoOpHash, tests::mock_rng::CycleRng, *, + ciphersuite::CipherSuite, + envelope::EnvelopeLen, + errors::*, + key_exchange::{ + group::KeGroup, + traits::{Ke1MessageLen, Ke2MessageLen}, + tripledh::{NonceLen, TripleDH}, + }, + messages::{ + CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen, + RegistrationResponseLen, RegistrationUploadLen, + }, + opaque::*, + slow_hash::NoOpHash, + tests::mock_rng::CycleRng, + *, }; use alloc::{string::ToString, vec, vec::Vec}; +use core::ops::Add; +use digest::FixedOutput; +use generic_array::{typenum::Sum, ArrayLength}; use json::JsonValue; +use voprf::group::Group; #[allow(non_snake_case)] #[derive(Debug)] @@ -130,29 +148,23 @@ fn populate_test_vectors(values: &JsonValue) -> OpaqueTestVectorParameters { } } -fn get_password_file_bytes( - parameters: &OpaqueTestVectorParameters, -) -> Result, ProtocolError> { +fn get_password_file_bytes(parameters: &OpaqueTestVectorParameters) -> Vec +where + // Envelope: Nonce + Hash + NonceLen: Add<::OutputSize>, + EnvelopeLen: ArrayLength, + // RegistrationUpload: (KePk + Hash) + Envelope + ::PkLen: Add<::OutputSize>, + Sum<::PkLen, ::OutputSize>: + ArrayLength + Add>, + RegistrationUploadLen: ArrayLength, + // ServerRegistration = RegistrationUpload +{ let password_file = ServerRegistration::::finish( - RegistrationUpload::deserialize(¶meters.registration_upload[..]).unwrap(), + RegistrationUpload::deserialize(¶meters.registration_upload).unwrap(), ); - password_file.serialize() -} - -fn parse_identifiers( - client_identity: &Option>, - server_identity: &Option>, -) -> Option { - match (client_identity, server_identity) { - (None, None) => None, - (Some(x), None) => Some(Identifiers::ClientIdentifier(x.clone())), - (None, Some(y)) => Some(Identifiers::ServerIdentifier(y.clone())), - (Some(x), Some(y)) => Some(Identifiers::ClientAndServerIdentifiers( - x.clone(), - y.clone(), - )), - } + password_file.serialize().to_vec() } macro_rules! json_to_test_vectors { @@ -175,32 +187,36 @@ 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, "Real", "ristretto255, SHA512",); + #[cfg(feature = "ristretto255")] + { + let ristretto_real_tvs = json_to_test_vectors!(rfc, "Real", "ristretto255, SHA512",); - let ristretto_fake_tvs = json_to_test_vectors!(rfc, "Fake", "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"); + assert!( + !(ristretto_real_tvs.is_empty() || ristretto_fake_tvs.is_empty()), + "Parsing error" + ); + + struct Ristretto255Sha512NoSlowHash; + impl CipherSuite for Ristretto255Sha512NoSlowHash { + type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; + type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; + type KeyExchange = TripleDH; + type Hash = sha2::Sha512; + type SlowHash = NoOpHash; + } + + test_registration_request::(&ristretto_real_tvs)?; + test_registration_response::(&ristretto_real_tvs)?; + test_registration_upload::(&ristretto_real_tvs)?; + test_ke1::(&ristretto_real_tvs)?; + test_ke2::(&ristretto_real_tvs)?; + test_ke3::(&ristretto_real_tvs)?; + test_server_login_finish::(&ristretto_real_tvs)?; + test_fake_vectors::(&ristretto_fake_tvs)?; } - struct Ristretto255Sha512NoSlowHash; - impl CipherSuite for Ristretto255Sha512NoSlowHash { - type OprfGroup = curve25519_dalek::ristretto::RistrettoPoint; - type KeGroup = curve25519_dalek::ristretto::RistrettoPoint; - type KeyExchange = TripleDH; - type Hash = sha2::Sha512; - type SlowHash = NoOpHash; - } - - test_registration_request::(&ristretto_real_tvs)?; - test_registration_response::(&ristretto_real_tvs)?; - test_registration_upload::(&ristretto_real_tvs)?; - test_ke1::(&ristretto_real_tvs)?; - test_ke2::(&ristretto_real_tvs)?; - test_ke3::(&ristretto_real_tvs)?; - test_server_login_finish::(&ristretto_real_tvs)?; - test_fake_vectors::(&ristretto_fake_tvs)?; - #[cfg(feature = "p256")] { let p256_real_tvs = @@ -208,14 +224,15 @@ fn tests() -> Result<(), ProtocolError> { 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"); - } + assert!( + !(p256_real_tvs.is_empty() || p256_fake_tvs.is_empty()), + "Parsing error" + ); struct P256Sha256NoSlowHash; impl CipherSuite for P256Sha256NoSlowHash { type OprfGroup = p256_::ProjectivePoint; - type KeGroup = p256_::ProjectivePoint; + type KeGroup = p256_::PublicKey; type KeyExchange = TripleDH; type Hash = sha2::Sha256; type SlowHash = NoOpHash; @@ -243,7 +260,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(()) @@ -251,19 +268,24 @@ fn test_registration_request( fn test_registration_response( tvs: &[OpaqueTestVectorParameters], -) -> Result<(), ProtocolError> { +) -> Result<(), ProtocolError> +where + // RegistrationResponse: KgPk + KePk + ::ElemLen: Add<::PkLen>, + RegistrationResponseLen: ArrayLength, +{ for parameters in tvs { let server_setup = ServerSetup::::deserialize( &[ - ¶meters.oprf_seed[..], - ¶meters.server_private_key[..], - ¶meters.dummy_private_key[..], + parameters.oprf_seed.as_slice(), + ¶meters.server_private_key, + ¶meters.dummy_private_key, ] .concat(), )?; let server_registration_start_result = ServerRegistration::::start( &server_setup, - RegistrationRequest::deserialize(¶meters.registration_request[..]).unwrap(), + RegistrationRequest::deserialize(¶meters.registration_request).unwrap(), ¶meters.credential_identifier, )?; assert_eq!( @@ -272,7 +294,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(()) @@ -280,7 +302,17 @@ fn test_registration_response( fn test_registration_upload( tvs: &[OpaqueTestVectorParameters], -) -> Result<(), ProtocolError> { +) -> Result<(), ProtocolError> +where + // Envelope: Nonce + Hash + NonceLen: Add<::OutputSize>, + EnvelopeLen: ArrayLength, + // RegistrationUpload: (KePk + Hash) + Envelope + ::PkLen: Add<::OutputSize>, + Sum<::PkLen, ::OutputSize>: + ArrayLength + Add>, + RegistrationUploadLen: ArrayLength, +{ for parameters in tvs { let mut rng = CycleRng::new(parameters.blind_registration.to_vec()); let client_registration_start_result = @@ -289,11 +321,14 @@ fn test_registration_upload( let mut finish_registration_rng = CycleRng::new(parameters.envelope_nonce.to_vec()); let result = client_registration_start_result.state.finish( &mut finish_registration_rng, - RegistrationResponse::deserialize(¶meters.registration_response[..]).unwrap(), - match parse_identifiers(¶meters.client_identity, ¶meters.server_identity) { - None => ClientRegistrationFinishParameters::default(), - Some(ids) => ClientRegistrationFinishParameters::new(Some(ids), None), - }, + RegistrationResponse::deserialize(¶meters.registration_response).unwrap(), + ClientRegistrationFinishParameters::new( + Identifiers { + client: parameters.client_identity.as_deref(), + server: parameters.server_identity.as_deref(), + }, + None, + ), )?; assert_eq!( hex::encode(¶meters.auth_key), @@ -305,7 +340,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), @@ -316,18 +351,23 @@ fn test_registration_upload( Ok(()) } -fn test_ke1(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError> { +fn test_ke1(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError> +where + // CredentialRequest: KgPk + Ke1Message + ::ElemLen: Add>, + CredentialRequestLen: ArrayLength, +{ for parameters in tvs { let client_login_start = [ - ¶meters.blind_login[..], - ¶meters.client_private_keyshare[..], - ¶meters.client_nonce[..], + parameters.blind_login.as_slice(), + ¶meters.client_private_keyshare, + ¶meters.client_nonce, ] .concat(); println!( - "¶meters.blind_login[..]: {:?}", - hex::encode(¶meters.blind_login[..]) + "parameters.blind_login: {:?}", + hex::encode(¶meters.blind_login) ); let mut client_login_start_rng = CycleRng::new(client_login_start); @@ -335,32 +375,59 @@ fn test_ke1(tvs: &[OpaqueTestVectorParameters]) -> Result<(), P 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(()) } -fn test_ke2(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError> { +fn test_ke2(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError> +where + // Envelope: Nonce + Hash + NonceLen: Add<::OutputSize>, + EnvelopeLen: ArrayLength, + // RegistrationUpload: (KePk + Hash) + Envelope + ::PkLen: Add<::OutputSize>, + Sum<::PkLen, ::OutputSize>: + ArrayLength + Add>, + RegistrationUploadLen: ArrayLength, + // ServerRegistration = RegistrationUpload + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, + // CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse + ::ElemLen: Add, + Sum<::ElemLen, NonceLen>: ArrayLength + Add>, + CredentialResponseWithoutKeLen: ArrayLength, + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, + // CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message + CredentialResponseWithoutKeLen: Add>, + CredentialResponseLen: ArrayLength, +{ for parameters in tvs { let server_setup = ServerSetup::::deserialize( &[ - ¶meters.oprf_seed[..], - ¶meters.server_private_key[..], - ¶meters.dummy_private_key[..], + parameters.oprf_seed.as_slice(), + ¶meters.server_private_key, + ¶meters.dummy_private_key, ] .concat(), )?; - let record = ServerRegistration::::deserialize( - &get_password_file_bytes::(¶meters)?[..], - )?; + let record = + ServerRegistration::::deserialize(&get_password_file_bytes::(parameters))?; let mut server_private_keyshare_and_nonce_rng = CycleRng::new( [ - ¶meters.masking_nonce[..], - ¶meters.server_private_keyshare[..], - ¶meters.server_nonce[..], + parameters.masking_nonce.as_slice(), + ¶meters.server_private_keyshare, + ¶meters.server_nonce, ] .concat(), ); @@ -368,14 +435,14 @@ fn test_ke2(tvs: &[OpaqueTestVectorParameters]) -> Result<(), P &mut server_private_keyshare_and_nonce_rng, &server_setup, Some(record), - CredentialRequest::::deserialize(¶meters.KE1[..]).unwrap(), + CredentialRequest::::deserialize(¶meters.KE1).unwrap(), ¶meters.credential_identifier, - match parse_identifiers(¶meters.client_identity, ¶meters.server_identity) { - None => ServerLoginStartParameters::WithContext(parameters.context.to_vec()), - Some(ids) => ServerLoginStartParameters::WithContextAndIdentifiers( - parameters.context.to_vec(), - ids, - ), + ServerLoginStartParameters { + context: Some(¶meters.context), + identifiers: Identifiers { + client: parameters.client_identity.as_deref(), + server: parameters.server_identity.as_deref(), + }, }, )?; assert_eq!( @@ -392,18 +459,25 @@ 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(()) } -fn test_ke3(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError> { +fn test_ke3(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError> +where + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, +{ for parameters in tvs { let client_login_start = [ - ¶meters.blind_login[..], - ¶meters.client_private_keyshare[..], - ¶meters.client_nonce[..], + parameters.blind_login.as_slice(), + ¶meters.client_private_keyshare, + ¶meters.client_nonce, ] .concat(); let mut client_login_start_rng = CycleRng::new(client_login_start); @@ -411,17 +485,15 @@ fn test_ke3(tvs: &[OpaqueTestVectorParameters]) -> Result<(), P ClientLogin::::start(&mut client_login_start_rng, ¶meters.password)?; let client_login_finish_result = client_login_start_result.state.finish( - CredentialResponse::::deserialize(¶meters.KE2[..])?, - match parse_identifiers(¶meters.client_identity, ¶meters.server_identity) { - None => { - ClientLoginFinishParameters::new(Some(parameters.context.clone()), None, None) - } - Some(ids) => ClientLoginFinishParameters::new( - Some(parameters.context.clone()), - Some(ids), - None, - ), - }, + CredentialResponse::::deserialize(¶meters.KE2)?, + ClientLoginFinishParameters::new( + Some(¶meters.context.clone()), + Identifiers { + client: parameters.client_identity.as_deref(), + server: parameters.server_identity.as_deref(), + }, + None, + ), )?; assert_eq!( @@ -438,7 +510,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), @@ -450,26 +522,41 @@ fn test_ke3(tvs: &[OpaqueTestVectorParameters]) -> Result<(), P fn test_server_login_finish( tvs: &[OpaqueTestVectorParameters], -) -> Result<(), ProtocolError> { +) -> Result<(), ProtocolError> +where + // Envelope: Nonce + Hash + NonceLen: Add<::OutputSize>, + EnvelopeLen: ArrayLength, + // RegistrationUpload: (KePk + Hash) + Envelope + ::PkLen: Add<::OutputSize>, + Sum<::PkLen, ::OutputSize>: + ArrayLength + Add>, + RegistrationUploadLen: ArrayLength, + // ServerRegistration = RegistrationUpload + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, +{ for parameters in tvs { let server_setup = ServerSetup::::deserialize( &[ - ¶meters.oprf_seed[..], - ¶meters.server_private_key[..], - ¶meters.dummy_private_key[..], + parameters.oprf_seed.as_slice(), + ¶meters.server_private_key, + ¶meters.dummy_private_key, ] .concat(), )?; - let record = ServerRegistration::::deserialize( - &get_password_file_bytes::(¶meters)?[..], - )?; + let record = + ServerRegistration::::deserialize(&get_password_file_bytes::(parameters))?; let mut server_private_keyshare_and_nonce_rng = CycleRng::new( [ - ¶meters.masking_nonce[..], - ¶meters.server_private_keyshare[..], - ¶meters.server_nonce[..], + parameters.masking_nonce.as_slice(), + ¶meters.server_private_keyshare, + ¶meters.server_nonce, ] .concat(), ); @@ -477,20 +564,20 @@ fn test_server_login_finish( &mut server_private_keyshare_and_nonce_rng, &server_setup, Some(record), - CredentialRequest::::deserialize(¶meters.KE1[..]).unwrap(), + CredentialRequest::::deserialize(¶meters.KE1).unwrap(), ¶meters.credential_identifier, - match parse_identifiers(¶meters.client_identity, ¶meters.server_identity) { - None => ServerLoginStartParameters::WithContext(parameters.context.to_vec()), - Some(ids) => ServerLoginStartParameters::WithContextAndIdentifiers( - parameters.context.to_vec(), - ids, - ), + ServerLoginStartParameters { + context: Some(¶meters.context), + identifiers: Identifiers { + client: parameters.client_identity.as_deref(), + server: parameters.server_identity.as_deref(), + }, }, )?; let server_login_result = server_login_start_result .state - .finish(CredentialFinalization::deserialize(¶meters.KE3[..])?)?; + .finish(CredentialFinalization::deserialize(¶meters.KE3)?)?; assert_eq!( hex::encode(¶meters.session_key), @@ -502,23 +589,37 @@ fn test_server_login_finish( fn test_fake_vectors( tvs: &[OpaqueTestVectorParameters], -) -> Result<(), ProtocolError> { +) -> Result<(), ProtocolError> +where + // MaskedResponse: (Nonce + Hash) + KePk + NonceLen: Add<::OutputSize>, + Sum::OutputSize>: + ArrayLength + Add<::PkLen>, + MaskedResponseLen: ArrayLength, + // CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse + ::ElemLen: Add, + Sum<::ElemLen, NonceLen>: ArrayLength + Add>, + CredentialResponseWithoutKeLen: ArrayLength, + // CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message + CredentialResponseWithoutKeLen: Add>, + CredentialResponseLen: ArrayLength, +{ for parameters in tvs { let server_setup = ServerSetup::::deserialize( &[ - ¶meters.oprf_seed[..], - ¶meters.server_private_key[..], - ¶meters.dummy_private_key[..], + parameters.oprf_seed.as_slice(), + ¶meters.server_private_key, + ¶meters.dummy_private_key, ] .concat(), )?; let mut server_private_keyshare_and_nonce_rng = CycleRng::new( [ - ¶meters.dummy_masking_key[..], - ¶meters.masking_nonce[..], - ¶meters.server_private_keyshare[..], - ¶meters.server_nonce[..], + parameters.dummy_masking_key.as_slice(), + ¶meters.masking_nonce, + ¶meters.server_private_keyshare, + ¶meters.server_nonce, ] .concat(), ); @@ -526,19 +627,19 @@ fn test_fake_vectors( &mut server_private_keyshare_and_nonce_rng, &server_setup, None, - CredentialRequest::::deserialize(¶meters.KE1[..]).unwrap(), + CredentialRequest::::deserialize(¶meters.KE1).unwrap(), ¶meters.credential_identifier, - match parse_identifiers(¶meters.client_identity, ¶meters.server_identity) { - None => ServerLoginStartParameters::WithContext(parameters.context.to_vec()), - Some(ids) => ServerLoginStartParameters::WithContextAndIdentifiers( - parameters.context.to_vec(), - ids, - ), + ServerLoginStartParameters { + context: Some(¶meters.context), + identifiers: Identifiers { + client: parameters.client_identity.as_deref(), + server: parameters.server_identity.as_deref(), + }, }, )?; assert_eq!( hex::encode(¶meters.KE2), - hex::encode(server_login_start_result.message.serialize()?) + hex::encode(server_login_start_result.message.serialize()) ); } Ok(())