* Rework `SecretKey` API to facilitate async * Remove left-over constraints
1880 lines
66 KiB
Rust
1880 lines
66 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.
|
|
|
|
#![allow(unsafe_code)]
|
|
|
|
use core::ops::Add;
|
|
use std::string::String;
|
|
use std::vec::Vec;
|
|
use std::{format, println, ptr, vec};
|
|
|
|
use digest::Output;
|
|
use generic_array::typenum::{Sum, Unsigned};
|
|
use generic_array::ArrayLength;
|
|
use rand::rngs::OsRng;
|
|
use serde_json::Value;
|
|
use subtle::ConstantTimeEq;
|
|
use voprf::Group;
|
|
|
|
use crate::ciphersuite::{CipherSuite, OprfGroup, OprfHash};
|
|
use crate::envelope::EnvelopeLen;
|
|
use crate::errors::*;
|
|
use crate::hash::OutputSize;
|
|
use crate::key_exchange::group::KeGroup;
|
|
use crate::key_exchange::traits::{Ke1MessageLen, Ke1StateLen, Ke2MessageLen};
|
|
use crate::key_exchange::tripledh::{DiffieHellman, NonceLen, TripleDh};
|
|
use crate::ksf::Identity;
|
|
use crate::messages::{
|
|
CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen,
|
|
RegistrationResponseLen, RegistrationUploadLen,
|
|
};
|
|
use crate::opaque::*;
|
|
use crate::tests::mock_rng::CycleRng;
|
|
use crate::*;
|
|
|
|
// Tests
|
|
// =====
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
struct Ristretto255;
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
impl CipherSuite for Ristretto255 {
|
|
type OprfCs = crate::Ristretto255;
|
|
type KeGroup = crate::Ristretto255;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
struct Ristretto255P256;
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
impl CipherSuite for Ristretto255P256 {
|
|
type OprfCs = p256::NistP256;
|
|
type KeGroup = crate::Ristretto255;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
struct Ristretto255P384;
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
impl CipherSuite for Ristretto255P384 {
|
|
type OprfCs = p384::NistP384;
|
|
type KeGroup = crate::Ristretto255;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
struct Ristretto255P521;
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
impl CipherSuite for Ristretto255P521 {
|
|
type OprfCs = p521::NistP521;
|
|
type KeGroup = crate::Ristretto255;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
struct P256;
|
|
|
|
impl CipherSuite for P256 {
|
|
type OprfCs = p256::NistP256;
|
|
type KeGroup = p256::NistP256;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
struct P256P384;
|
|
|
|
impl CipherSuite for P256P384 {
|
|
type OprfCs = p384::NistP384;
|
|
type KeGroup = p256::NistP256;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
struct P256P521;
|
|
|
|
impl CipherSuite for P256P521 {
|
|
type OprfCs = p521::NistP521;
|
|
type KeGroup = p256::NistP256;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
struct P256Ristretto255;
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
impl CipherSuite for P256Ristretto255 {
|
|
type OprfCs = crate::Ristretto255;
|
|
type KeGroup = p256::NistP256;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
struct P384;
|
|
|
|
impl CipherSuite for P384 {
|
|
type OprfCs = p384::NistP384;
|
|
type KeGroup = p384::NistP384;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
struct P384P256;
|
|
|
|
impl CipherSuite for P384P256 {
|
|
type OprfCs = p256::NistP256;
|
|
type KeGroup = p384::NistP384;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
struct P384P521;
|
|
|
|
impl CipherSuite for P384P521 {
|
|
type OprfCs = p521::NistP521;
|
|
type KeGroup = p384::NistP384;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
struct P384Ristretto255;
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
impl CipherSuite for P384Ristretto255 {
|
|
type OprfCs = crate::Ristretto255;
|
|
type KeGroup = p384::NistP384;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
struct P521;
|
|
|
|
impl CipherSuite for P521 {
|
|
type OprfCs = p521::NistP521;
|
|
type KeGroup = p521::NistP521;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
struct P521P256;
|
|
|
|
impl CipherSuite for P521P256 {
|
|
type OprfCs = p256::NistP256;
|
|
type KeGroup = p521::NistP521;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
struct P521P384;
|
|
|
|
impl CipherSuite for P521P384 {
|
|
type OprfCs = p384::NistP384;
|
|
type KeGroup = p521::NistP521;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
struct P521Ristretto255;
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
impl CipherSuite for P521Ristretto255 {
|
|
type OprfCs = crate::Ristretto255;
|
|
type KeGroup = p521::NistP521;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
#[cfg(all(feature = "curve25519", feature = "ristretto255"))]
|
|
struct Curve25519Ristretto255;
|
|
|
|
#[cfg(all(feature = "curve25519", feature = "ristretto255"))]
|
|
impl CipherSuite for Curve25519Ristretto255 {
|
|
type OprfCs = crate::Ristretto255;
|
|
type KeGroup = crate::Curve25519;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
#[cfg(feature = "curve25519")]
|
|
struct Curve25519P256;
|
|
|
|
#[cfg(feature = "curve25519")]
|
|
impl CipherSuite for Curve25519P256 {
|
|
type OprfCs = p256::NistP256;
|
|
type KeGroup = crate::Curve25519;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
#[cfg(feature = "curve25519")]
|
|
struct Curve25519P384;
|
|
|
|
#[cfg(feature = "curve25519")]
|
|
impl CipherSuite for Curve25519P384 {
|
|
type OprfCs = p384::NistP384;
|
|
type KeGroup = crate::Curve25519;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
#[cfg(feature = "curve25519")]
|
|
struct Curve25519P521;
|
|
|
|
#[cfg(feature = "curve25519")]
|
|
impl CipherSuite for Curve25519P521 {
|
|
type OprfCs = p521::NistP521;
|
|
type KeGroup = crate::Curve25519;
|
|
type KeyExchange = TripleDh;
|
|
type Ksf = Identity;
|
|
}
|
|
|
|
pub struct TestVectorParameters {
|
|
pub client_s_pk: Vec<u8>,
|
|
pub client_s_sk: Vec<u8>,
|
|
pub client_e_pk: Vec<u8>,
|
|
pub client_e_sk: Vec<u8>,
|
|
pub server_s_pk: Vec<u8>,
|
|
pub server_s_sk: Vec<u8>,
|
|
pub server_e_pk: Vec<u8>,
|
|
pub server_e_sk: Vec<u8>,
|
|
pub fake_sk: Vec<u8>,
|
|
pub credential_identifier: Vec<u8>,
|
|
pub id_u: Vec<u8>,
|
|
pub id_s: Vec<u8>,
|
|
pub password: Vec<u8>,
|
|
pub blinding_factor: 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 context: Vec<u8>,
|
|
pub registration_request: Vec<u8>,
|
|
pub registration_response: Vec<u8>,
|
|
pub registration_upload: Vec<u8>,
|
|
pub credential_request: Vec<u8>,
|
|
pub credential_response: Vec<u8>,
|
|
pub credential_finalization: Vec<u8>,
|
|
client_registration_state: Vec<u8>,
|
|
client_login_state: Vec<u8>,
|
|
server_login_state: Vec<u8>,
|
|
pub password_file: Vec<u8>,
|
|
pub export_key: Vec<u8>,
|
|
pub session_key: Vec<u8>,
|
|
}
|
|
|
|
static STR_PASSWORD: &str = "password";
|
|
static STR_CREDENTIAL_IDENTIFIER: &str = "credential_identifier";
|
|
|
|
macro_rules! run_all {
|
|
($name:ident $(, $par:expr)*) => {
|
|
use super::full_test_vectors;
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
$name::<Ristretto255>(full_test_vectors::TEST_VECTOR_RISTRETTO255 $(, $par)*)?;
|
|
#[cfg(feature = "ristretto255")]
|
|
$name::<Ristretto255P256>(full_test_vectors::TEST_VECTOR_RISTRETTO255_P256 $(, $par)*)?;
|
|
#[cfg(feature = "ristretto255")]
|
|
$name::<Ristretto255P384>(full_test_vectors::TEST_VECTOR_RISTRETTO255_P384 $(, $par)*)?;
|
|
#[cfg(feature = "ristretto255")]
|
|
$name::<Ristretto255P521>(full_test_vectors::TEST_VECTOR_RISTRETTO255_P521 $(, $par)*)?;
|
|
$name::<P256>(full_test_vectors::TEST_VECTOR_P256 $(, $par)*)?;
|
|
$name::<P256P384>(full_test_vectors::TEST_VECTOR_P256_P384 $(, $par)*)?;
|
|
$name::<P256P521>(full_test_vectors::TEST_VECTOR_P256_P521 $(, $par)*)?;
|
|
#[cfg(feature = "ristretto255")]
|
|
$name::<P256Ristretto255>(full_test_vectors::TEST_VECTOR_P256_RISTRETTO255 $(, $par)*)?;
|
|
$name::<P384>(full_test_vectors::TEST_VECTOR_P384 $(, $par)*)?;
|
|
$name::<P384P256>(full_test_vectors::TEST_VECTOR_P384_P256 $(, $par)*)?;
|
|
$name::<P384P521>(full_test_vectors::TEST_VECTOR_P384_P521 $(, $par)*)?;
|
|
#[cfg(feature = "ristretto255")]
|
|
$name::<P384Ristretto255>(full_test_vectors::TEST_VECTOR_P384_RISTRETTO255 $(, $par)*)?;
|
|
$name::<P521>(full_test_vectors::TEST_VECTOR_P521 $(, $par)*)?;
|
|
$name::<P521P256>(full_test_vectors::TEST_VECTOR_P521_P256 $(, $par)*)?;
|
|
$name::<P521P384>(full_test_vectors::TEST_VECTOR_P521_P384 $(, $par)*)?;
|
|
#[cfg(feature = "ristretto255")]
|
|
$name::<P521Ristretto255>(full_test_vectors::TEST_VECTOR_P521_RISTRETTO255 $(, $par)*)?;
|
|
#[cfg(feature = "curve25519")]
|
|
$name::<Curve25519P256>(full_test_vectors::TEST_VECTOR_CURVE25519_P256 $(, $par)*)?;
|
|
#[cfg(feature = "curve25519")]
|
|
$name::<Curve25519P384>(full_test_vectors::TEST_VECTOR_CURVE25519_P384 $(, $par)*)?;
|
|
#[cfg(feature = "curve25519")]
|
|
$name::<Curve25519P521>(full_test_vectors::TEST_VECTOR_CURVE25519_P521 $(, $par)*)?;
|
|
#[cfg(all(feature = "curve25519", feature = "ristretto255"))]
|
|
$name::<Curve25519Ristretto255>(full_test_vectors::TEST_VECTOR_CURVE25519_RISTRETTO255 $(, $par)*)?;
|
|
};
|
|
}
|
|
|
|
fn decode(values: &Value, key: &str) -> Option<Vec<u8>> {
|
|
values[key].as_str().and_then(|s| hex::decode(s).ok())
|
|
}
|
|
|
|
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(),
|
|
}
|
|
}
|
|
|
|
fn stringify_test_vectors(p: &TestVectorParameters) -> String {
|
|
let mut s = String::new();
|
|
s.push_str("{\n");
|
|
s.push_str(
|
|
format!(
|
|
" \"client_s_pk\": \"{}\",\n",
|
|
hex::encode(&p.client_s_pk)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"client_s_sk\": \"{}\",\n",
|
|
hex::encode(&p.client_s_sk)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"client_e_pk\": \"{}\",\n",
|
|
hex::encode(&p.client_e_pk)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"client_e_sk\": \"{}\",\n",
|
|
hex::encode(&p.client_e_sk)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"server_s_pk\": \"{}\",\n",
|
|
hex::encode(&p.server_s_pk)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"server_s_sk\": \"{}\",\n",
|
|
hex::encode(&p.server_s_sk)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"server_e_pk\": \"{}\",\n",
|
|
hex::encode(&p.server_e_pk)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"server_e_sk\": \"{}\",\n",
|
|
hex::encode(&p.server_e_sk)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(format!(" \"fake_sk\": \"{}\",\n", hex::encode(&p.fake_sk)).as_str());
|
|
s.push_str(
|
|
format!(
|
|
" \"credential_identifier\": \"{}\",\n",
|
|
hex::encode(&p.credential_identifier)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(format!(" \"id_u\": \"{}\",\n", hex::encode(&p.id_u)).as_str());
|
|
s.push_str(format!(" \"id_s\": \"{}\",\n", hex::encode(&p.id_s)).as_str());
|
|
s.push_str(format!(" \"password\": \"{}\",\n", hex::encode(&p.password)).as_str());
|
|
s.push_str(
|
|
format!(
|
|
" \"blinding_factor\": \"{}\",\n",
|
|
hex::encode(&p.blinding_factor)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(format!(" \"oprf_seed\": \"{}\",\n", hex::encode(&p.oprf_seed)).as_str());
|
|
s.push_str(
|
|
format!(
|
|
" \"masking_nonce\": \"{}\",\n",
|
|
hex::encode(&p.masking_nonce)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"envelope_nonce\": \"{}\",\n",
|
|
hex::encode(&p.envelope_nonce)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"client_nonce\": \"{}\",\n",
|
|
hex::encode(&p.client_nonce)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"server_nonce\": \"{}\",\n",
|
|
hex::encode(&p.server_nonce)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(format!(" \"context\": \"{}\",\n", hex::encode(&p.context)).as_str());
|
|
s.push_str(
|
|
format!(
|
|
" \"registration_request\": \"{}\",\n",
|
|
hex::encode(&p.registration_request)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"registration_response\": \"{}\",\n",
|
|
hex::encode(&p.registration_response)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"registration_upload\": \"{}\",\n",
|
|
hex::encode(&p.registration_upload)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"credential_request\": \"{}\",\n",
|
|
hex::encode(&p.credential_request)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"credential_response\": \"{}\",\n",
|
|
hex::encode(&p.credential_response)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"credential_finalization\": \"{}\",\n",
|
|
hex::encode(&p.credential_finalization)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"client_registration_state\": \"{}\",\n",
|
|
hex::encode(&p.client_registration_state)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"client_login_state\": \"{}\",\n",
|
|
hex::encode(&p.client_login_state)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"server_login_state\": \"{}\",\n",
|
|
hex::encode(&p.server_login_state)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(
|
|
format!(
|
|
" \"password_file\": \"{}\",\n",
|
|
hex::encode(&p.password_file)
|
|
)
|
|
.as_str(),
|
|
);
|
|
s.push_str(format!(" \"export_key\": \"{}\",\n", hex::encode(&p.export_key)).as_str());
|
|
s.push_str(format!(" \"session_key\": \"{}\"\n", hex::encode(&p.session_key)).as_str());
|
|
s.push_str("}\n");
|
|
s
|
|
}
|
|
|
|
fn generate_parameters<CS: CipherSuite>() -> Result<TestVectorParameters, ProtocolError>
|
|
where
|
|
// ClientRegistration: KgSk + KgPk
|
|
<OprfGroup<CS> as Group>::ScalarLen: Add<<OprfGroup<CS> as Group>::ElemLen>,
|
|
ClientRegistrationLen<CS>: ArrayLength<u8>,
|
|
// RegistrationResponse: KgPk + KePk
|
|
<OprfGroup<CS> as Group>::ElemLen: Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
RegistrationResponseLen<CS>: ArrayLength<u8>,
|
|
// Envelope: Nonce + Hash
|
|
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
EnvelopeLen<CS>: ArrayLength<u8>,
|
|
// RegistrationUpload: (KePk + Hash) + Envelope
|
|
<CS::KeGroup as KeGroup>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<<CS::KeGroup as KeGroup>::PkLen, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
|
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
|
// ServerRegistration = RegistrationUpload
|
|
// Ke1Message: Nonce + KePk
|
|
NonceLen: Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
Ke1MessageLen<CS>: ArrayLength<u8>,
|
|
// CredentialRequest: KgPk + Ke1Message
|
|
<OprfGroup<CS> as Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
|
CredentialRequestLen<CS>: ArrayLength<u8>,
|
|
// ClientLogin: KgSk + CredentialRequest + Ke1State
|
|
<OprfGroup<CS> as Group>::ScalarLen: Add<CredentialRequestLen<CS>>,
|
|
Sum<<OprfGroup<CS> as Group>::ScalarLen, CredentialRequestLen<CS>>:
|
|
ArrayLength<u8> + Add<Ke1StateLen<CS>>,
|
|
ClientLoginLen<CS>: ArrayLength<u8>,
|
|
// MaskedResponse: (Nonce + Hash) + KePk
|
|
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<NonceLen, OutputSize<OprfHash<CS>>>: ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
|
<OprfGroup<CS> as Group>::ElemLen: Add<NonceLen>,
|
|
Sum<<OprfGroup<CS> as Group>::ElemLen, NonceLen>: ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
|
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
|
|
// Ke2Message: (Nonce + KePk) + Hash
|
|
NonceLen: Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
Sum<NonceLen, <CS::KeGroup as KeGroup>::PkLen>: ArrayLength<u8> + Add<OutputSize<OprfHash<CS>>>,
|
|
Ke2MessageLen<CS>: ArrayLength<u8>,
|
|
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
|
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
|
CredentialResponseLen<CS>: ArrayLength<u8>,
|
|
{
|
|
use rand::RngCore;
|
|
|
|
use crate::keypair::KeyPair;
|
|
|
|
let mut rng = OsRng;
|
|
|
|
// Inputs
|
|
let server_s_kp = KeyPair::<CS::KeGroup>::generate_random::<CS::OprfCs, _>(&mut rng);
|
|
let server_e_kp = KeyPair::<CS::KeGroup>::generate_random::<CS::OprfCs, _>(&mut rng);
|
|
let client_s_kp = KeyPair::<CS::KeGroup>::generate_random::<CS::OprfCs, _>(&mut rng);
|
|
let client_e_kp = KeyPair::<CS::KeGroup>::generate_random::<CS::OprfCs, _>(&mut rng);
|
|
let fake_kp = KeyPair::<CS::KeGroup>::generate_random::<CS::OprfCs, _>(&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 = Output::<OprfHash<CS>>::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 = [0u8; NonceLen::USIZE];
|
|
rng.fill_bytes(&mut client_nonce);
|
|
let mut server_nonce = [0u8; NonceLen::USIZE];
|
|
rng.fill_bytes(&mut server_nonce);
|
|
|
|
let fake_sk: Vec<u8> = fake_kp.private().serialize().to_vec();
|
|
let server_setup = ServerSetup::<CS>::deserialize(
|
|
&[
|
|
oprf_seed.as_ref(),
|
|
&server_s_kp.private().serialize(),
|
|
&fake_sk,
|
|
]
|
|
.concat(),
|
|
)
|
|
.unwrap();
|
|
|
|
let blinding_factor = <OprfGroup<CS> as Group>::random_scalar(&mut rng);
|
|
let blinding_factor_bytes = OprfGroup::<CS>::serialize_scalar(blinding_factor);
|
|
|
|
let mut blinding_factor_registration_rng = CycleRng::new(blinding_factor_bytes.to_vec());
|
|
let client_registration_start_result =
|
|
ClientRegistration::<CS>::start(&mut blinding_factor_registration_rng, password).unwrap();
|
|
let blinding_factor_bytes_returned = OprfGroup::<CS>::serialize_scalar(
|
|
client_registration_start_result
|
|
.state
|
|
.oprf_client
|
|
.get_blind(),
|
|
);
|
|
assert_eq!(
|
|
hex::encode(&blinding_factor_bytes),
|
|
hex::encode(&blinding_factor_bytes_returned)
|
|
);
|
|
|
|
let registration_request_bytes = client_registration_start_result.message.serialize();
|
|
let client_registration_state = client_registration_start_result.state.serialize();
|
|
|
|
let server_registration_start_result = ServerRegistration::<CS>::start(
|
|
&server_setup,
|
|
client_registration_start_result.message,
|
|
credential_identifier,
|
|
)
|
|
.unwrap();
|
|
let registration_response_bytes = server_registration_start_result.message.serialize();
|
|
|
|
let mut client_s_sk_and_nonce: Vec<u8> = Vec::new();
|
|
client_s_sk_and_nonce.extend_from_slice(&client_s_kp.private().serialize());
|
|
client_s_sk_and_nonce.extend_from_slice(&envelope_nonce);
|
|
|
|
let mut finish_registration_rng = CycleRng::new(client_s_sk_and_nonce);
|
|
let client_registration_finish_result = client_registration_start_result
|
|
.state
|
|
.finish(
|
|
&mut finish_registration_rng,
|
|
password,
|
|
server_registration_start_result.message,
|
|
ClientRegistrationFinishParameters::new(
|
|
Identifiers {
|
|
client: Some(id_u),
|
|
server: Some(id_s),
|
|
},
|
|
None,
|
|
),
|
|
)
|
|
.unwrap();
|
|
let registration_upload_bytes = client_registration_finish_result.message.serialize();
|
|
|
|
let password_file = ServerRegistration::finish(client_registration_finish_result.message);
|
|
let password_file_bytes = password_file.serialize();
|
|
|
|
let mut client_login_start: Vec<u8> = Vec::new();
|
|
client_login_start.extend_from_slice(&blinding_factor_bytes);
|
|
client_login_start.extend_from_slice(&client_e_kp.private().serialize());
|
|
client_login_start.extend_from_slice(&client_nonce);
|
|
|
|
let mut client_login_start_rng = CycleRng::new(client_login_start);
|
|
let client_login_start_result =
|
|
ClientLogin::<CS>::start(&mut client_login_start_rng, password).unwrap();
|
|
let credential_request_bytes = client_login_start_result.message.serialize();
|
|
let client_login_state = client_login_start_result.state.serialize().to_vec();
|
|
|
|
let mut server_e_sk_and_nonce_rng = CycleRng::new(
|
|
[
|
|
masking_nonce.to_vec(),
|
|
server_e_kp.private().serialize().to_vec(),
|
|
server_nonce.to_vec(),
|
|
]
|
|
.concat(),
|
|
);
|
|
let server_login_start_result = ServerLogin::<CS>::start(
|
|
&mut server_e_sk_and_nonce_rng,
|
|
&server_setup,
|
|
Some(password_file),
|
|
client_login_start_result.message,
|
|
credential_identifier,
|
|
ServerLoginStartParameters {
|
|
context: Some(context),
|
|
identifiers: Identifiers {
|
|
client: Some(id_u),
|
|
server: Some(id_s),
|
|
},
|
|
},
|
|
)
|
|
.unwrap();
|
|
let credential_response_bytes = server_login_start_result.message.serialize();
|
|
let server_login_state = server_login_start_result.state.serialize();
|
|
|
|
let client_login_finish_result = client_login_start_result
|
|
.state
|
|
.finish(
|
|
password,
|
|
server_login_start_result.message,
|
|
ClientLoginFinishParameters::new(
|
|
Some(context),
|
|
Identifiers {
|
|
client: Some(id_u),
|
|
server: Some(id_s),
|
|
},
|
|
None,
|
|
),
|
|
)
|
|
.unwrap();
|
|
let credential_finalization_bytes = client_login_finish_result.message.serialize();
|
|
|
|
Ok(TestVectorParameters {
|
|
client_s_pk: client_s_kp.public().serialize().to_vec(),
|
|
client_s_sk: client_s_kp.private().serialize().to_vec(),
|
|
client_e_pk: client_e_kp.public().serialize().to_vec(),
|
|
client_e_sk: client_e_kp.private().serialize().to_vec(),
|
|
server_s_pk: server_s_kp.public().serialize().to_vec(),
|
|
server_s_sk: server_s_kp.private().serialize().to_vec(),
|
|
server_e_pk: server_e_kp.public().serialize().to_vec(),
|
|
server_e_sk: server_e_kp.private().serialize().to_vec(),
|
|
fake_sk,
|
|
credential_identifier: credential_identifier.to_vec(),
|
|
id_u: id_u.to_vec(),
|
|
id_s: id_s.to_vec(),
|
|
password: password.to_vec(),
|
|
blinding_factor: blinding_factor_bytes.to_vec(),
|
|
oprf_seed: oprf_seed.to_vec(),
|
|
masking_nonce: masking_nonce.to_vec(),
|
|
envelope_nonce: envelope_nonce.to_vec(),
|
|
client_nonce: client_nonce.to_vec(),
|
|
server_nonce: server_nonce.to_vec(),
|
|
context: context.to_vec(),
|
|
registration_request: registration_request_bytes.to_vec(),
|
|
registration_response: registration_response_bytes.to_vec(),
|
|
registration_upload: registration_upload_bytes.to_vec(),
|
|
credential_request: credential_request_bytes.to_vec(),
|
|
credential_response: credential_response_bytes.to_vec(),
|
|
credential_finalization: credential_finalization_bytes.to_vec(),
|
|
password_file: password_file_bytes.to_vec(),
|
|
client_registration_state: client_registration_state.to_vec(),
|
|
client_login_state,
|
|
server_login_state: server_login_state.to_vec(),
|
|
session_key: client_login_finish_result.session_key.to_vec(),
|
|
export_key: client_registration_finish_result.export_key.to_vec(),
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn generate_test_vectors() -> Result<(), ProtocolError> {
|
|
let mut output = String::new();
|
|
|
|
#[rustfmt::skip]
|
|
output.push_str(
|
|
"\
|
|
// Copyright (c) Meta Platforms, Inc. and affiliates.\n\
|
|
//\n\
|
|
// This source code is dual-licensed under either the MIT license found in the\n\
|
|
// LICENSE-MIT file in the root directory of this source tree or the Apache\n\
|
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory\n\
|
|
// of this source tree. You may select, at your option, one of the above-listed\n\
|
|
// licenses.\n\
|
|
//\n\
|
|
// To regenerate these test vectors, run:\n\
|
|
// FULL_TEST_VECTORS_FILE=src/tests/full_test_vectors.rs cargo test --features ristretto255,curve25519 -- generate_test_vectors\n\
|
|
\n",
|
|
);
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
{
|
|
let parameters = generate_parameters::<Ristretto255>()?;
|
|
output.push_str("#[cfg(feature = \"ristretto255\")]\n");
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_RISTRETTO255: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
|
|
let parameters = generate_parameters::<Ristretto255P256>()?;
|
|
output.push_str("#[cfg(feature = \"ristretto255\")]\n");
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_RISTRETTO255_P256: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
|
|
let parameters = generate_parameters::<Ristretto255P384>()?;
|
|
output.push_str("#[cfg(feature = \"ristretto255\")]\n");
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_RISTRETTO255_P384: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
|
|
let parameters = generate_parameters::<Ristretto255P521>()?;
|
|
output.push_str("#[cfg(feature = \"ristretto255\")]\n");
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_RISTRETTO255_P521: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
}
|
|
|
|
let parameters = generate_parameters::<P256>()?;
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_P256: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
|
|
let parameters = generate_parameters::<P256P384>()?;
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_P256_P384: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
|
|
let parameters = generate_parameters::<P256P521>()?;
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_P256_P521: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
{
|
|
let parameters = generate_parameters::<P256Ristretto255>()?;
|
|
output.push_str("#[cfg(feature = \"ristretto255\")]\n");
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_P256_RISTRETTO255: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
}
|
|
|
|
let parameters = generate_parameters::<P384>()?;
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_P384: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
|
|
let parameters = generate_parameters::<P384P256>()?;
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_P384_P256: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
|
|
let parameters = generate_parameters::<P384P521>()?;
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_P384_P521: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
{
|
|
let parameters = generate_parameters::<P384Ristretto255>()?;
|
|
output.push_str("#[cfg(feature = \"ristretto255\")]\n");
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_P384_RISTRETTO255: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
}
|
|
|
|
let parameters = generate_parameters::<P521>()?;
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_P521: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
|
|
let parameters = generate_parameters::<P521P256>()?;
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_P521_P256: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
|
|
let parameters = generate_parameters::<P521P384>()?;
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_P521_P384: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
{
|
|
let parameters = generate_parameters::<P521Ristretto255>()?;
|
|
output.push_str("#[cfg(feature = \"ristretto255\")]\n");
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_P521_RISTRETTO255: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
}
|
|
|
|
#[cfg(feature = "curve25519")]
|
|
{
|
|
let parameters = generate_parameters::<Curve25519P256>()?;
|
|
output.push_str("#[cfg(feature = \"curve25519\")]\n");
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_CURVE25519_P256: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
|
|
let parameters = generate_parameters::<Curve25519P384>()?;
|
|
output.push_str("#[cfg(feature = \"curve25519\")]\n");
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_CURVE25519_P384: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
|
|
let parameters = generate_parameters::<Curve25519P521>()?;
|
|
output.push_str("#[cfg(feature = \"curve25519\")]\n");
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_CURVE25519_P521: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
|
|
#[cfg(feature = "ristretto255")]
|
|
{
|
|
let parameters = generate_parameters::<Curve25519Ristretto255>()?;
|
|
output.push_str("#[cfg(all(feature = \"curve25519\", feature = \"ristretto255\"))]\n");
|
|
output.push_str(&format!(
|
|
"pub static TEST_VECTOR_CURVE25519_RISTRETTO255: &str = r#\"\n{}\"#;\n",
|
|
stringify_test_vectors(¶meters)
|
|
));
|
|
}
|
|
}
|
|
|
|
if let Ok(path) = std::env::var("FULL_TEST_VECTORS_FILE") {
|
|
std::fs::write(path, output).unwrap();
|
|
} else {
|
|
println!("{output}");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_registration_request() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite>(test_vector: &str) -> Result<(), ProtocolError>
|
|
where
|
|
// ClientRegistration: KgSk + KgPk
|
|
<OprfGroup<CS> as Group>::ScalarLen: Add<<OprfGroup<CS> as Group>::ElemLen>,
|
|
ClientRegistrationLen<CS>: ArrayLength<u8>,
|
|
{
|
|
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::<CS>::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(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "serde")]
|
|
#[test]
|
|
fn test_serialization() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite>(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::<CS>::start(&mut rng, ¶meters.password)?;
|
|
|
|
// Test the bincode serialization (binary).
|
|
let registration_request =
|
|
bincode::serialize(&client_registration_start_result.message).unwrap();
|
|
assert_eq!(
|
|
registration_request.len(),
|
|
RegistrationRequestLen::<CS>::USIZE
|
|
);
|
|
let registration_request: RegistrationRequest<CS> =
|
|
bincode::deserialize(®istration_request).unwrap();
|
|
assert_eq!(
|
|
hex::encode(client_registration_start_result.message.serialize()),
|
|
hex::encode(registration_request.serialize()),
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_registration_response() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite>(test_vector: &str) -> Result<(), ProtocolError>
|
|
where
|
|
// RegistrationResponse: KgPk + KePk
|
|
<OprfGroup<CS> as Group>::ElemLen: Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
RegistrationResponseLen<CS>: ArrayLength<u8>,
|
|
{
|
|
let parameters = populate_test_vectors(
|
|
&serde_json::from_str(test_vector).map_err(|_| ProtocolError::SerializationError)?,
|
|
);
|
|
|
|
let server_setup = ServerSetup::<CS>::deserialize(
|
|
&[
|
|
parameters.oprf_seed,
|
|
parameters.server_s_sk,
|
|
parameters.fake_sk,
|
|
]
|
|
.concat(),
|
|
)?;
|
|
|
|
let server_registration_start_result = ServerRegistration::<CS>::start(
|
|
&server_setup,
|
|
RegistrationRequest::deserialize(¶meters.registration_request)?,
|
|
¶meters.credential_identifier,
|
|
)?;
|
|
assert_eq!(
|
|
hex::encode(parameters.registration_response),
|
|
hex::encode(server_registration_start_result.message.serialize())
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_registration_upload() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite>(test_vector: &str) -> Result<(), ProtocolError>
|
|
where
|
|
// Envelope: Nonce + Hash
|
|
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
EnvelopeLen<CS>: ArrayLength<u8>,
|
|
// RegistrationUpload: (KePk + Hash) + Envelope
|
|
<CS::KeGroup as KeGroup>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<<CS::KeGroup as KeGroup>::PkLen, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
|
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
|
{
|
|
let parameters = populate_test_vectors(
|
|
&serde_json::from_str(test_vector).map_err(|_| ProtocolError::SerializationError)?,
|
|
);
|
|
|
|
let client_s_sk_and_nonce: Vec<u8> =
|
|
[parameters.client_s_sk, parameters.envelope_nonce].concat();
|
|
let mut finish_registration_rng = CycleRng::new(client_s_sk_and_nonce);
|
|
let result = ClientRegistration::<CS>::deserialize(¶meters.client_registration_state)?
|
|
.finish(
|
|
&mut finish_registration_rng,
|
|
¶meters.password,
|
|
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)
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_password_file() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite>(test_vector: &str) -> Result<(), ProtocolError>
|
|
where
|
|
// Envelope: Nonce + Hash
|
|
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
EnvelopeLen<CS>: ArrayLength<u8>,
|
|
// RegistrationUpload: (KePk + Hash) + Envelope
|
|
<CS::KeGroup as KeGroup>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<<CS::KeGroup as KeGroup>::PkLen, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
|
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
|
// ServerRegistration = RegistrationUpload
|
|
{
|
|
let parameters = populate_test_vectors(&serde_json::from_str(test_vector).unwrap());
|
|
|
|
let password_file = ServerRegistration::finish(RegistrationUpload::<CS>::deserialize(
|
|
¶meters.registration_upload,
|
|
)?);
|
|
|
|
assert_eq!(
|
|
hex::encode(parameters.password_file),
|
|
hex::encode(password_file.serialize())
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_credential_request() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite>(test_vector: &str) -> Result<(), ProtocolError>
|
|
where
|
|
// CredentialRequest: KgPk + Ke1Message
|
|
<OprfGroup<CS> as Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
|
CredentialRequestLen<CS>: ArrayLength<u8>,
|
|
// ClientLogin: KgSk + CredentialRequest + Ke1State
|
|
<OprfGroup<CS> as Group>::ScalarLen: Add<CredentialRequestLen<CS>>,
|
|
Sum<<OprfGroup<CS> as Group>::ScalarLen, CredentialRequestLen<CS>>:
|
|
ArrayLength<u8> + Add<Ke1StateLen<CS>>,
|
|
ClientLoginLen<CS>: ArrayLength<u8>,
|
|
{
|
|
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::<CS>::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(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_credential_response() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite>(test_vector: &str) -> Result<(), ProtocolError>
|
|
where
|
|
// MaskedResponse: (Nonce + Hash) + KePk
|
|
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<NonceLen, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
|
<OprfGroup<CS> as Group>::ElemLen: Add<NonceLen>,
|
|
Sum<<OprfGroup<CS> as Group>::ElemLen, NonceLen>:
|
|
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
|
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
|
|
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
|
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
|
CredentialResponseLen<CS>: ArrayLength<u8>,
|
|
{
|
|
let parameters = populate_test_vectors(&serde_json::from_str(test_vector).unwrap());
|
|
|
|
let server_setup = ServerSetup::<CS>::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::<CS>::start(
|
|
&mut server_e_sk_and_nonce_rng,
|
|
&server_setup,
|
|
Some(ServerRegistration::deserialize(¶meters.password_file)?),
|
|
CredentialRequest::<CS>::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(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_credential_finalization() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite>(test_vector: &str) -> Result<(), ProtocolError>
|
|
where
|
|
// MaskedResponse: (Nonce + Hash) + KePk
|
|
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<NonceLen, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
{
|
|
let parameters = populate_test_vectors(&serde_json::from_str(test_vector).unwrap());
|
|
|
|
let client_login_finish_result =
|
|
ClientLogin::<CS>::deserialize(¶meters.client_login_state)?.finish(
|
|
¶meters.password,
|
|
CredentialResponse::<CS>::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.serialize())
|
|
);
|
|
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(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_server_login_finish() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite>(test_vector: &str) -> Result<(), ProtocolError> {
|
|
let parameters = populate_test_vectors(&serde_json::from_str(test_vector).unwrap());
|
|
|
|
let server_login_result = ServerLogin::<CS>::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)
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn test_complete_flow<CS: CipherSuite>(
|
|
_test_vector: &str,
|
|
registration_password: &[u8],
|
|
login_password: &[u8],
|
|
) -> Result<(), ProtocolError>
|
|
where
|
|
// MaskedResponse: (Nonce + Hash) + KePk
|
|
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<NonceLen, OutputSize<OprfHash<CS>>>: ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
{
|
|
let credential_identifier = b"credentialIdentifier";
|
|
let mut client_rng = OsRng;
|
|
let mut server_rng = OsRng;
|
|
let server_setup = ServerSetup::<CS>::new(&mut server_rng);
|
|
let client_registration_start_result =
|
|
ClientRegistration::<CS>::start(&mut client_rng, registration_password)?;
|
|
let server_registration_start_result = ServerRegistration::<CS>::start(
|
|
&server_setup,
|
|
client_registration_start_result.message,
|
|
credential_identifier,
|
|
)?;
|
|
let client_registration_finish_result = client_registration_start_result.state.finish(
|
|
&mut client_rng,
|
|
registration_password,
|
|
server_registration_start_result.message,
|
|
ClientRegistrationFinishParameters::default(),
|
|
)?;
|
|
let p_file = ServerRegistration::finish(client_registration_finish_result.message);
|
|
let client_login_start_result = ClientLogin::<CS>::start(&mut client_rng, login_password)?;
|
|
let server_login_start_result = ServerLogin::<CS>::start(
|
|
&mut server_rng,
|
|
&server_setup,
|
|
Some(p_file),
|
|
client_login_start_result.message,
|
|
credential_identifier,
|
|
ServerLoginStartParameters::default(),
|
|
)?;
|
|
|
|
let client_login_result = client_login_start_result.state.finish(
|
|
login_password,
|
|
server_login_start_result.message,
|
|
ClientLoginFinishParameters::default(),
|
|
);
|
|
|
|
if hex::encode(registration_password) == hex::encode(login_password) {
|
|
let client_login_finish_result = client_login_result?;
|
|
let server_login_finish_result = server_login_start_result
|
|
.state
|
|
.finish(client_login_finish_result.message)?;
|
|
|
|
assert_eq!(
|
|
hex::encode(&server_login_finish_result.session_key),
|
|
hex::encode(&client_login_finish_result.session_key)
|
|
);
|
|
assert_eq!(
|
|
hex::encode(client_registration_finish_result.export_key),
|
|
hex::encode(client_login_finish_result.export_key)
|
|
);
|
|
} else {
|
|
assert!(matches!(
|
|
client_login_result,
|
|
Err(ProtocolError::InvalidLoginError)
|
|
));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_complete_flow_success() -> Result<(), ProtocolError> {
|
|
run_all!(test_complete_flow, b"good password", b"good password");
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_complete_flow_fail() -> Result<(), ProtocolError> {
|
|
run_all!(test_complete_flow, b"good password", b"bad password");
|
|
Ok(())
|
|
}
|
|
|
|
// Zeroize tests
|
|
|
|
#[test]
|
|
fn test_zeroize_client_registration_start() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite>(_test_vector: &str) -> Result<(), ProtocolError> {
|
|
let mut client_rng = OsRng;
|
|
let client_registration_start_result =
|
|
ClientRegistration::<CS>::start(&mut client_rng, STR_PASSWORD.as_bytes())?;
|
|
|
|
let mut state = client_registration_start_result.state;
|
|
unsafe { ptr::drop_in_place(&mut state) };
|
|
for byte in state.to_vec() {
|
|
assert_eq!(byte, 0);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_zeroize_client_registration_finish() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite>(_test_vector: &str) -> Result<(), ProtocolError> {
|
|
let mut client_rng = OsRng;
|
|
let mut server_rng = OsRng;
|
|
let server_setup = ServerSetup::<CS>::new(&mut server_rng);
|
|
let client_registration_start_result =
|
|
ClientRegistration::<CS>::start(&mut client_rng, STR_PASSWORD.as_bytes())?;
|
|
let server_registration_start_result = ServerRegistration::<CS>::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,
|
|
STR_PASSWORD.as_bytes(),
|
|
server_registration_start_result.message,
|
|
ClientRegistrationFinishParameters::default(),
|
|
)?;
|
|
|
|
let mut state = client_registration_finish_result.state;
|
|
unsafe { ptr::drop_in_place(&mut state) };
|
|
for byte in state.to_vec() {
|
|
assert_eq!(byte, 0);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_zeroize_server_registration_finish() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite>(_test_vector: &str) -> Result<(), ProtocolError>
|
|
where
|
|
// Envelope: Nonce + Hash
|
|
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
EnvelopeLen<CS>: ArrayLength<u8>,
|
|
// RegistrationUpload: (KePk + Hash) + Envelope
|
|
<CS::KeGroup as KeGroup>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<<CS::KeGroup as KeGroup>::PkLen, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
|
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
|
// ServerRegistration = RegistrationUpload
|
|
{
|
|
let mut client_rng = OsRng;
|
|
let mut server_rng = OsRng;
|
|
let server_setup = ServerSetup::<CS>::new(&mut server_rng);
|
|
let client_registration_start_result =
|
|
ClientRegistration::<CS>::start(&mut client_rng, STR_PASSWORD.as_bytes())?;
|
|
let server_registration_start_result = ServerRegistration::<CS>::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,
|
|
STR_PASSWORD.as_bytes(),
|
|
server_registration_start_result.message,
|
|
ClientRegistrationFinishParameters::default(),
|
|
)?;
|
|
let p_file = ServerRegistration::finish(client_registration_finish_result.message);
|
|
|
|
let mut state = p_file;
|
|
util::drop_manually(&mut state);
|
|
util::test_zeroized(&mut state.0.envelope.mode);
|
|
util::test_zeroized(&mut state.0.masking_key);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_zeroize_client_login_start() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite<KeyExchange = TripleDh>>(
|
|
_test_vector: &str,
|
|
) -> Result<(), ProtocolError>
|
|
where
|
|
<CS::KeGroup as KeGroup>::Sk: DiffieHellman<CS::KeGroup>,
|
|
// CredentialRequest: KgPk + Ke1Message
|
|
<OprfGroup<CS> as Group>::ElemLen: Add<Sum<NonceLen, <CS::KeGroup as KeGroup>::PkLen>>,
|
|
CredentialRequestLen<CS>: ArrayLength<u8>,
|
|
// Ke1State: KeSk + Nonce
|
|
<CS::KeGroup as KeGroup>::SkLen: Add<NonceLen>,
|
|
Sum<<CS::KeGroup as KeGroup>::SkLen, NonceLen>: ArrayLength<u8>,
|
|
// Ke1Message: Nonce + KePk
|
|
NonceLen: Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
Sum<NonceLen, <CS::KeGroup as KeGroup>::PkLen>: ArrayLength<u8>,
|
|
// Ke2State: (Hash + Hash) + Hash
|
|
OutputSize<OprfHash<CS>>: Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<OutputSize<OprfHash<CS>>, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength<u8> + Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<Sum<OutputSize<OprfHash<CS>>, OutputSize<OprfHash<CS>>>, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength<u8>,
|
|
// Ke2Message: (Nonce + KePk) + Hash
|
|
NonceLen: Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
Sum<NonceLen, <CS::KeGroup as KeGroup>::PkLen>:
|
|
ArrayLength<u8> + Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<Sum<NonceLen, <CS::KeGroup as KeGroup>::PkLen>, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength<u8>,
|
|
{
|
|
let mut client_rng = OsRng;
|
|
let client_login_start_result =
|
|
ClientLogin::<CS>::start(&mut client_rng, STR_PASSWORD.as_bytes())?;
|
|
|
|
let mut state = client_login_start_result.state;
|
|
util::drop_manually(&mut state);
|
|
util::test_zeroized(&mut state.oprf_client);
|
|
util::test_zeroized(&mut state.ke1_state);
|
|
util::test_zeroized(&mut state.credential_request.ke1_message.client_nonce);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_zeroize_server_login_start() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite>(_test_vector: &str) -> Result<(), ProtocolError>
|
|
where
|
|
// MaskedResponse: (Nonce + Hash) + KePk
|
|
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<NonceLen, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
{
|
|
let mut client_rng = OsRng;
|
|
let mut server_rng = OsRng;
|
|
let server_setup = ServerSetup::<CS>::new(&mut server_rng);
|
|
let client_registration_start_result =
|
|
ClientRegistration::<CS>::start(&mut client_rng, STR_PASSWORD.as_bytes())?;
|
|
let server_registration_start_result = ServerRegistration::<CS>::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,
|
|
STR_PASSWORD.as_bytes(),
|
|
server_registration_start_result.message,
|
|
ClientRegistrationFinishParameters::default(),
|
|
)?;
|
|
let p_file = ServerRegistration::finish(client_registration_finish_result.message);
|
|
let client_login_start_result =
|
|
ClientLogin::<CS>::start(&mut client_rng, STR_PASSWORD.as_bytes())?;
|
|
let server_login_start_result = ServerLogin::<CS>::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;
|
|
unsafe { ptr::drop_in_place(&mut state) };
|
|
for byte in state.serialize() {
|
|
assert_eq!(byte, 0);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_zeroize_client_login_finish() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite<KeyExchange = TripleDh>>(
|
|
_test_vector: &str,
|
|
) -> Result<(), ProtocolError>
|
|
where
|
|
<CS::KeGroup as KeGroup>::Sk: DiffieHellman<CS::KeGroup>,
|
|
// MaskedResponse: (Nonce + Hash) + KePk
|
|
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<NonceLen, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
// CredentialRequest: KgPk + Ke1Message
|
|
<OprfGroup<CS> as Group>::ElemLen: Add<Sum<NonceLen, <CS::KeGroup as KeGroup>::PkLen>>,
|
|
CredentialRequestLen<CS>: ArrayLength<u8>,
|
|
// Ke1State: KeSk + Nonce
|
|
<CS::KeGroup as KeGroup>::SkLen: Add<NonceLen>,
|
|
Sum<<CS::KeGroup as KeGroup>::SkLen, NonceLen>: ArrayLength<u8>,
|
|
// Ke1Message: Nonce + KePk
|
|
NonceLen: Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
Sum<NonceLen, <CS::KeGroup as KeGroup>::PkLen>: ArrayLength<u8>,
|
|
// Ke2State: (Hash + Hash) + Hash
|
|
OutputSize<OprfHash<CS>>: Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<OutputSize<OprfHash<CS>>, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength<u8> + Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<Sum<OutputSize<OprfHash<CS>>, OutputSize<OprfHash<CS>>>, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength<u8>,
|
|
// Ke2Message: (Nonce + KePk) + Hash
|
|
NonceLen: Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
Sum<NonceLen, <CS::KeGroup as KeGroup>::PkLen>:
|
|
ArrayLength<u8> + Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<Sum<NonceLen, <CS::KeGroup as KeGroup>::PkLen>, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength<u8>,
|
|
{
|
|
let mut client_rng = OsRng;
|
|
let mut server_rng = OsRng;
|
|
let server_setup = ServerSetup::<CS>::new(&mut server_rng);
|
|
let client_registration_start_result =
|
|
ClientRegistration::<CS>::start(&mut client_rng, STR_PASSWORD.as_bytes())?;
|
|
let server_registration_start_result = ServerRegistration::<CS>::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,
|
|
STR_PASSWORD.as_bytes(),
|
|
server_registration_start_result.message,
|
|
ClientRegistrationFinishParameters::default(),
|
|
)?;
|
|
let p_file = ServerRegistration::finish(client_registration_finish_result.message);
|
|
let client_login_start_result =
|
|
ClientLogin::<CS>::start(&mut client_rng, STR_PASSWORD.as_bytes())?;
|
|
let server_login_start_result = ServerLogin::<CS>::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(
|
|
STR_PASSWORD.as_bytes(),
|
|
server_login_start_result.message,
|
|
ClientLoginFinishParameters::default(),
|
|
)?;
|
|
|
|
let mut state = client_login_finish_result.state;
|
|
util::drop_manually(&mut state);
|
|
util::test_zeroized(&mut state.oprf_client);
|
|
util::test_zeroized(&mut state.ke1_state);
|
|
util::test_zeroized(&mut state.credential_request.ke1_message.client_nonce);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_zeroize_server_login_finish() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite>(_test_vector: &str) -> Result<(), ProtocolError>
|
|
where
|
|
// MaskedResponse: (Nonce + Hash) + KePk
|
|
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<NonceLen, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
{
|
|
let mut client_rng = OsRng;
|
|
let mut server_rng = OsRng;
|
|
let server_setup = ServerSetup::<CS>::new(&mut server_rng);
|
|
let client_registration_start_result =
|
|
ClientRegistration::<CS>::start(&mut client_rng, STR_PASSWORD.as_bytes())?;
|
|
let server_registration_start_result = ServerRegistration::<CS>::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,
|
|
STR_PASSWORD.as_bytes(),
|
|
server_registration_start_result.message,
|
|
ClientRegistrationFinishParameters::default(),
|
|
)?;
|
|
let p_file = ServerRegistration::finish(client_registration_finish_result.message);
|
|
let client_login_start_result =
|
|
ClientLogin::<CS>::start(&mut client_rng, STR_PASSWORD.as_bytes())?;
|
|
let server_login_start_result = ServerLogin::<CS>::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(
|
|
STR_PASSWORD.as_bytes(),
|
|
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;
|
|
unsafe { ptr::drop_in_place(&mut state) };
|
|
for byte in state.serialize() {
|
|
assert_eq!(byte, 0);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_scalar_always_nonzero() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite>(_test_vector: &str) -> 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::<CS>::start(&mut client_registration_rng, STR_PASSWORD.as_bytes())?;
|
|
|
|
assert!(!bool::from(
|
|
OprfGroup::<CS>::identity_elem().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::<CS>::start(&mut client_login_rng, STR_PASSWORD.as_bytes())?;
|
|
|
|
assert!(!bool::from(
|
|
OprfGroup::<CS>::identity_elem().ct_eq(
|
|
&client_login_start_result
|
|
.message
|
|
.get_blinded_element_for_testing()
|
|
.value(),
|
|
)
|
|
));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_reflected_value_error_registration() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite>(_test_vector: &str) -> 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::<CS>::new(&mut server_rng);
|
|
let client_registration_start_result =
|
|
ClientRegistration::<CS>::start(&mut client_rng, password)?;
|
|
let alpha = client_registration_start_result
|
|
.message
|
|
.get_blinded_element_for_testing()
|
|
.value();
|
|
let server_registration_start_result = ServerRegistration::<CS>::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 client_registration_finish_result = client_registration_start_result.state.finish(
|
|
&mut client_rng,
|
|
password,
|
|
reflected_registration_response,
|
|
ClientRegistrationFinishParameters::default(),
|
|
);
|
|
|
|
assert!(matches!(
|
|
client_registration_finish_result,
|
|
Err(ProtocolError::ReflectedValueError)
|
|
));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_reflected_value_error_login() -> Result<(), ProtocolError> {
|
|
fn inner<CS: CipherSuite>(_test_vector: &str) -> Result<(), ProtocolError>
|
|
where
|
|
// MaskedResponse: (Nonce + Hash) + KePk
|
|
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
Sum<NonceLen, OutputSize<OprfHash<CS>>>:
|
|
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
{
|
|
let credential_identifier = b"credentialIdentifier";
|
|
let password = b"password";
|
|
let mut client_rng = OsRng;
|
|
let mut server_rng = OsRng;
|
|
let server_setup = ServerSetup::<CS>::new(&mut server_rng);
|
|
let client_registration_start_result =
|
|
ClientRegistration::<CS>::start(&mut client_rng, password)?;
|
|
let server_registration_start_result = ServerRegistration::<CS>::start(
|
|
&server_setup,
|
|
client_registration_start_result.message,
|
|
credential_identifier,
|
|
)?;
|
|
let client_registration_finish_result = client_registration_start_result.state.finish(
|
|
&mut client_rng,
|
|
password,
|
|
server_registration_start_result.message,
|
|
ClientRegistrationFinishParameters::default(),
|
|
)?;
|
|
let p_file = ServerRegistration::finish(client_registration_finish_result.message);
|
|
let client_login_start_result = ClientLogin::<CS>::start(&mut client_rng, password)?;
|
|
let alpha = client_login_start_result
|
|
.message
|
|
.get_blinded_element_for_testing()
|
|
.value();
|
|
let server_login_start_result = ServerLogin::<CS>::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 client_login_result = client_login_start_result.state.finish(
|
|
password,
|
|
reflected_credential_response,
|
|
ClientLoginFinishParameters::default(),
|
|
);
|
|
|
|
assert!(matches!(
|
|
client_login_result,
|
|
Err(ProtocolError::ReflectedValueError)
|
|
));
|
|
Ok(())
|
|
}
|
|
|
|
run_all!(inner);
|
|
|
|
Ok(())
|
|
}
|