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:
daxpedda
2025-05-19 13:56:25 -07:00
committed by GitHub
parent 58b4d746c0
commit bebd2c605b
37 changed files with 9402 additions and 3375 deletions
+49 -64
View File
@@ -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(&parameters.KE1).unwrap(),
&parameters.credential_identifier,
ServerLoginStartParameters {
ServerLoginParameters {
context: Some(&parameters.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, &parameters.password)?;
let client_login_finish_result = client_login_start_result.state.finish(
&mut OsRng,
&parameters.password,
CredentialResponse::<CS>::deserialize(&parameters.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(&parameters.KE1).unwrap(),
&parameters.credential_identifier,
ServerLoginStartParameters {
ServerLoginParameters {
context: Some(&parameters.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(&parameters.KE3)?)?;
let server_login_result = server_login_start_result.state.finish(
CredentialFinalization::deserialize(&parameters.KE3)?,
ServerLoginParameters {
context: Some(&parameters.context),
identifiers: Identifiers {
client: parameters.client_identity.as_deref(),
server: parameters.server_identity.as_deref(),
},
},
)?;
assert_eq!(
hex::encode(&parameters.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(&parameters.KE1).unwrap(),
&parameters.credential_identifier,
ServerLoginStartParameters {
ServerLoginParameters {
context: Some(&parameters.context),
identifiers: Identifiers {
client: parameters.client_identity.as_deref(),