Rust CI / cargo fmt (pull_request) Successful in 13s
Rust CI / test (1.88.0 / no backend / no frontend) (pull_request) Failing after 13s
Rust CI / cargo clippy (pull_request) Successful in 1m23s
Rust CI / test (stable / no backend / no frontend) (pull_request) Successful in 2m18s
Rust CI / test (1.88.0 / no backend / --features argon2) (pull_request) Failing after 3s
Rust CI / test (stable / no backend / --features argon2) (pull_request) Successful in 2m27s
Rust CI / test (stable / no backend / --features serde) (pull_request) Successful in 2m48s
Rust CI / test (1.88.0 / no backend / --features serde) (pull_request) Failing after 4s
Rust CI / test (1.88.0 / --features curve25519 / no frontend) (pull_request) Failing after 4s
Rust CI / test (stable / --features curve25519 / no frontend) (pull_request) Successful in 2m23s
Rust CI / test (1.88.0 / --features curve25519 / --features argon2) (pull_request) Failing after 4s
Rust CI / test (stable / --features curve25519 / --features argon2) (pull_request) Successful in 2m32s
Rust CI / test (1.88.0 / --features curve25519 / --features serde) (pull_request) Failing after 4s
Rust CI / test (stable / --features curve25519 / --features serde) (pull_request) Successful in 2m50s
Rust CI / test (stable / --features ecdsa / no frontend) (pull_request) Successful in 2m44s
Rust CI / test (1.88.0 / --features ecdsa / no frontend) (pull_request) Failing after 4s
Rust CI / test (1.88.0 / --features ecdsa / --features serde) (pull_request) Has been cancelled
Rust CI / test (stable / --features ecdsa / --features serde) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ed25519 / no frontend) (pull_request) Has been cancelled
Rust CI / test (stable / --features ed25519 / no frontend) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ed25519 / --features argon2) (pull_request) Has been cancelled
Rust CI / test (stable / --features ed25519 / --features argon2) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ed25519 / --features serde) (pull_request) Has been cancelled
Rust CI / test (stable / --features ed25519 / --features serde) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ristretto255 / no frontend) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255 / no frontend) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ristretto255 / --features argon2) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255 / --features argon2) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ristretto255 / --features serde) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255 / --features serde) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ecdsa / --features argon2) (pull_request) Failing after 3s
Rust CI / test (1.88.0 / --features ristretto255,curve25519,ecdsa,ed25519 / no frontend) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255,curve25519,ecdsa,ed25519 / no frontend) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ristretto255,curve25519,ecdsa,ed25519 / --features argon2) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255,curve25519,ecdsa,ed25519 / --features argon2) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ristretto255,curve25519,ecdsa,ed25519 / --features serde) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255,curve25519,ecdsa,ed25519 / --features serde) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ristretto255,kem / no frontend) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255,kem / no frontend) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ristretto255,kem / --features argon2) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255,kem / --features argon2) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ristretto255,kem / --features serde) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255,kem / --features serde) (pull_request) Has been cancelled
Rust CI / test simple_login example (pull_request) Has been cancelled
Rust CI / test digital_locker example (pull_request) Has been cancelled
Rust CI / cargo bench compilation () (pull_request) Has been cancelled
Rust CI / cargo bench compilation (--features ristretto255) (pull_request) Has been cancelled
Rust CI / cargo bench compilation (--features ristretto255,kem) (pull_request) Has been cancelled
Rust CI / cargo audit (pull_request) Has been cancelled
Rust CI / test (stable / --features ecdsa / --features argon2) (pull_request) Has been cancelled
Rust CI / no-std (wasm32-unknown-unknown / ristretto255,curve25519,ecdsa,ed25519) (pull_request) Has been cancelled
Rust CI / no-std (thumbv6m-none-eabi / no backend) (pull_request) Has been cancelled
Rust CI / no-std (wasm32-unknown-unknown / no backend) (pull_request) Has been cancelled
Rust CI / no-std (thumbv6m-none-eabi / curve25519) (pull_request) Has been cancelled
Rust CI / no-std (wasm32-unknown-unknown / curve25519) (pull_request) Has been cancelled
Rust CI / no-std (thumbv6m-none-eabi / ecdsa) (pull_request) Has been cancelled
Rust CI / no-std (wasm32-unknown-unknown / ecdsa) (pull_request) Has been cancelled
Rust CI / no-std (thumbv6m-none-eabi / ed25519) (pull_request) Has been cancelled
Rust CI / no-std (wasm32-unknown-unknown / ed25519) (pull_request) Has been cancelled
Rust CI / no-std (thumbv6m-none-eabi / ristretto255) (pull_request) Has been cancelled
Rust CI / no-std (wasm32-unknown-unknown / ristretto255) (pull_request) Has been cancelled
Rust CI / no-std (thumbv6m-none-eabi / ristretto255,curve25519,ecdsa,ed25519) (pull_request) Has been cancelled
Upgrade all core cryptographic dependencies to their latest versions: Dependencies: - digest: 0.10 to 0.11 - elliptic-curve: 0.13 to 0.14 - hkdf: 0.12 to 0.13 - hmac: 0.12 to 0.13 - rand: 0.8 to 0.10 - rand_chacha: 0.3 to 0.10 - sha2: 0.10 to 0.11 - getrandom: 0.2 to 0.4 (WASM) - ml-kem: 0.3.0-rc.0 to 0.3 - ecdsa: 0.16 to 0.17.0-rc.23 - rfc6979: 0.4 to 0.6 (now internal to ecdsa) - p256/p384/p521: 0.13 to 0.14.0-rc.15 - curve25519-dalek: 4 to 5.0.0-rc - ed25519-dalek: 2 to 3.0.0-rc - cryptoki: 0.9 to 0.12 - rustyline: 17 to 18 - scrypt: 0.11 to 0.12 - voprf replaced by voprf-vx 1.0.0-pre.0 Migration changes: - generic-array 0.14 to 1.4 with hybrid-array 0.4 interop - ArrayLength<u8> to ArrayLength (generic-array 1.x) - Added ConcatExt trait to disambiguate from [T]::concat - Replaced Hmac with SimpleHmac for digest 0.11 compatibility - Added OutputSize<H>: ArrayLength bounds throughout Hash trait - Converted hybrid_array::Array between GenericArray at API boundaries - Updated GroupEncoding Repr bound to hybrid_array::Array - ECDSA sign now uses ecdsa::hazmat::sign_prehashed_rfc6979 - Removed direct rfc6979 dependency (handled by ecdsa internally) - Replaced bincode with postcard for no_std serialization - Re-exported hybrid_array from crate root Other changes: - Renamed crate to opaque-vx - Increased MSRV to 1.88 - Added cryptography to Cargo.toml categories - Removed Facebook-specific contributions from CONTRIBUTING.md - Removed v3 to v4 migration test - Removed unstable rustfmt configurations for stable compatibility
693 lines
25 KiB
Rust
693 lines
25 KiB
Rust
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
//
|
|
// This source code is dual-licensed under either the MIT license found in the
|
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
// of this source tree. You may select, at your option, one of the above-listed
|
|
// licenses.
|
|
|
|
use core::ops::Add;
|
|
use std::vec;
|
|
use std::vec::Vec;
|
|
|
|
use crate::ciphersuite::{CipherSuite, KeGroup, OprfGroup, OprfHash};
|
|
use crate::envelope::EnvelopeLen;
|
|
use crate::errors::*;
|
|
use crate::hash::OutputSize;
|
|
use crate::key_exchange::group::Group;
|
|
use crate::key_exchange::shared::NonceLen;
|
|
use crate::key_exchange::{Deserialize, Ke1MessageLen, Ke2MessageLen, KeyExchange, Serialize};
|
|
use crate::ksf::Identity;
|
|
use crate::messages::{
|
|
CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen,
|
|
RegistrationResponseLen, RegistrationUploadLen,
|
|
};
|
|
use crate::opaque::*;
|
|
use crate::tests::decode;
|
|
use crate::tests::mock_rng::CycleRng;
|
|
use crate::*;
|
|
use digest::OutputSizeUser;
|
|
use generic_array::typenum::Sum;
|
|
use generic_array::{ArrayLength, GenericArray};
|
|
use rand::Rng;
|
|
use rand::rngs::SysRng;
|
|
use rand_core::UnwrapErr;
|
|
use serde_json::Value;
|
|
|
|
#[allow(non_snake_case)]
|
|
#[derive(Debug)]
|
|
pub struct OpaqueTestVectorParameters {
|
|
pub dummy_public_key: Vec<u8>,
|
|
pub dummy_masking_key: Vec<u8>,
|
|
pub context: Vec<u8>,
|
|
#[allow(dead_code)] // client_private_key is not tested in the test vectors
|
|
pub client_private_key: Option<Vec<u8>>,
|
|
pub client_keyshare_seed: Vec<u8>,
|
|
pub server_public_key: Vec<u8>,
|
|
pub server_private_key: Vec<u8>,
|
|
pub server_keyshare_seed: Vec<u8>,
|
|
pub client_identity: Option<Vec<u8>>,
|
|
pub server_identity: Option<Vec<u8>>,
|
|
pub credential_identifier: Vec<u8>,
|
|
pub password: Vec<u8>,
|
|
pub blind_registration: Vec<u8>,
|
|
pub oprf_seed: Vec<u8>,
|
|
pub masking_nonce: Vec<u8>,
|
|
pub envelope_nonce: Vec<u8>,
|
|
pub client_nonce: Vec<u8>,
|
|
pub server_nonce: Vec<u8>,
|
|
pub registration_request: Vec<u8>,
|
|
pub registration_response: Vec<u8>,
|
|
pub registration_upload: Vec<u8>,
|
|
pub KE1: Vec<u8>,
|
|
pub blind_login: Vec<u8>,
|
|
pub KE2: Vec<u8>,
|
|
pub KE3: Vec<u8>,
|
|
pub export_key: Vec<u8>,
|
|
pub session_key: Vec<u8>,
|
|
pub auth_key: Vec<u8>,
|
|
pub randomized_pwd: Vec<u8>,
|
|
pub handshake_secret: Vec<u8>,
|
|
pub server_mac_key: Vec<u8>,
|
|
pub client_mac_key: Vec<u8>,
|
|
pub oprf_key: Vec<u8>,
|
|
}
|
|
|
|
macro_rules! parse {
|
|
( $v:ident, $s:expr ) => {
|
|
parse_default!($v, $s, vec![])
|
|
};
|
|
}
|
|
|
|
macro_rules! parse_default {
|
|
( $v:ident, $s:expr, $d:expr ) => {
|
|
match decode(&$v, $s) {
|
|
Some(x) => x,
|
|
None => $d,
|
|
}
|
|
};
|
|
}
|
|
|
|
fn populate_test_vectors<CS: CipherSuite>(values: &Value) -> OpaqueTestVectorParameters {
|
|
let mut rng = UnwrapErr(SysRng);
|
|
|
|
OpaqueTestVectorParameters {
|
|
dummy_public_key: {
|
|
decode(values, "client_public_key").unwrap_or_else(|| {
|
|
KeGroup::<CS>::serialize_sk(&KeGroup::<CS>::random_sk(&mut UnwrapErr(SysRng)))
|
|
.to_vec()
|
|
})
|
|
},
|
|
dummy_masking_key: {
|
|
match decode(values, "masking_key") {
|
|
Some(value) => value,
|
|
None => {
|
|
let mut bytes =
|
|
GenericArray::<u8, <OprfHash<CS> as OutputSizeUser>::OutputSize>::default();
|
|
rng.fill_bytes(&mut bytes);
|
|
bytes.to_vec()
|
|
}
|
|
}
|
|
},
|
|
context: parse!(values, "Context"),
|
|
client_private_key: decode(values, "client_private_key"),
|
|
client_keyshare_seed: parse!(values, "client_keyshare_seed"),
|
|
server_public_key: parse!(values, "server_public_key"),
|
|
server_private_key: parse!(values, "server_private_key"),
|
|
server_keyshare_seed: parse!(values, "server_keyshare_seed"),
|
|
client_identity: decode(values, "client_identity"),
|
|
server_identity: decode(values, "server_identity"),
|
|
credential_identifier: parse!(values, "credential_identifier"),
|
|
password: parse!(values, "password"),
|
|
blind_registration: parse!(values, "blind_registration"),
|
|
oprf_seed: parse!(values, "oprf_seed"),
|
|
masking_nonce: parse!(values, "masking_nonce"),
|
|
envelope_nonce: parse!(values, "envelope_nonce"),
|
|
client_nonce: parse!(values, "client_nonce"),
|
|
server_nonce: parse!(values, "server_nonce"),
|
|
registration_request: parse!(values, "registration_request"),
|
|
registration_response: parse!(values, "registration_response"),
|
|
registration_upload: parse!(values, "registration_upload"),
|
|
KE1: parse!(values, "KE1"),
|
|
KE2: parse!(values, "KE2"),
|
|
KE3: parse!(values, "KE3"),
|
|
blind_login: parse!(values, "blind_login"),
|
|
export_key: parse!(values, "export_key"),
|
|
session_key: parse!(values, "session_key"),
|
|
auth_key: parse!(values, "auth_key"),
|
|
randomized_pwd: parse!(values, "randomized_password"),
|
|
handshake_secret: parse!(values, "handshake_secret"),
|
|
server_mac_key: parse!(values, "server_mac_key"),
|
|
client_mac_key: parse!(values, "client_mac_key"),
|
|
oprf_key: parse!(values, "oprf_key"),
|
|
}
|
|
}
|
|
|
|
fn get_password_file_bytes<CS: CipherSuite>(parameters: &OpaqueTestVectorParameters) -> Vec<u8>
|
|
where
|
|
// RegistrationUpload: (KePk + Hash) + Envelope
|
|
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength + Add<EnvelopeLen<CS>>,
|
|
RegistrationUploadLen<CS>: ArrayLength,
|
|
// ServerRegistration = RegistrationUpload
|
|
{
|
|
let password_file = ServerRegistration::<CS>::finish(
|
|
RegistrationUpload::deserialize(¶meters.registration_upload).unwrap(),
|
|
);
|
|
|
|
password_file.serialize().to_vec()
|
|
}
|
|
|
|
macro_rules! json_to_test_vectors {
|
|
( $v:ident, $vector_type:expr, $cs:expr, $cs_ty:ty) => {
|
|
$v[$vector_type]
|
|
.as_array()
|
|
.into_iter()
|
|
.flatten()
|
|
.filter_map(|x| {
|
|
if let Some(val) = x.get($cs) {
|
|
Some(populate_test_vectors::<$cs_ty>(val))
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect::<Vec<OpaqueTestVectorParameters>>()
|
|
};
|
|
}
|
|
|
|
#[test]
|
|
fn tests() -> Result<(), ProtocolError> {
|
|
let rfc: Value =
|
|
serde_json::from_str(super::parser::rfc_to_json(super::rfc9807_vectors::VECTORS).as_str())
|
|
.expect("Could not parse json");
|
|
|
|
std::eprintln!("{}", serde_json::to_string_pretty(&rfc).unwrap());
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
{
|
|
struct Ristretto255Sha512NoKsf;
|
|
impl CipherSuite for Ristretto255Sha512NoKsf {
|
|
type OprfCs = Ristretto255;
|
|
type KeyExchange = TripleDh<Ristretto255, sha2::Sha512>;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
let ristretto_real_tvs = json_to_test_vectors!(
|
|
rfc,
|
|
"Real",
|
|
"ristretto255-SHA512, ristretto255",
|
|
Ristretto255Sha512NoKsf
|
|
);
|
|
|
|
let ristretto_fake_tvs = json_to_test_vectors!(
|
|
rfc,
|
|
"Fake",
|
|
"ristretto255-SHA512, ristretto255",
|
|
Ristretto255Sha512NoKsf
|
|
);
|
|
|
|
assert!(
|
|
!(ristretto_real_tvs.is_empty() || ristretto_fake_tvs.is_empty()),
|
|
"Parsing error"
|
|
);
|
|
|
|
// There should be 2 real test vectors and 1 fake test vector
|
|
assert_eq!(ristretto_real_tvs.len(), 2);
|
|
assert_eq!(ristretto_fake_tvs.len(), 1);
|
|
|
|
test_registration_request::<Ristretto255Sha512NoKsf>(&ristretto_real_tvs)?;
|
|
test_registration_response::<Ristretto255Sha512NoKsf>(&ristretto_real_tvs)?;
|
|
test_registration_upload::<Ristretto255Sha512NoKsf>(&ristretto_real_tvs)?;
|
|
test_ke1::<Ristretto255Sha512NoKsf>(&ristretto_real_tvs)?;
|
|
test_ke2::<Ristretto255Sha512NoKsf>(&ristretto_real_tvs)?;
|
|
test_ke3::<Ristretto255Sha512NoKsf>(&ristretto_real_tvs)?;
|
|
test_server_login_finish::<Ristretto255Sha512NoKsf>(&ristretto_real_tvs)?;
|
|
test_fake_vectors::<Ristretto255Sha512NoKsf>(&ristretto_fake_tvs)?;
|
|
}
|
|
|
|
#[cfg(all(feature = "ristretto255", feature = "curve25519"))]
|
|
{
|
|
struct Ristretto255Sha512Curve25519NoKsf;
|
|
impl CipherSuite for Ristretto255Sha512Curve25519NoKsf {
|
|
type OprfCs = crate::Ristretto255;
|
|
type KeyExchange = TripleDh<crate::Curve25519, sha2::Sha512>;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
let ristretto_real_tvs = json_to_test_vectors!(
|
|
rfc,
|
|
"Real",
|
|
"ristretto255-SHA512, curve25519",
|
|
Ristretto255Sha512Curve25519NoKsf
|
|
);
|
|
|
|
let ristretto_fake_tvs = json_to_test_vectors!(
|
|
rfc,
|
|
"Fake",
|
|
"ristretto255-SHA512, curve25519",
|
|
Ristretto255Sha512Curve25519NoKsf
|
|
);
|
|
|
|
assert!(
|
|
!(ristretto_real_tvs.is_empty() || ristretto_fake_tvs.is_empty()),
|
|
"Parsing error"
|
|
);
|
|
|
|
// There should be 2 real test vectors and 1 fake test vector
|
|
assert_eq!(ristretto_real_tvs.len(), 2);
|
|
assert_eq!(ristretto_fake_tvs.len(), 1);
|
|
|
|
test_registration_request::<Ristretto255Sha512Curve25519NoKsf>(&ristretto_real_tvs)?;
|
|
test_registration_response::<Ristretto255Sha512Curve25519NoKsf>(&ristretto_real_tvs)?;
|
|
test_registration_upload::<Ristretto255Sha512Curve25519NoKsf>(&ristretto_real_tvs)?;
|
|
test_ke1::<Ristretto255Sha512Curve25519NoKsf>(&ristretto_real_tvs)?;
|
|
test_ke2::<Ristretto255Sha512Curve25519NoKsf>(&ristretto_real_tvs)?;
|
|
test_ke3::<Ristretto255Sha512Curve25519NoKsf>(&ristretto_real_tvs)?;
|
|
test_server_login_finish::<Ristretto255Sha512Curve25519NoKsf>(&ristretto_real_tvs)?;
|
|
test_fake_vectors::<Ristretto255Sha512Curve25519NoKsf>(&ristretto_fake_tvs)?;
|
|
}
|
|
|
|
struct P256Sha256NoKsf;
|
|
impl CipherSuite for P256Sha256NoKsf {
|
|
type OprfCs = p256::NistP256;
|
|
type KeyExchange = TripleDh<p256::NistP256, sha2::Sha256>;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
let p256_real_tvs = json_to_test_vectors!(
|
|
rfc,
|
|
"Real",
|
|
"P256-SHA256, P256_XMD:SHA-256_SSWU_RO_",
|
|
P256Sha256NoKsf
|
|
);
|
|
let p256_fake_tvs = json_to_test_vectors!(
|
|
rfc,
|
|
"Fake",
|
|
"P256-SHA256, P256_XMD:SHA-256_SSWU_RO_",
|
|
P256Sha256NoKsf
|
|
);
|
|
|
|
assert!(
|
|
!(p256_real_tvs.is_empty() || p256_fake_tvs.is_empty()),
|
|
"Parsing error"
|
|
);
|
|
|
|
// There should be 2 real test vectors and 1 fake test vector
|
|
assert_eq!(p256_real_tvs.len(), 2);
|
|
assert_eq!(p256_fake_tvs.len(), 1);
|
|
|
|
test_registration_request::<P256Sha256NoKsf>(&p256_real_tvs)?;
|
|
test_registration_response::<P256Sha256NoKsf>(&p256_real_tvs)?;
|
|
test_registration_upload::<P256Sha256NoKsf>(&p256_real_tvs)?;
|
|
test_ke1::<P256Sha256NoKsf>(&p256_real_tvs)?;
|
|
test_ke2::<P256Sha256NoKsf>(&p256_real_tvs)?;
|
|
test_ke3::<P256Sha256NoKsf>(&p256_real_tvs)?;
|
|
test_server_login_finish::<P256Sha256NoKsf>(&p256_real_tvs)?;
|
|
test_fake_vectors::<P256Sha256NoKsf>(&p256_fake_tvs)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn test_registration_request<CS: CipherSuite>(
|
|
tvs: &[OpaqueTestVectorParameters],
|
|
) -> Result<(), ProtocolError> {
|
|
for parameters in tvs {
|
|
let mut rng = CycleRng::new(parameters.blind_registration.to_vec());
|
|
let client_registration_start_result =
|
|
ClientRegistration::<CS>::start(&mut rng, ¶meters.password)?;
|
|
assert_eq!(
|
|
hex::encode(¶meters.registration_request),
|
|
hex::encode(client_registration_start_result.message.serialize())
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn test_registration_response<CS: CipherSuite>(
|
|
tvs: &[OpaqueTestVectorParameters],
|
|
) -> Result<(), ProtocolError>
|
|
where
|
|
// RegistrationResponse: KgPk + KePk
|
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<<KeGroup<CS> as Group>::PkLen>,
|
|
RegistrationResponseLen<CS>: ArrayLength,
|
|
{
|
|
for parameters in tvs {
|
|
let server_setup = ServerSetup::<CS>::deserialize(
|
|
&[
|
|
parameters.oprf_seed.as_slice(),
|
|
¶meters.server_private_key,
|
|
¶meters.dummy_public_key,
|
|
]
|
|
.concat(),
|
|
)?;
|
|
let server_registration_start_result = ServerRegistration::<CS>::start(
|
|
&server_setup,
|
|
RegistrationRequest::deserialize(¶meters.registration_request).unwrap(),
|
|
¶meters.credential_identifier,
|
|
)?;
|
|
assert_eq!(
|
|
hex::encode(¶meters.server_public_key),
|
|
hex::encode(server_setup.keypair().public().serialize()),
|
|
);
|
|
assert_eq!(
|
|
hex::encode(¶meters.oprf_key),
|
|
hex::encode(server_registration_start_result.oprf_key)
|
|
);
|
|
assert_eq!(
|
|
hex::encode(¶meters.registration_response),
|
|
hex::encode(server_registration_start_result.message.serialize())
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn test_registration_upload<CS: CipherSuite>(
|
|
tvs: &[OpaqueTestVectorParameters],
|
|
) -> Result<(), ProtocolError>
|
|
where
|
|
// RegistrationUpload: (KePk + Hash) + Envelope
|
|
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength + Add<EnvelopeLen<CS>>,
|
|
RegistrationUploadLen<CS>: ArrayLength,
|
|
{
|
|
for parameters in tvs {
|
|
let mut rng = CycleRng::new(parameters.blind_registration.to_vec());
|
|
let client_registration_start_result =
|
|
ClientRegistration::<CS>::start(&mut rng, ¶meters.password)?;
|
|
|
|
let mut finish_registration_rng = CycleRng::new(parameters.envelope_nonce.to_vec());
|
|
let result = client_registration_start_result.state.finish(
|
|
&mut finish_registration_rng,
|
|
¶meters.password,
|
|
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),
|
|
hex::encode(result.auth_key)
|
|
);
|
|
assert_eq!(
|
|
hex::encode(¶meters.randomized_pwd),
|
|
hex::encode(result.randomized_pwd)
|
|
);
|
|
assert_eq!(
|
|
hex::encode(¶meters.registration_upload),
|
|
hex::encode(result.message.serialize())
|
|
);
|
|
assert_eq!(
|
|
hex::encode(¶meters.export_key),
|
|
hex::encode(result.export_key)
|
|
);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn test_ke1<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError>
|
|
where
|
|
// CredentialRequest: KgPk + Ke1Message
|
|
<CS::KeyExchange as KeyExchange>::KE1Message: Serialize,
|
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
|
CredentialRequestLen<CS>: ArrayLength,
|
|
{
|
|
for parameters in tvs {
|
|
let client_login_start = [
|
|
parameters.blind_login.as_slice(),
|
|
¶meters.client_keyshare_seed,
|
|
¶meters.client_nonce,
|
|
]
|
|
.concat();
|
|
|
|
let mut client_login_start_rng = CycleRng::new(client_login_start);
|
|
let client_login_start_result =
|
|
ClientLogin::<CS>::start(&mut client_login_start_rng, ¶meters.password)?;
|
|
assert_eq!(
|
|
hex::encode(¶meters.KE1),
|
|
hex::encode(client_login_start_result.message.serialize())
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn test_ke2<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError>
|
|
where
|
|
<CS::KeyExchange as KeyExchange>::KE1Message: Deserialize,
|
|
// RegistrationUpload: (KePk + Hash) + Envelope
|
|
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength + Add<EnvelopeLen<CS>>,
|
|
RegistrationUploadLen<CS>: ArrayLength,
|
|
// ServerRegistration = RegistrationUpload
|
|
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
|
|
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
|
|
ArrayLength + Add<MaskedResponseLen<CS>>,
|
|
CredentialResponseWithoutKeLen<CS>: ArrayLength,
|
|
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
|
<CS::KeyExchange as KeyExchange>::KE2Message: Serialize,
|
|
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
|
CredentialResponseLen<CS>: ArrayLength,
|
|
{
|
|
for parameters in tvs {
|
|
let server_setup = ServerSetup::<CS>::deserialize(
|
|
&[
|
|
parameters.oprf_seed.as_slice(),
|
|
¶meters.server_private_key,
|
|
¶meters.dummy_public_key,
|
|
]
|
|
.concat(),
|
|
)?;
|
|
|
|
let record =
|
|
ServerRegistration::<CS>::deserialize(&get_password_file_bytes::<CS>(parameters))?;
|
|
|
|
let mut server_keyshare_seed_and_nonce_rng = CycleRng::new(
|
|
[
|
|
parameters.dummy_masking_key.as_slice(),
|
|
¶meters.masking_nonce,
|
|
¶meters.server_keyshare_seed,
|
|
¶meters.server_nonce,
|
|
]
|
|
.concat(),
|
|
);
|
|
let server_login_start_result = ServerLogin::<CS>::start(
|
|
&mut server_keyshare_seed_and_nonce_rng,
|
|
&server_setup,
|
|
Some(record),
|
|
CredentialRequest::<CS>::deserialize(¶meters.KE1).unwrap(),
|
|
¶meters.credential_identifier,
|
|
ServerLoginParameters {
|
|
context: Some(¶meters.context),
|
|
identifiers: Identifiers {
|
|
client: parameters.client_identity.as_deref(),
|
|
server: parameters.server_identity.as_deref(),
|
|
},
|
|
},
|
|
)?;
|
|
assert_eq!(
|
|
hex::encode(¶meters.handshake_secret),
|
|
hex::encode(server_login_start_result.handshake_secret)
|
|
);
|
|
assert_eq!(
|
|
hex::encode(¶meters.server_mac_key),
|
|
hex::encode(server_login_start_result.server_mac_key)
|
|
);
|
|
assert_eq!(
|
|
hex::encode(¶meters.oprf_key),
|
|
hex::encode(server_login_start_result.oprf_key)
|
|
);
|
|
assert_eq!(
|
|
hex::encode(¶meters.KE2),
|
|
hex::encode(server_login_start_result.message.serialize())
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn test_ke3<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError>
|
|
where
|
|
<CS::KeyExchange as KeyExchange>::KE2Message: Deserialize + Serialize,
|
|
<CS::KeyExchange as KeyExchange>::KE3Message: Serialize,
|
|
{
|
|
for parameters in tvs {
|
|
let client_login_start = [
|
|
parameters.blind_login.as_slice(),
|
|
¶meters.client_keyshare_seed,
|
|
¶meters.client_nonce,
|
|
]
|
|
.concat();
|
|
let mut client_login_start_rng = CycleRng::new(client_login_start);
|
|
let client_login_start_result =
|
|
ClientLogin::<CS>::start(&mut client_login_start_rng, ¶meters.password)?;
|
|
|
|
let client_login_finish_result = client_login_start_result.state.finish(
|
|
&mut UnwrapErr(SysRng),
|
|
¶meters.password,
|
|
CredentialResponse::<CS>::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!(
|
|
hex::encode(¶meters.session_key),
|
|
hex::encode(&client_login_finish_result.session_key)
|
|
);
|
|
assert_eq!(
|
|
hex::encode(¶meters.handshake_secret),
|
|
hex::encode(&client_login_finish_result.handshake_secret)
|
|
);
|
|
assert_eq!(
|
|
hex::encode(¶meters.client_mac_key),
|
|
hex::encode(&client_login_finish_result.client_mac_key)
|
|
);
|
|
assert_eq!(
|
|
hex::encode(¶meters.KE3),
|
|
hex::encode(client_login_finish_result.message.serialize())
|
|
);
|
|
assert_eq!(
|
|
hex::encode(¶meters.export_key),
|
|
hex::encode(client_login_finish_result.export_key)
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn test_server_login_finish<CS: CipherSuite>(
|
|
tvs: &[OpaqueTestVectorParameters],
|
|
) -> Result<(), ProtocolError>
|
|
where
|
|
<CS::KeyExchange as KeyExchange>::KE1Message: Deserialize,
|
|
<CS::KeyExchange as KeyExchange>::KE3Message: Deserialize,
|
|
// RegistrationUpload: (KePk + Hash) + Envelope
|
|
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength + Add<EnvelopeLen<CS>>,
|
|
RegistrationUploadLen<CS>: ArrayLength,
|
|
// ServerRegistration = RegistrationUpload
|
|
{
|
|
for parameters in tvs {
|
|
let server_setup = ServerSetup::<CS>::deserialize(
|
|
&[
|
|
parameters.oprf_seed.as_slice(),
|
|
¶meters.server_private_key,
|
|
¶meters.dummy_public_key,
|
|
]
|
|
.concat(),
|
|
)?;
|
|
|
|
let record =
|
|
ServerRegistration::<CS>::deserialize(&get_password_file_bytes::<CS>(parameters))?;
|
|
|
|
let mut server_keyshare_seed_and_nonce_rng = CycleRng::new(
|
|
[
|
|
parameters.dummy_masking_key.as_slice(),
|
|
¶meters.masking_nonce,
|
|
¶meters.server_keyshare_seed,
|
|
¶meters.server_nonce,
|
|
]
|
|
.concat(),
|
|
);
|
|
let server_login_start_result = ServerLogin::<CS>::start(
|
|
&mut server_keyshare_seed_and_nonce_rng,
|
|
&server_setup,
|
|
Some(record),
|
|
CredentialRequest::<CS>::deserialize(¶meters.KE1).unwrap(),
|
|
¶meters.credential_identifier,
|
|
ServerLoginParameters {
|
|
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)?,
|
|
ServerLoginParameters {
|
|
context: Some(¶meters.context),
|
|
identifiers: Identifiers {
|
|
client: parameters.client_identity.as_deref(),
|
|
server: parameters.server_identity.as_deref(),
|
|
},
|
|
},
|
|
)?;
|
|
|
|
assert_eq!(
|
|
hex::encode(¶meters.session_key),
|
|
hex::encode(&server_login_result.session_key)
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn test_fake_vectors<CS: CipherSuite>(
|
|
tvs: &[OpaqueTestVectorParameters],
|
|
) -> Result<(), ProtocolError>
|
|
where
|
|
<CS::KeyExchange as KeyExchange>::KE1Message: Deserialize,
|
|
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
|
|
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
|
|
ArrayLength + Add<MaskedResponseLen<CS>>,
|
|
CredentialResponseWithoutKeLen<CS>: ArrayLength,
|
|
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
|
<CS::KeyExchange as KeyExchange>::KE2Message: Serialize,
|
|
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
|
CredentialResponseLen<CS>: ArrayLength,
|
|
{
|
|
for parameters in tvs {
|
|
let server_setup = ServerSetup::<CS>::deserialize(
|
|
&[
|
|
parameters.oprf_seed.as_slice(),
|
|
¶meters.server_private_key,
|
|
¶meters.dummy_public_key,
|
|
]
|
|
.concat(),
|
|
)?;
|
|
|
|
let mut server_keyshare_seed_and_nonce_rng = CycleRng::new(
|
|
[
|
|
parameters.dummy_masking_key.as_slice(),
|
|
¶meters.masking_nonce,
|
|
¶meters.server_keyshare_seed,
|
|
¶meters.server_nonce,
|
|
]
|
|
.concat(),
|
|
);
|
|
let server_login_start_result = ServerLogin::<CS>::start(
|
|
&mut server_keyshare_seed_and_nonce_rng,
|
|
&server_setup,
|
|
None,
|
|
CredentialRequest::<CS>::deserialize(¶meters.KE1).unwrap(),
|
|
¶meters.credential_identifier,
|
|
ServerLoginParameters {
|
|
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())
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|