SIGMA-I Key Exchange (#378)
* Move `KeGroup` to `KeyExchange::Group` - Introduce `KeyExchange::Hash`, which separates the OPRF hash from the one used in `KeyExchange`. - Remove `De/Serialize` requirement on key exchange messages and states, which forced a lot of where bounds on downstream users. - Rename `KeGroup` to `Group`. - Replace `D` generic for hash with `H`. * Use `voprf::derive_key()` directly * Implement SIGMA-I key exchange * Improve `KeyExchange` for SIGMA-I and Ed25519 * Implement EdDSA * Un-qualify some method calls * SIGMA-I: only include client identity in client mac * SIGMA-I: include server mac in client signature * Expose key exchange types in `crate` & move modules * Implement Ed25519ph * Document `ed25519` crate feature * Remove `ristretto255-voprf` crate feature * Adjust CI crate feature testing * Fix Rustdoc * Remove unnecessary generic parameters from SIGMA-I * Properly mark to-do's with TODO * Assorted fixes * SIGMA-I: include context in signature * SIGMA-I: include identifiers in signature * Merge `ServerLoginStart/FinishParameters` * Re-export more necessary types * More carefully expose types * Add ECDSA test * SIGMA-I: share context hashing * De-duplicate client static public key storage * Hide `KeyExchange` better * Use the correct hash in the root documentation * Bump `derive-where` * Format documentation examples a bit further * Add remote OPRF seed documentation * Rename `deserialize_key_pair` to `deserialize_take_key_pair` * Add more key tests * Remove `SharedSecret` trait * SIGMA-I refactor message API * Share more implementation between 3DH and SIGMA-I * Remove unnecessary zero scalar check for Curve25519 * Use correct hash in test * Add some more TODOs * Exclude `tests` folder from Cargo publishing * Enable missing dependencies * Use right crate for testing Ed25519 * Remove unnecessary `Sized` constraints * Remove unnecessary `ecdsa` crate features * Move signature de/serialization to trait methods * Nit: move import to appropriate location * Add warning to SIGMA-I
This commit is contained in:
+330
-583
File diff suppressed because it is too large
Load Diff
+3902
-606
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,4 @@ mod full_test_vectors;
|
||||
pub mod mock_rng;
|
||||
mod opaque_vectors;
|
||||
mod parser;
|
||||
#[cfg(test_hsm)]
|
||||
mod remote_key;
|
||||
mod test_opaque_vectors;
|
||||
|
||||
@@ -1,458 +0,0 @@
|
||||
// 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 std::env;
|
||||
use std::ops::Add;
|
||||
use std::sync::{LazyLock, Mutex};
|
||||
use std::vec::Vec;
|
||||
|
||||
use cryptoki::context::{CInitializeArgs, Pkcs11};
|
||||
use cryptoki::mechanism::elliptic_curve::{EcKdf, Ecdh1DeriveParams};
|
||||
use cryptoki::mechanism::Mechanism;
|
||||
use cryptoki::object::{Attribute, AttributeType, KeyType, ObjectClass, ObjectHandle};
|
||||
use cryptoki::session::{Session, UserType};
|
||||
use cryptoki::types::AuthPin;
|
||||
use digest::OutputSizeUser;
|
||||
use elliptic_curve::group::Curve;
|
||||
use elliptic_curve::pkcs8::der::asn1::{OctetString, OctetStringRef};
|
||||
use elliptic_curve::pkcs8::der::{Decode, Encode};
|
||||
use elliptic_curve::pkcs8::{AssociatedOid, ObjectIdentifier};
|
||||
use elliptic_curve::point::{AffineCoordinates, DecompressPoint};
|
||||
use elliptic_curve::sec1::{ModulusSize, Tag, ToEncodedPoint};
|
||||
use elliptic_curve::{AffinePoint, CurveArithmetic, FieldBytesSize, Group, ProjectivePoint};
|
||||
use generic_array::typenum::{Sum, Unsigned};
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use p256::NistP256;
|
||||
use p384::NistP384;
|
||||
use p521::NistP521;
|
||||
use rand::rngs::OsRng;
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
|
||||
|
||||
use crate::ciphersuite::{OprfGroup, OprfHash};
|
||||
use crate::envelope::NonceLen;
|
||||
use crate::hash::OutputSize;
|
||||
use crate::key_exchange::group::KeGroup;
|
||||
use crate::key_exchange::tripledh::{DiffieHellman, TripleDh};
|
||||
use crate::keypair::{KeyPair, PublicKey};
|
||||
use crate::ksf::Identity;
|
||||
use crate::opaque::MaskedResponseLen;
|
||||
use crate::{
|
||||
CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginStartResult,
|
||||
ClientRegistration, ClientRegistrationFinishParameters, ClientRegistrationStartResult,
|
||||
ServerLogin, ServerLoginStartParameters, ServerLoginStartResult, ServerRegistration,
|
||||
ServerSetup,
|
||||
};
|
||||
#[cfg(all(feature = "curve25519", feature = "ristretto255"))]
|
||||
use crate::{Curve25519, Ristretto255};
|
||||
|
||||
#[test]
|
||||
fn p256() {
|
||||
struct Suite;
|
||||
|
||||
impl CipherSuite for Suite {
|
||||
type OprfCs = NistP256;
|
||||
type KeGroup = NistP256;
|
||||
type KeyExchange = TripleDh;
|
||||
type Ksf = Identity;
|
||||
}
|
||||
|
||||
test::<Suite>(
|
||||
Mechanism::EccKeyPairGen,
|
||||
NistP256::OID,
|
||||
Mechanism::Sha256Hmac,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn p384() {
|
||||
struct Suite;
|
||||
|
||||
impl CipherSuite for Suite {
|
||||
type OprfCs = NistP384;
|
||||
type KeGroup = NistP384;
|
||||
type KeyExchange = TripleDh;
|
||||
type Ksf = Identity;
|
||||
}
|
||||
|
||||
test::<Suite>(
|
||||
Mechanism::EccKeyPairGen,
|
||||
NistP384::OID,
|
||||
Mechanism::Sha384Hmac,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn p521() {
|
||||
struct Suite;
|
||||
|
||||
impl CipherSuite for Suite {
|
||||
type OprfCs = NistP521;
|
||||
type KeGroup = NistP521;
|
||||
type KeyExchange = TripleDh;
|
||||
type Ksf = Identity;
|
||||
}
|
||||
|
||||
test::<Suite>(
|
||||
Mechanism::EccKeyPairGen,
|
||||
NistP521::OID,
|
||||
Mechanism::Sha512Hmac,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(feature = "curve25519", feature = "ristretto255"))]
|
||||
fn curve25519() {
|
||||
struct Suite;
|
||||
|
||||
impl CipherSuite for Suite {
|
||||
type OprfCs = Ristretto255;
|
||||
type KeGroup = Curve25519;
|
||||
type KeyExchange = TripleDh;
|
||||
type Ksf = Identity;
|
||||
}
|
||||
|
||||
test::<Suite>(
|
||||
// This should be [`Mechanism::EccMontgomeryKeyPairGen`], but SoftHSM has an incorrect
|
||||
// implementation. See https://github.com/softhsm/SoftHSMv2/issues/647.
|
||||
Mechanism::EccEdwardsKeyPairGen,
|
||||
ObjectIdentifier::new("1.3.101.110").unwrap(),
|
||||
Mechanism::Sha512Hmac,
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct RemoteKey(ObjectHandle);
|
||||
|
||||
trait Pkcs11DiffieHellman<KG: KeGroup> {
|
||||
fn pkcs11_diffie_hellman(
|
||||
&self,
|
||||
server_pk: &PublicKey<KG>,
|
||||
client_pk: &PublicKey<KG>,
|
||||
) -> GenericArray<u8, KG::PkLen>;
|
||||
}
|
||||
|
||||
fn test<CS: CipherSuite<KeyExchange = TripleDh>>(
|
||||
dh_mechanism: Mechanism,
|
||||
oid: ObjectIdentifier,
|
||||
hmac_mechanism: Mechanism,
|
||||
) where
|
||||
RemoteKey: Pkcs11DiffieHellman<CS::KeGroup>,
|
||||
<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>,
|
||||
// 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 (remote_key, pk) = pkcs11_generate_key_pair(dh_mechanism, oid);
|
||||
|
||||
let keypair = KeyPair::new(RemoteKey(remote_key), pk);
|
||||
let oprf_seed = pkcs11_generate_oprf_seed(<OprfHash<CS> as OutputSizeUser>::OutputSize::U64);
|
||||
let server_setup = ServerSetup::new_with_key_pair_and_seed(&mut OsRng, keypair, oprf_seed);
|
||||
|
||||
const PASSWORD: &str = "password";
|
||||
|
||||
let ClientRegistrationStartResult {
|
||||
message,
|
||||
state: client,
|
||||
} = ClientRegistration::<CS>::start(&mut OsRng, PASSWORD.as_bytes()).unwrap();
|
||||
let key_material_info = server_setup.key_material_info(&[]);
|
||||
let key_material = pkcs11_hkdf::<CS>(
|
||||
key_material_info.ikm,
|
||||
hmac_mechanism,
|
||||
Vec::from_iter(key_material_info.info.into_iter().flatten().copied()),
|
||||
);
|
||||
let message = ServerRegistration::start_with_key_material(&server_setup, key_material, message)
|
||||
.unwrap()
|
||||
.message;
|
||||
let message = client
|
||||
.finish(
|
||||
&mut OsRng,
|
||||
PASSWORD.as_bytes(),
|
||||
message,
|
||||
ClientRegistrationFinishParameters::default(),
|
||||
)
|
||||
.unwrap()
|
||||
.message;
|
||||
let file = ServerRegistration::finish(message);
|
||||
|
||||
let ClientLoginStartResult {
|
||||
message,
|
||||
state: client,
|
||||
} = ClientLogin::<CS>::start(&mut OsRng, PASSWORD.as_bytes()).unwrap();
|
||||
let key_material_info = server_setup.key_material_info(&[]);
|
||||
let key_material = pkcs11_hkdf::<CS>(
|
||||
key_material_info.ikm,
|
||||
hmac_mechanism,
|
||||
Vec::from_iter(key_material_info.info.into_iter().flatten().copied()),
|
||||
);
|
||||
let builder = ServerLogin::builder_with_key_material(
|
||||
&mut OsRng,
|
||||
&server_setup,
|
||||
key_material,
|
||||
Some(file),
|
||||
message,
|
||||
ServerLoginStartParameters::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let shared_secret = builder
|
||||
.private_key()
|
||||
.pkcs11_diffie_hellman(server_setup.keypair().public(), builder.data());
|
||||
|
||||
let ServerLoginStartResult {
|
||||
message,
|
||||
state: server,
|
||||
..
|
||||
} = builder.clone().build(shared_secret).unwrap();
|
||||
|
||||
let message = client
|
||||
.clone()
|
||||
.finish(
|
||||
PASSWORD.as_bytes(),
|
||||
message,
|
||||
ClientLoginFinishParameters::default(),
|
||||
)
|
||||
.map(|result| result.message);
|
||||
|
||||
message
|
||||
.map(|message| server.finish(message).unwrap())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
static SESSION: LazyLock<Mutex<Session>> = LazyLock::new(|| {
|
||||
let module = env::var("PKCS11_MODULE").expect("`PKCS11_MODULE` environment variable");
|
||||
let pkcs11 = Pkcs11::new(module).unwrap();
|
||||
pkcs11.initialize(CInitializeArgs::OsThreads).unwrap();
|
||||
|
||||
let slot = pkcs11.get_slots_with_token().unwrap()[0];
|
||||
|
||||
let so_pin = AuthPin::new("abcdef".into());
|
||||
pkcs11.init_token(slot, &so_pin, "Test Token").unwrap();
|
||||
|
||||
let user_pin = AuthPin::new("fedcba".into());
|
||||
|
||||
{
|
||||
let session = pkcs11.open_rw_session(slot).unwrap();
|
||||
session.login(UserType::So, Some(&so_pin)).unwrap();
|
||||
session.init_pin(&user_pin).unwrap();
|
||||
}
|
||||
|
||||
let session = pkcs11.open_rw_session(slot).unwrap();
|
||||
session.login(UserType::User, Some(&user_pin)).unwrap();
|
||||
|
||||
Mutex::new(session)
|
||||
});
|
||||
|
||||
fn pkcs11_generate_key_pair<KG: KeGroup>(
|
||||
mechanism: Mechanism,
|
||||
oid: ObjectIdentifier,
|
||||
) -> (ObjectHandle, PublicKey<KG>) {
|
||||
let session = SESSION.lock().unwrap();
|
||||
let (pk, remote_key) = session
|
||||
.generate_key_pair(
|
||||
&mechanism,
|
||||
&[
|
||||
Attribute::Token(false),
|
||||
Attribute::EcParams(oid.to_der().unwrap()),
|
||||
],
|
||||
&[Attribute::Token(false), Attribute::Derive(true)],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let Attribute::EcPoint(pk) = session
|
||||
.get_attributes(pk, &[AttributeType::EcPoint])
|
||||
.unwrap()
|
||||
.pop()
|
||||
.unwrap()
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
drop(session);
|
||||
|
||||
let pk = OctetString::from_der(&pk).unwrap();
|
||||
let pk = PublicKey::deserialize(pk.as_bytes()).unwrap();
|
||||
|
||||
(remote_key, pk)
|
||||
}
|
||||
|
||||
fn pkcs11_generate_oprf_seed(length: u64) -> ObjectHandle {
|
||||
SESSION
|
||||
.lock()
|
||||
.unwrap()
|
||||
.generate_key(
|
||||
&Mechanism::GenericSecretKeyGen,
|
||||
&[Attribute::Token(false), Attribute::ValueLen(length.into())],
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
// SoftHSM, nor any other popular HSM at the time of writing, supports HKDF. So
|
||||
// we instead implement HKDF by hand on top of the HSMs HMAC, which is supported
|
||||
// by almost all HSMs and still protects the OPRF seed.
|
||||
fn pkcs11_hkdf<CS: CipherSuite>(
|
||||
hmac: ObjectHandle,
|
||||
mechanism: Mechanism,
|
||||
info: Vec<u8>,
|
||||
) -> GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen> {
|
||||
let mut okm = GenericArray::default();
|
||||
let mut prev: Option<Vec<u8>> = None;
|
||||
let chunk_len = <OprfHash<CS> as OutputSizeUser>::OutputSize::USIZE;
|
||||
|
||||
if okm.len() > chunk_len * 255 {
|
||||
panic!("invalid length");
|
||||
}
|
||||
|
||||
let session = SESSION.lock().unwrap();
|
||||
|
||||
for (block_n, block) in (0..).zip(okm.chunks_mut(chunk_len)) {
|
||||
let mut data = Vec::new();
|
||||
|
||||
if let Some(ref prev) = prev {
|
||||
data.extend(prev.as_slice())
|
||||
};
|
||||
|
||||
data.extend(&info);
|
||||
data.extend(&[block_n + 1]);
|
||||
|
||||
let output = session.sign(&mechanism, hmac, &data).unwrap();
|
||||
block.copy_from_slice(&output[..block.len()]);
|
||||
prev = Some(output);
|
||||
}
|
||||
|
||||
okm
|
||||
}
|
||||
|
||||
impl Pkcs11DiffieHellman<NistP256> for RemoteKey {
|
||||
fn pkcs11_diffie_hellman(
|
||||
&self,
|
||||
server_pk: &PublicKey<NistP256>,
|
||||
client_pk: &PublicKey<NistP256>,
|
||||
) -> GenericArray<u8, <NistP256 as KeGroup>::PkLen> {
|
||||
ec_pkcs_11_derive_secret::<NistP256>(self.0, server_pk, client_pk)
|
||||
}
|
||||
}
|
||||
|
||||
impl Pkcs11DiffieHellman<NistP384> for RemoteKey {
|
||||
fn pkcs11_diffie_hellman(
|
||||
&self,
|
||||
server_pk: &PublicKey<NistP384>,
|
||||
client_pk: &PublicKey<NistP384>,
|
||||
) -> GenericArray<u8, <NistP384 as KeGroup>::PkLen> {
|
||||
ec_pkcs_11_derive_secret::<NistP384>(self.0, server_pk, client_pk)
|
||||
}
|
||||
}
|
||||
|
||||
impl Pkcs11DiffieHellman<NistP521> for RemoteKey {
|
||||
fn pkcs11_diffie_hellman(
|
||||
&self,
|
||||
server_pk: &PublicKey<NistP521>,
|
||||
client_pk: &PublicKey<NistP521>,
|
||||
) -> GenericArray<u8, <NistP521 as KeGroup>::PkLen> {
|
||||
ec_pkcs_11_derive_secret::<NistP521>(self.0, server_pk, client_pk)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "curve25519", feature = "ristretto255"))]
|
||||
impl Pkcs11DiffieHellman<Curve25519> for RemoteKey {
|
||||
fn pkcs11_diffie_hellman(
|
||||
&self,
|
||||
_: &PublicKey<Curve25519>,
|
||||
pk: &PublicKey<Curve25519>,
|
||||
) -> GenericArray<u8, <Curve25519 as KeGroup>::PkLen> {
|
||||
let shared_secret = pkcs11_derive_secret(self.0, &pk.serialize());
|
||||
|
||||
GenericArray::clone_from_slice(&shared_secret)
|
||||
}
|
||||
}
|
||||
|
||||
fn pkcs11_derive_secret(sk: ObjectHandle, pk: &[u8]) -> Vec<u8> {
|
||||
let session = SESSION.lock().unwrap();
|
||||
let shared_secret = session
|
||||
.derive_key(
|
||||
&Mechanism::Ecdh1Derive(Ecdh1DeriveParams::new(EcKdf::null(), pk)),
|
||||
sk,
|
||||
&[
|
||||
Attribute::Token(false),
|
||||
Attribute::KeyType(KeyType::GENERIC_SECRET),
|
||||
Attribute::Class(ObjectClass::SECRET_KEY),
|
||||
Attribute::Extractable(true),
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let Attribute::Value(shared_secret) = session
|
||||
.get_attributes(shared_secret, &[AttributeType::Value])
|
||||
.unwrap()
|
||||
.pop()
|
||||
.unwrap()
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
drop(session);
|
||||
|
||||
shared_secret
|
||||
}
|
||||
|
||||
fn ec_pkcs_11_derive_secret<KG>(
|
||||
server_sk: ObjectHandle,
|
||||
server_pk: &PublicKey<KG>,
|
||||
client_pk: &PublicKey<KG>,
|
||||
) -> GenericArray<u8, <KG as KeGroup>::PkLen>
|
||||
where
|
||||
KG: KeGroup<Pk = ProjectivePoint<KG>> + CurveArithmetic,
|
||||
AffinePoint<KG>: DecompressPoint<KG> + ToEncodedPoint<KG>,
|
||||
FieldBytesSize<KG>: ModulusSize,
|
||||
{
|
||||
let client_pk_point = client_pk.to_group_type();
|
||||
let client_pk = client_pk.serialize();
|
||||
let client_pk = OctetStringRef::new(&client_pk).unwrap();
|
||||
let client_pk = client_pk.to_der().unwrap();
|
||||
|
||||
let shared_secret_bytes = pkcs11_derive_secret(server_sk, &client_pk);
|
||||
let shared_secret_point = AffinePoint::<KG>::decompress(
|
||||
&GenericArray::clone_from_slice(&shared_secret_bytes),
|
||||
Choice::from(0),
|
||||
)
|
||||
.unwrap();
|
||||
let mut shared_secret = GenericArray::default();
|
||||
shared_secret[1..].copy_from_slice(&shared_secret_bytes);
|
||||
|
||||
let shifted_client_pk = client_pk_point + ProjectivePoint::<KG>::generator();
|
||||
let shifted_client_pk = shifted_client_pk.to_affine().to_encoded_point(true);
|
||||
let shifted_client_pk = OctetStringRef::new(shifted_client_pk.as_bytes()).unwrap();
|
||||
let shifted_client_pk = shifted_client_pk.to_der().unwrap();
|
||||
|
||||
let check_point = pkcs11_derive_secret(server_sk, &shifted_client_pk);
|
||||
|
||||
let shifted_server_pk = server_pk.to_group_type() + shared_secret_point;
|
||||
let shifted_server_pk = shifted_server_pk.to_affine();
|
||||
|
||||
let tag = u8::conditional_select(
|
||||
&(Tag::CompressedEvenY as u8),
|
||||
&(Tag::CompressedOddY as u8),
|
||||
check_point.ct_ne(&shifted_server_pk.x()),
|
||||
);
|
||||
shared_secret[0] = tag;
|
||||
|
||||
shared_secret
|
||||
}
|
||||
@@ -16,15 +16,16 @@ use generic_array::{ArrayLength, GenericArray};
|
||||
use rand::rngs::OsRng;
|
||||
use rand::RngCore;
|
||||
use serde_json::Value;
|
||||
use voprf::Group;
|
||||
|
||||
use crate::ciphersuite::{CipherSuite, OprfGroup, OprfHash};
|
||||
use crate::ciphersuite::{CipherSuite, KeGroup, 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, Ke2MessageLen};
|
||||
use crate::key_exchange::tripledh::{NonceLen, TripleDh};
|
||||
use crate::key_exchange::group::Group;
|
||||
use crate::key_exchange::shared::NonceLen;
|
||||
use crate::key_exchange::traits::{
|
||||
Deserialize, Ke1MessageLen, Ke2MessageLen, KeyExchange, Serialize,
|
||||
};
|
||||
use crate::ksf::Identity;
|
||||
use crate::messages::{
|
||||
CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen,
|
||||
@@ -99,7 +100,7 @@ fn populate_test_vectors<CS: CipherSuite>(values: &Value) -> OpaqueTestVectorPar
|
||||
dummy_private_key: {
|
||||
match decode(values, "client_private_key") {
|
||||
Some(value) => value,
|
||||
None => CS::KeGroup::serialize_sk(CS::KeGroup::random_sk(&mut OsRng)).to_vec(),
|
||||
None => KeGroup::<CS>::serialize_sk(KeGroup::<CS>::random_sk(&mut OsRng)).to_vec(),
|
||||
}
|
||||
},
|
||||
dummy_masking_key: {
|
||||
@@ -149,12 +150,9 @@ fn populate_test_vectors<CS: CipherSuite>(values: &Value) -> OpaqueTestVectorPar
|
||||
|
||||
fn get_password_file_bytes<CS: CipherSuite>(parameters: &OpaqueTestVectorParameters) -> Vec<u8>
|
||||
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>>>:
|
||||
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
||||
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
|
||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||
// ServerRegistration = RegistrationUpload
|
||||
@@ -194,8 +192,7 @@ fn tests() -> Result<(), ProtocolError> {
|
||||
struct Ristretto255Sha512NoKsf;
|
||||
impl CipherSuite for Ristretto255Sha512NoKsf {
|
||||
type OprfCs = crate::Ristretto255;
|
||||
type KeGroup = crate::Ristretto255;
|
||||
type KeyExchange = TripleDh;
|
||||
type KeyExchange = TripleDh<crate::Ristretto255, sha2::Sha512>;
|
||||
type Ksf = Identity;
|
||||
}
|
||||
|
||||
@@ -233,8 +230,7 @@ fn tests() -> Result<(), ProtocolError> {
|
||||
struct Ristretto255Sha512Curve25519NoKsf;
|
||||
impl CipherSuite for Ristretto255Sha512Curve25519NoKsf {
|
||||
type OprfCs = crate::Ristretto255;
|
||||
type KeGroup = crate::Curve25519;
|
||||
type KeyExchange = TripleDh;
|
||||
type KeyExchange = TripleDh<crate::Curve25519, sha2::Sha512>;
|
||||
type Ksf = Identity;
|
||||
}
|
||||
|
||||
@@ -270,8 +266,7 @@ fn tests() -> Result<(), ProtocolError> {
|
||||
struct P256Sha256NoKsf;
|
||||
impl CipherSuite for P256Sha256NoKsf {
|
||||
type OprfCs = p256::NistP256;
|
||||
type KeGroup = p256::NistP256;
|
||||
type KeyExchange = TripleDh;
|
||||
type KeyExchange = TripleDh<p256::NistP256, sha2::Sha256>;
|
||||
type Ksf = Identity;
|
||||
}
|
||||
|
||||
@@ -325,7 +320,7 @@ fn test_registration_response<CS: CipherSuite>(
|
||||
) -> Result<(), ProtocolError>
|
||||
where
|
||||
// RegistrationResponse: KgPk + KePk
|
||||
<OprfGroup<CS> as Group>::ElemLen: Add<<CS::KeGroup as KeGroup>::PkLen>,
|
||||
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<<KeGroup<CS> as Group>::PkLen>,
|
||||
RegistrationResponseLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
@@ -362,12 +357,9 @@ fn test_registration_upload<CS: CipherSuite>(
|
||||
tvs: &[OpaqueTestVectorParameters],
|
||||
) -> 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>>>:
|
||||
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
||||
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
|
||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
@@ -413,7 +405,8 @@ where
|
||||
fn test_ke1<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError>
|
||||
where
|
||||
// CredentialRequest: KgPk + Ke1Message
|
||||
<OprfGroup<CS> as Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
||||
<CS::KeyExchange as KeyExchange>::KE1Message: Serialize,
|
||||
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
||||
CredentialRequestLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
@@ -437,28 +430,20 @@ where
|
||||
|
||||
fn test_ke2<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError>
|
||||
where
|
||||
// Envelope: Nonce + Hash
|
||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
||||
<CS::KeyExchange as KeyExchange>::KE1Message: Deserialize,
|
||||
// RegistrationUpload: (KePk + Hash) + Envelope
|
||||
<CS::KeGroup as KeGroup>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
||||
Sum<<CS::KeGroup as KeGroup>::PkLen, OutputSize<OprfHash<CS>>>:
|
||||
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
||||
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
|
||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||
// ServerRegistration = RegistrationUpload
|
||||
// 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>>,
|
||||
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
|
||||
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
|
||||
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
||||
CredentialResponseWithoutKeLen<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>,
|
||||
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
||||
<CS::KeyExchange as KeyExchange>::KE2Message: Serialize,
|
||||
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
||||
CredentialResponseLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
@@ -490,7 +475,7 @@ where
|
||||
Some(record),
|
||||
CredentialRequest::<CS>::deserialize(¶meters.KE1).unwrap(),
|
||||
¶meters.credential_identifier,
|
||||
ServerLoginStartParameters {
|
||||
ServerLoginParameters {
|
||||
context: Some(¶meters.context),
|
||||
identifiers: Identifiers {
|
||||
client: parameters.client_identity.as_deref(),
|
||||
@@ -520,10 +505,8 @@ where
|
||||
|
||||
fn test_ke3<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> 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>,
|
||||
<CS::KeyExchange as KeyExchange>::KE2Message: Deserialize + Serialize,
|
||||
<CS::KeyExchange as KeyExchange>::KE3Message: Serialize,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let client_login_start = [
|
||||
@@ -537,6 +520,7 @@ where
|
||||
ClientLogin::<CS>::start(&mut client_login_start_rng, ¶meters.password)?;
|
||||
|
||||
let client_login_finish_result = client_login_start_result.state.finish(
|
||||
&mut OsRng,
|
||||
¶meters.password,
|
||||
CredentialResponse::<CS>::deserialize(¶meters.KE2)?,
|
||||
ClientLoginFinishParameters::new(
|
||||
@@ -577,19 +561,14 @@ fn test_server_login_finish<CS: CipherSuite>(
|
||||
tvs: &[OpaqueTestVectorParameters],
|
||||
) -> Result<(), ProtocolError>
|
||||
where
|
||||
// Envelope: Nonce + Hash
|
||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
||||
<CS::KeyExchange as KeyExchange>::KE1Message: Deserialize,
|
||||
<CS::KeyExchange as KeyExchange>::KE3Message: Deserialize,
|
||||
// RegistrationUpload: (KePk + Hash) + Envelope
|
||||
<CS::KeGroup as KeGroup>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
||||
Sum<<CS::KeGroup as KeGroup>::PkLen, OutputSize<OprfHash<CS>>>:
|
||||
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
||||
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
|
||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||
// ServerRegistration = RegistrationUpload
|
||||
// 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>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let server_setup = ServerSetup::<CS>::deserialize(
|
||||
@@ -619,7 +598,7 @@ where
|
||||
Some(record),
|
||||
CredentialRequest::<CS>::deserialize(¶meters.KE1).unwrap(),
|
||||
¶meters.credential_identifier,
|
||||
ServerLoginStartParameters {
|
||||
ServerLoginParameters {
|
||||
context: Some(¶meters.context),
|
||||
identifiers: Identifiers {
|
||||
client: parameters.client_identity.as_deref(),
|
||||
@@ -628,9 +607,16 @@ where
|
||||
},
|
||||
)?;
|
||||
|
||||
let server_login_result = server_login_start_result
|
||||
.state
|
||||
.finish(CredentialFinalization::deserialize(¶meters.KE3)?)?;
|
||||
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),
|
||||
@@ -644,15 +630,14 @@ fn test_fake_vectors<CS: CipherSuite>(
|
||||
tvs: &[OpaqueTestVectorParameters],
|
||||
) -> 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>,
|
||||
<CS::KeyExchange as KeyExchange>::KE1Message: Deserialize,
|
||||
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
||||
<OprfGroup<CS> as Group>::ElemLen: Add<NonceLen>,
|
||||
Sum<<OprfGroup<CS> as Group>::ElemLen, NonceLen>: ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
||||
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
|
||||
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
|
||||
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
||||
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
|
||||
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
||||
<CS::KeyExchange as KeyExchange>::KE2Message: Serialize,
|
||||
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
||||
CredentialResponseLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
@@ -681,7 +666,7 @@ where
|
||||
None,
|
||||
CredentialRequest::<CS>::deserialize(¶meters.KE1).unwrap(),
|
||||
¶meters.credential_identifier,
|
||||
ServerLoginStartParameters {
|
||||
ServerLoginParameters {
|
||||
context: Some(¶meters.context),
|
||||
identifiers: Identifiers {
|
||||
client: parameters.client_identity.as_deref(),
|
||||
|
||||
Reference in New Issue
Block a user