Publicly Expose trait KeyExchange (#379)

* Clean up types used in `trait KeyExchange`

* Expose `trait KeyExchange` publicly
This commit is contained in:
daxpedda
2025-06-24 15:17:29 -07:00
committed by GitHub
parent 3777ee680a
commit 8ee35498ea
16 changed files with 558 additions and 539 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ use generic_array::ArrayLength;
use crate::envelope::NonceLen; use crate::envelope::NonceLen;
use crate::hash::{Hash, OutputSize, ProxyHash}; use crate::hash::{Hash, OutputSize, ProxyHash};
use crate::key_exchange::group::Group; use crate::key_exchange::group::Group;
use crate::key_exchange::traits::KeyExchange; use crate::key_exchange::KeyExchange;
use crate::ksf::Ksf; use crate::ksf::Ksf;
use crate::opaque::MaskedResponseLen; use crate::opaque::MaskedResponseLen;
+1 -1
View File
@@ -22,7 +22,7 @@ use crate::ciphersuite::{CipherSuite, KeGroup, OprfHash};
use crate::errors::{InternalError, ProtocolError}; use crate::errors::{InternalError, ProtocolError};
use crate::hash::OutputSize; use crate::hash::OutputSize;
use crate::key_exchange::group::Group; use crate::key_exchange::group::Group;
use crate::key_exchange::traits::SerializedIdentifiers; use crate::key_exchange::SerializedIdentifiers;
use crate::keypair::{KeyPair, PrivateKey, PublicKey}; use crate::keypair::{KeyPair, PrivateKey, PublicKey};
use crate::opaque::Identifiers; use crate::opaque::Identifiers;
use crate::serialization::{GenericArrayExt, SliceExt, UpdateExt}; use crate::serialization::{GenericArrayExt, SliceExt, UpdateExt};
+429 -2
View File
@@ -12,7 +12,434 @@
pub mod group; pub mod group;
pub(crate) mod shared; pub(crate) mod shared;
pub mod sigma_i; pub mod sigma_i;
pub(crate) mod traits;
pub mod tripledh; pub mod tripledh;
pub use crate::key_exchange::traits::KeyExchange; use core::iter;
use core::ops::Add;
use derive_where::derive_where;
use digest::core_api::{BlockSizeUser, CoreProxy};
use digest::Output;
use generic_array::sequence::Concat;
use generic_array::typenum::{IsLess, Le, NonZero, Sum, U2, U256};
use generic_array::{ArrayLength, GenericArray};
use rand::{CryptoRng, RngCore};
use voprf::{BlindedElement, EvaluationElement};
use zeroize::{Zeroize, ZeroizeOnDrop};
#[cfg(test)]
use crate::ciphersuite::KeHash;
use crate::ciphersuite::{CipherSuite, OprfGroup};
use crate::errors::ProtocolError;
use crate::hash::{Hash, ProxyHash};
use crate::key_exchange::group::Group;
use crate::key_exchange::shared::{NonceLen, STR_CONTEXT};
use crate::keypair::{PrivateKey, PublicKey};
use crate::opaque::{Identifiers, MaskedResponse, MaskedResponseLen};
use crate::serialization::{i2osp, SliceExt};
/// The key exchange trait.
pub trait KeyExchange
where
<Self::Hash as CoreProxy>::Core: ProxyHash,
<<Self::Hash as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<<Self::Hash as CoreProxy>::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
{
/// The group used for the key exchange.
type Group: Group;
/// The hash used for the key exchange.
type Hash: Hash;
/// Client state.
type KE1State: ZeroizeOnDrop + Clone;
/// Server state.
type KE2State<CS: CipherSuite>: ZeroizeOnDrop + Clone;
/// First message sent by the client.
type KE1Message: ZeroizeOnDrop + Clone;
/// Server state builder.
type KE2Builder<'a, CS: CipherSuite<KeyExchange = Self>>: ZeroizeOnDrop + Clone;
/// Server data for the remote key interaction.
type KE2BuilderData<'a, CS: 'static + CipherSuite>;
/// Server remote key input.
type KE2BuilderInput<CS: CipherSuite>;
/// Message sent by the server.
type KE2Message: ZeroizeOnDrop + Clone;
/// Second message sent by the client.
type KE3Message: ZeroizeOnDrop + Clone;
/// Client generates [`KE1Message`](Self::KE1Message) and
/// [`KE1State`](Self::KE1State).
fn generate_ke1<R: RngCore + CryptoRng>(
rng: &mut R,
) -> Result<GenerateKe1Result<Self>, ProtocolError>;
/// Server generates [`KE2Builder`](Self::KE2Builder).
fn ke2_builder<'a, CS: CipherSuite<KeyExchange = Self>, R: RngCore + CryptoRng>(
rng: &mut R,
credential_request: SerializedCredentialRequest<CS>,
ke1_message: Self::KE1Message,
credential_response: SerializedCredentialResponse<CS>,
client_s_pk: PublicKey<Self::Group>,
identifiers: SerializedIdentifiers<'a, Self::Group>,
context: SerializedContext<'a>,
) -> Result<Self::KE2Builder<'a, CS>, ProtocolError>;
/// Server returns the data for the remote key interaction.
fn ke2_builder_data<'a, CS: CipherSuite<KeyExchange = Self>>(
builder: &'a Self::KE2Builder<'_, CS>,
) -> Self::KE2BuilderData<'a, CS>;
/// Server generates the input without a remote key.
fn generate_ke2_input<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
builder: &Self::KE2Builder<'_, CS>,
rng: &mut R,
server_s_sk: &PrivateKey<Self::Group>,
) -> Self::KE2BuilderInput<CS>;
/// Server generates [`KE2Message`](Self::KE2Message) and
/// [`KE2State`](Self::KE2State).
fn build_ke2<CS: CipherSuite<KeyExchange = Self>>(
builder: Self::KE2Builder<'_, CS>,
input: Self::KE2BuilderInput<CS>,
) -> Result<GenerateKe2Result<CS>, ProtocolError>;
/// Client generates [`KE3Message`](Self::KE3Message) and the session key.
#[allow(clippy::too_many_arguments)]
fn generate_ke3<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
rng: &mut R,
credential_request: SerializedCredentialRequest<CS>,
ke1_message: Self::KE1Message,
credential_response: SerializedCredentialResponse<CS>,
ke1_state: &Self::KE1State,
ke2_message: Self::KE2Message,
server_s_pk: PublicKey<Self::Group>,
client_s_sk: PrivateKey<Self::Group>,
identifiers: SerializedIdentifiers<'_, Self::Group>,
context: SerializedContext<'_>,
) -> Result<GenerateKe3Result<Self>, ProtocolError>;
/// Server generates the session key.
fn finish_ke<CS: CipherSuite<KeyExchange = Self>>(
ke2_state: &Self::KE2State<CS>,
ke3_message: Self::KE3Message,
identifiers: Identifiers<'_>,
context: SerializedContext<'_>,
) -> Result<Output<Self::Hash>, ProtocolError>;
}
/// Serialized form of [`CredentialRequest`](crate::CredentialRequest).
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize)]
pub struct SerializedCredentialRequest<CS: CipherSuite>(
GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ElemLen>,
);
impl<CS: CipherSuite> SerializedCredentialRequest<CS> {
pub(crate) fn new(blinded_element: &BlindedElement<CS::OprfCs>) -> Self {
Self(blinded_element.serialize())
}
/// Returns the serialized form of
/// [`CredentialRequest`](crate::CredentialRequest) in multiple byte slices.
pub fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
iter::once(self.0.as_slice())
}
/// Returns a [`SerializedCredentialRequest`] deserialized from the given
/// `bytes`.
pub fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError> {
Ok(Self(bytes.take_array("blinded element")?))
}
}
type SerializedCredentialRequestLen<CS: CipherSuite> = <OprfGroup<CS> as voprf::Group>::ElemLen;
impl<CS: CipherSuite> Serialize for SerializedCredentialRequest<CS> {
type Len = SerializedCredentialRequestLen<CS>;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.0.clone()
}
}
/// Serialized form of [`CredentialResponse`](crate::CredentialResponse).
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
pub struct SerializedCredentialResponse<CS: CipherSuite> {
evaluation_element: GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ElemLen>,
masking_nonce: GenericArray<u8, NonceLen>,
masked_response: MaskedResponse<CS>,
}
impl<CS: CipherSuite> SerializedCredentialResponse<CS> {
pub(crate) fn new(
evaluation_element: &EvaluationElement<CS::OprfCs>,
masking_nonce: GenericArray<u8, NonceLen>,
masked_response: MaskedResponse<CS>,
) -> Self {
Self {
evaluation_element: evaluation_element.serialize(),
masking_nonce,
masked_response,
}
}
/// Returns the serialized form of
/// [`CredentialResponse`](crate::CredentialResponse) in multiple byte
/// slices.
pub fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
[self.evaluation_element.as_slice(), &self.masking_nonce]
.into_iter()
.chain(self.masked_response.iter())
}
/// Returns a [`SerializedCredentialRequest`] deserialized from the given
/// `bytes`.
pub fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
Ok(Self {
evaluation_element: input.take_array("evaluation element")?,
masking_nonce: input.take_array("masking nonce")?,
masked_response: MaskedResponse::deserialize_take(input)?,
})
}
}
type SerializedCredentialResponseLen<CS: CipherSuite> =
Sum<Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>, MaskedResponseLen<CS>>;
impl<CS: CipherSuite> Serialize for SerializedCredentialResponse<CS>
where
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
SerializedCredentialResponseLen<CS>: ArrayLength<u8>,
{
type Len = SerializedCredentialResponseLen<CS>;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.evaluation_element
.clone()
.concat(self.masking_nonce)
.concat(self.masked_response.serialize())
}
}
/// Serialized form of a `context` given in
/// [`ClientLoginFinishParameters`](crate::ClientLoginFinishParameters) or
/// [`ServerLoginParameters`](crate::ServerLoginParameters).
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
#[derive(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
pub struct SerializedContext<'a> {
length: GenericArray<u8, U2>,
#[zeroize(skip)]
context: &'a [u8],
}
impl<'a> SerializedContext<'a> {
pub(crate) fn from(context: Option<&'a [u8]>) -> Result<Self, ProtocolError> {
let context = context.unwrap_or(&[]);
Ok(Self {
length: i2osp::<U2>(context.len())?,
context,
})
}
/// Returns the serialized form of `context` in multiple byte slices.
pub fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
iter::once(STR_CONTEXT).chain([self.length.as_slice(), self.context])
}
}
/// Serialized form of [`Identifiers`](crate::Identifiers).
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound(deserialize = "'de: 'a", serialize = ""))
)]
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
pub struct SerializedIdentifiers<'a, G: Group> {
/// Client identifiers.
pub client: SerializedIdentifier<'a, G>,
/// Server identifiers.
pub server: SerializedIdentifier<'a, G>,
}
/// Serialized form of a single identifier from
/// [`Identifiers`](crate::Identifiers).
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound(deserialize = "'de: 'a", serialize = ""))
)]
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
pub struct SerializedIdentifier<'a, G: Group> {
length: GenericArray<u8, U2>,
identifier: Identifier<'a, G>,
}
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
enum Identifier<'a, G: Group> {
Owned(GenericArray<u8, G::PkLen>),
#[derive_where(skip_inner(Zeroize))]
Borrowed(&'a [u8]),
}
impl<'a, G: Group> SerializedIdentifiers<'a, G> {
pub(crate) fn from_identifiers(
ids: Identifiers<'a>,
client_s_pk: GenericArray<u8, G::PkLen>,
server_s_pk: GenericArray<u8, G::PkLen>,
) -> Result<Self, ProtocolError> {
let client = SerializedIdentifier::from_identifier(ids.client, client_s_pk)?;
let server = SerializedIdentifier::from_identifier(ids.server, server_s_pk)?;
Ok(Self { client, server })
}
}
impl<'a, G: Group> SerializedIdentifier<'a, G> {
/// Creates a [`SerializedIdentifier`] an identifier or the corresponding
/// static public key.
pub fn from_identifier(
id: Option<&'a [u8]>,
s_pk: GenericArray<u8, G::PkLen>,
) -> Result<Self, ProtocolError> {
if let Some(id) = id {
Ok(SerializedIdentifier {
length: i2osp::<U2>(id.len())?,
identifier: Identifier::Borrowed(id),
})
} else {
Ok(SerializedIdentifier {
length: i2osp::<U2>(s_pk.len())?,
identifier: Identifier::Owned(s_pk),
})
}
}
/// Returns the serialized form of an identifier in multiple byte slices.
pub fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
[self.length.as_slice()]
.into_iter()
.chain(match &self.identifier {
Identifier::Owned(bytes) => [bytes.as_slice()],
Identifier::Borrowed(bytes) => [*bytes],
})
}
}
/// Deserialization trait for key exchange types.
pub trait Deserialize: Sized {
/// Deserialize [`Self`] from the given `bytes`.
///
/// The deserialized bytes must be taken from `bytes`.
fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError>;
}
/// Serialization trait for key exchange types.
pub trait Serialize {
/// The length of the serialized types.
type Len: ArrayLength<u8>;
/// Serialize [`Self`] to a fixed-length byte array.
fn serialize(&self) -> GenericArray<u8, Self::Len>;
}
/// Result type of [`KeyExchange::generate_ke1()`].
pub struct GenerateKe1Result<KE: KeyExchange + ?Sized> {
/// The client state.
pub state: KE::KE1State,
/// The first client message.
pub message: KE::KE1Message,
}
/// Result type of [`KeyExchange::build_ke2()`].
pub struct GenerateKe2Result<CS: CipherSuite> {
/// The server state.
pub state: <CS::KeyExchange as KeyExchange>::KE2State<CS>,
/// The server message.
pub message: <CS::KeyExchange as KeyExchange>::KE2Message,
#[cfg(test)]
pub(crate) handshake_secret: Output<KeHash<CS>>,
#[cfg(test)]
pub(crate) km2: Output<KeHash<CS>>,
}
/// Result type of [`KeyExchange::generate_ke3()`].
pub struct GenerateKe3Result<KE: KeyExchange + ?Sized> {
/// The session key.
pub session_key: Output<KE::Hash>,
/// The second client message.
pub message: KE::KE3Message,
#[cfg(test)]
pub(crate) handshake_secret: Output<KE::Hash>,
#[cfg(test)]
pub(crate) km3: Output<KE::Hash>,
}
pub(crate) type Ke1StateLen<CS: CipherSuite> =
<<CS::KeyExchange as KeyExchange>::KE1State as Serialize>::Len;
pub(crate) type Ke1MessageLen<CS: CipherSuite> =
<<CS::KeyExchange as KeyExchange>::KE1Message as Serialize>::Len;
pub(crate) type Ke2StateLen<CS: CipherSuite> =
<<CS::KeyExchange as KeyExchange>::KE2State<CS> as Serialize>::Len;
pub(crate) type Ke2MessageLen<CS: CipherSuite> =
<<CS::KeyExchange as KeyExchange>::KE2Message as Serialize>::Len;
pub(crate) type Ke3MessageLen<CS: CipherSuite> =
<<CS::KeyExchange as KeyExchange>::KE3Message as Serialize>::Len;
//////////////////////////
// Test Implementations //
//===================== //
//////////////////////////
#[cfg(test)]
use crate::serialization::AssertZeroized;
#[cfg(test)]
impl<CS: CipherSuite> AssertZeroized for SerializedCredentialRequest<CS> {
fn assert_zeroized(&self) {
let Self(blinded_element) = self;
for byte in blinded_element.iter() {
assert_eq!(byte, &0);
}
}
}
#[cfg(test)]
impl<CS: CipherSuite> AssertZeroized for SerializedCredentialResponse<CS> {
fn assert_zeroized(&self) {
let Self {
evaluation_element,
masking_nonce,
masked_response,
} = self;
for byte in evaluation_element
.iter()
.chain(masking_nonce)
.chain(masked_response.iter().flatten())
{
assert_eq!(byte, &0);
}
}
}
+16 -12
View File
@@ -17,14 +17,14 @@ use generic_array::{ArrayLength, GenericArray};
use hkdf::{Hkdf, HkdfExtract}; use hkdf::{Hkdf, HkdfExtract};
use rand::{CryptoRng, RngCore}; use rand::{CryptoRng, RngCore};
use super::{
Deserialize, GenerateKe1Result, KeyExchange, Serialize, SerializedContext,
SerializedCredentialRequest, SerializedCredentialResponse, SerializedIdentifiers,
};
use crate::ciphersuite::{CipherSuite, KeGroup, KeHash}; use crate::ciphersuite::{CipherSuite, KeGroup, KeHash};
use crate::errors::{InternalError, ProtocolError}; use crate::errors::{InternalError, ProtocolError};
use crate::hash::{Hash, OutputSize, ProxyHash}; use crate::hash::{Hash, OutputSize, ProxyHash};
use crate::key_exchange::group::Group; use crate::key_exchange::group::Group;
use crate::key_exchange::traits::{
CredentialRequestParts, CredentialResponseParts, Deserialize, Serialize, SerializedContext,
SerializedIdentifiers,
};
use crate::keypair::{KeyPair, PrivateKey, PublicKey}; use crate::keypair::{KeyPair, PrivateKey, PublicKey};
use crate::serialization::{i2osp, SliceExt, UpdateExt}; use crate::serialization::{i2osp, SliceExt, UpdateExt};
@@ -100,9 +100,13 @@ pub(super) struct DerivedKeys<H: OutputSizeUser> {
// Helper functions // Helper functions
pub(super) fn generate_ke1<R: RngCore + CryptoRng, G: Group>( pub(super) fn generate_ke1<
R: RngCore + CryptoRng,
KE: KeyExchange<KE1State = Ke1State<G>, KE1Message = Ke1Message<G>>,
G: Group,
>(
rng: &mut R, rng: &mut R,
) -> Result<(Ke1State<G>, Ke1Message<G>), ProtocolError> { ) -> Result<GenerateKe1Result<KE>, ProtocolError> {
let client_e_kp = KeyPair::<G>::derive_random(rng); let client_e_kp = KeyPair::<G>::derive_random(rng);
let client_nonce = generate_nonce::<R>(rng); let client_nonce = generate_nonce::<R>(rng);
@@ -111,13 +115,13 @@ pub(super) fn generate_ke1<R: RngCore + CryptoRng, G: Group>(
client_e_pk: client_e_kp.public().clone(), client_e_pk: client_e_kp.public().clone(),
}; };
Ok(( Ok(GenerateKe1Result {
Ke1State { state: Ke1State {
client_e_sk: client_e_kp.private().clone(), client_e_sk: client_e_kp.private().clone(),
client_nonce, client_nonce,
}, },
ke1_message, message: ke1_message,
)) })
} }
// Generate a random nonce up to NonceLen::USIZE bytes. // Generate a random nonce up to NonceLen::USIZE bytes.
@@ -130,9 +134,9 @@ pub(super) fn generate_nonce<R: RngCore + CryptoRng>(rng: &mut R) -> GenericArra
pub(super) fn transcript<CS: CipherSuite, KE: Group>( pub(super) fn transcript<CS: CipherSuite, KE: Group>(
context: &SerializedContext<'_>, context: &SerializedContext<'_>,
identifiers: &SerializedIdentifiers<'_, KeGroup<CS>>, identifiers: &SerializedIdentifiers<'_, KeGroup<CS>>,
credential_request: &CredentialRequestParts<CS>, credential_request: &SerializedCredentialRequest<CS>,
ke1_message: &Ke1MessageIter<KE>, ke1_message: &Ke1MessageIter<KE>,
credential_response: &CredentialResponseParts<CS>, credential_response: &SerializedCredentialResponse<CS>,
server_nonce: GenericArray<u8, NonceLen>, server_nonce: GenericArray<u8, NonceLen>,
server_e_pk: &GenericArray<u8, KE::PkLen>, server_e_pk: &GenericArray<u8, KE::PkLen>,
) -> KeHash<CS> { ) -> KeHash<CS> {
+22 -20
View File
@@ -20,10 +20,10 @@ use crate::errors::ProtocolError;
use crate::hash::OutputSize; use crate::hash::OutputSize;
use crate::key_exchange::group::Group; use crate::key_exchange::group::Group;
use crate::key_exchange::shared::{Ke1MessageIter, Ke1MessageIterLen, NonceLen}; use crate::key_exchange::shared::{Ke1MessageIter, Ke1MessageIterLen, NonceLen};
use crate::key_exchange::traits::{ use crate::key_exchange::{
CredentialRequestParts, CredentialRequestPartsLen, CredentialResponseParts, Deserialize, Serialize, SerializedContext, SerializedCredentialRequest,
CredentialResponsePartsLen, Deserialize, Serialize, SerializedContext, SerializedIdentifier, SerializedCredentialRequestLen, SerializedCredentialResponse, SerializedCredentialResponseLen,
SerializedIdentifiers, SerializedIdentifier, SerializedIdentifiers,
}; };
use crate::opaque::MaskedResponseLen; use crate::opaque::MaskedResponseLen;
use crate::serialization::{SliceExt, UpdateExt}; use crate::serialization::{SliceExt, UpdateExt};
@@ -99,9 +99,9 @@ pub struct MessageBuilder<'a, CS: CipherSuite> {
)] )]
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize, ZeroizeOnDrop)] #[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize, ZeroizeOnDrop)]
pub struct CachedMessage<CS: CipherSuite, KE: Group> { pub struct CachedMessage<CS: CipherSuite, KE: Group> {
pub(super) credential_request: CredentialRequestParts<CS>, pub(super) credential_request: SerializedCredentialRequest<CS>,
pub(super) ke1_message: Ke1MessageIter<KE>, pub(super) ke1_message: Ke1MessageIter<KE>,
pub(super) credential_response: CredentialResponseParts<CS>, pub(super) credential_response: SerializedCredentialResponse<CS>,
pub(super) server_nonce: GenericArray<u8, NonceLen>, pub(super) server_nonce: GenericArray<u8, NonceLen>,
pub(super) server_e_pk: GenericArray<u8, KE::PkLen>, pub(super) server_e_pk: GenericArray<u8, KE::PkLen>,
pub(super) server_mac: Output<KeHash<CS>>, pub(super) server_mac: Output<KeHash<CS>>,
@@ -231,9 +231,9 @@ impl<'a, CS: CipherSuite> MessageBuilder<'a, CS> {
impl<CS: CipherSuite, KE: Group> Deserialize for CachedMessage<CS, KE> { impl<CS: CipherSuite, KE: Group> Deserialize for CachedMessage<CS, KE> {
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> { fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
Ok(Self { Ok(Self {
credential_request: CredentialRequestParts::deserialize_take(input)?, credential_request: SerializedCredentialRequest::deserialize_take(input)?,
ke1_message: Ke1MessageIter::deserialize_take(input)?, ke1_message: Ke1MessageIter::deserialize_take(input)?,
credential_response: CredentialResponseParts::deserialize_take(input)?, credential_response: SerializedCredentialResponse::deserialize_take(input)?,
server_nonce: input.take_array("server nonce")?, server_nonce: input.take_array("server nonce")?,
server_e_pk: input.take_array("serialized server ephemeral key")?, server_e_pk: input.take_array("serialized server ephemeral key")?,
server_mac: input.take_array("server mac")?, server_mac: input.take_array("server mac")?,
@@ -246,8 +246,8 @@ type CachedMessageLen<CS: CipherSuite, KE: Group> = Sum<
Sum< Sum<
Sum< Sum<
Sum< Sum<
Sum<CredentialRequestPartsLen<CS>, Ke1MessageIterLen<KE>>, Sum<SerializedCredentialRequestLen<CS>, Ke1MessageIterLen<KE>>,
CredentialResponsePartsLen<CS>, SerializedCredentialResponseLen<CS>,
>, >,
NonceLen, NonceLen,
>, >,
@@ -258,23 +258,25 @@ type CachedMessageLen<CS: CipherSuite, KE: Group> = Sum<
impl<CS: CipherSuite, KE: Group> Serialize for CachedMessage<CS, KE> impl<CS: CipherSuite, KE: Group> Serialize for CachedMessage<CS, KE>
where where
CredentialRequestPartsLen<CS>: ArrayLength<u8> + Add<Ke1MessageIterLen<KE>>, SerializedCredentialRequestLen<CS>: ArrayLength<u8> + Add<Ke1MessageIterLen<KE>>,
Sum<CredentialRequestPartsLen<CS>, Ke1MessageIterLen<KE>>: Sum<SerializedCredentialRequestLen<CS>, Ke1MessageIterLen<KE>>:
ArrayLength<u8> + Add<CredentialResponsePartsLen<CS>>, ArrayLength<u8> + Add<SerializedCredentialResponseLen<CS>>,
Sum<Sum<CredentialRequestPartsLen<CS>, Ke1MessageIterLen<KE>>, CredentialResponsePartsLen<CS>>: Sum<
ArrayLength<u8> + Add<NonceLen>, Sum<SerializedCredentialRequestLen<CS>, Ke1MessageIterLen<KE>>,
SerializedCredentialResponseLen<CS>,
>: ArrayLength<u8> + Add<NonceLen>,
Sum< Sum<
Sum< Sum<
Sum<CredentialRequestPartsLen<CS>, Ke1MessageIterLen<KE>>, Sum<SerializedCredentialRequestLen<CS>, Ke1MessageIterLen<KE>>,
CredentialResponsePartsLen<CS>, SerializedCredentialResponseLen<CS>,
>, >,
NonceLen, NonceLen,
>: ArrayLength<u8> + Add<KE::PkLen>, >: ArrayLength<u8> + Add<KE::PkLen>,
Sum< Sum<
Sum< Sum<
Sum< Sum<
Sum<CredentialRequestPartsLen<CS>, Ke1MessageIterLen<KE>>, Sum<SerializedCredentialRequestLen<CS>, Ke1MessageIterLen<KE>>,
CredentialResponsePartsLen<CS>, SerializedCredentialResponseLen<CS>,
>, >,
NonceLen, NonceLen,
>, >,
@@ -288,7 +290,7 @@ where
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>, <OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>: Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
ArrayLength<u8> + Add<MaskedResponseLen<CS>>, ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
CredentialResponsePartsLen<CS>: ArrayLength<u8>, SerializedCredentialResponseLen<CS>: ArrayLength<u8>,
{ {
type Len = CachedMessageLen<CS, KE>; type Len = CachedMessageLen<CS, KE>;
+28 -36
View File
@@ -35,6 +35,11 @@ use zeroize::Zeroize;
use self::message::Role; use self::message::Role;
pub use self::message::{CachedMessage, HashOutput, Message, MessageBuilder, VerifyMessage}; pub use self::message::{CachedMessage, HashOutput, Message, MessageBuilder, VerifyMessage};
use super::{
Deserialize, GenerateKe1Result, GenerateKe2Result, GenerateKe3Result, KeyExchange, Serialize,
SerializedContext, SerializedCredentialRequest, SerializedCredentialResponse,
SerializedIdentifier, SerializedIdentifiers,
};
use crate::ciphersuite::{CipherSuite, KeGroup, KeHash}; use crate::ciphersuite::{CipherSuite, KeGroup, KeHash};
use crate::envelope::NonceLen; use crate::envelope::NonceLen;
use crate::errors::{InternalError, ProtocolError}; use crate::errors::{InternalError, ProtocolError};
@@ -42,11 +47,6 @@ use crate::hash::{Hash, OutputSize, ProxyHash};
use crate::key_exchange::group::Group; use crate::key_exchange::group::Group;
use crate::key_exchange::shared::{derive_keys, generate_ke1, generate_nonce, transcript}; use crate::key_exchange::shared::{derive_keys, generate_ke1, generate_nonce, transcript};
pub use crate::key_exchange::shared::{DiffieHellman, Ke1Message, Ke1State}; pub use crate::key_exchange::shared::{DiffieHellman, Ke1Message, Ke1State};
use crate::key_exchange::traits::{
CredentialRequestParts, CredentialResponseParts, Deserialize, GenerateKe2Result,
GenerateKe3Result, KeyExchange, Sealed, Serialize, SerializedContext, SerializedIdentifier,
SerializedIdentifiers,
};
use crate::keypair::{KeyPair, PrivateKey, PublicKey}; use crate::keypair::{KeyPair, PrivateKey, PublicKey};
use crate::opaque::Identifiers; use crate::opaque::Identifiers;
use crate::serialization::{SliceExt, UpdateExt}; use crate::serialization::{SliceExt, UpdateExt};
@@ -156,9 +156,9 @@ pub struct Ke2Builder<'a, CS: CipherSuite, KE: Group> {
expected_mac: Output<KeHash<CS>>, expected_mac: Output<KeHash<CS>>,
session_key: Output<KeHash<CS>>, session_key: Output<KeHash<CS>>,
#[cfg(test)] #[cfg(test)]
km3: Output<KeHash<CS>>,
#[cfg(test)]
handshake_secret: Output<KeHash<CS>>, handshake_secret: Output<KeHash<CS>>,
#[cfg(test)]
km2: Output<KeHash<CS>>,
} }
/// The server state produced after the second key exchange message /// The server state produced after the second key exchange message
@@ -239,15 +239,15 @@ where
fn generate_ke1<R: RngCore + CryptoRng>( fn generate_ke1<R: RngCore + CryptoRng>(
rng: &mut R, rng: &mut R,
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> { ) -> Result<GenerateKe1Result<Self>, ProtocolError> {
generate_ke1(rng) generate_ke1(rng)
} }
fn ke2_builder<'a, CS: CipherSuite<KeyExchange = Self>, R: RngCore + CryptoRng>( fn ke2_builder<'a, CS: CipherSuite<KeyExchange = Self>, R: RngCore + CryptoRng>(
rng: &mut R, rng: &mut R,
credential_request: CredentialRequestParts<CS>, credential_request: SerializedCredentialRequest<CS>,
ke1_message: Self::KE1Message, ke1_message: Self::KE1Message,
credential_response: CredentialResponseParts<CS>, credential_response: SerializedCredentialResponse<CS>,
client_s_pk: PublicKey<Self::Group>, client_s_pk: PublicKey<Self::Group>,
identifiers: SerializedIdentifiers<'a, KeGroup<CS>>, identifiers: SerializedIdentifiers<'a, KeGroup<CS>>,
context: SerializedContext<'a>, context: SerializedContext<'a>,
@@ -309,9 +309,9 @@ where
expected_mac: client_mac, expected_mac: client_mac,
session_key: derived_keys.session_key, session_key: derived_keys.session_key,
#[cfg(test)] #[cfg(test)]
km3: derived_keys.km3,
#[cfg(test)]
handshake_secret: derived_keys.handshake_secret, handshake_secret: derived_keys.handshake_secret,
#[cfg(test)]
km2: derived_keys.km2,
}) })
} }
@@ -333,33 +333,33 @@ where
builder: Self::KE2Builder<'_, CS>, builder: Self::KE2Builder<'_, CS>,
input: Self::KE2BuilderInput<CS>, input: Self::KE2BuilderInput<CS>,
) -> Result<GenerateKe2Result<CS>, ProtocolError> { ) -> Result<GenerateKe2Result<CS>, ProtocolError> {
Ok(( Ok(GenerateKe2Result {
Ke2State { state: Ke2State {
client_s_pk: builder.client_s_pk.clone(), client_s_pk: builder.client_s_pk.clone(),
session_key: builder.session_key.clone(), session_key: builder.session_key.clone(),
verify_state: input.1, verify_state: input.1,
expected_mac: builder.expected_mac.clone(), expected_mac: builder.expected_mac.clone(),
}, },
Ke2Message { message: Ke2Message {
server_nonce: builder.server_nonce, server_nonce: builder.server_nonce,
server_e_pk: builder.server_e_pk.clone(), server_e_pk: builder.server_e_pk.clone(),
signature: input.0, signature: input.0,
mac: builder.transcript.cache.server_mac.clone(), mac: builder.transcript.cache.server_mac.clone(),
}, },
#[cfg(test)] #[cfg(test)]
builder.handshake_secret.clone(), handshake_secret: builder.handshake_secret.clone(),
#[cfg(test)] #[cfg(test)]
builder.km3.clone(), km2: builder.km2.clone(),
)) })
} }
fn generate_ke3<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>( fn generate_ke3<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
rng: &mut R, rng: &mut R,
credential_request: CredentialRequestParts<CS>, credential_request: SerializedCredentialRequest<CS>,
ke1_message: Self::KE1Message, ke1_message: Self::KE1Message,
credential_response: CredentialResponseParts<CS>, credential_response: SerializedCredentialResponse<CS>,
ke2_message: Self::KE2Message,
ke1_state: &Self::KE1State, ke1_state: &Self::KE1State,
ke2_message: Self::KE2Message,
server_s_pk: PublicKey<Self::Group>, server_s_pk: PublicKey<Self::Group>,
client_s_sk: PrivateKey<Self::Group>, client_s_sk: PrivateKey<Self::Group>,
identifiers: SerializedIdentifiers<'_, KeGroup<CS>>, identifiers: SerializedIdentifiers<'_, KeGroup<CS>>,
@@ -427,22 +427,22 @@ where
&ke2_message.signature, &ke2_message.signature,
)?; )?;
Ok(( Ok(GenerateKe3Result {
derived_keys.session_key, session_key: derived_keys.session_key,
Ke3Message { message: Ke3Message {
signature, signature,
mac: client_mac, mac: client_mac,
}, },
#[cfg(test)] #[cfg(test)]
derived_keys.handshake_secret, handshake_secret: derived_keys.handshake_secret,
#[cfg(test)] #[cfg(test)]
derived_keys.km3, km3: derived_keys.km3,
)) })
} }
fn finish_ke<CS: CipherSuite<KeyExchange = Self>>( fn finish_ke<CS: CipherSuite<KeyExchange = Self>>(
ke3_message: Self::KE3Message,
ke2_state: &Self::KE2State<CS>, ke2_state: &Self::KE2State<CS>,
ke3_message: Self::KE3Message,
identifiers: Identifiers<'_>, identifiers: Identifiers<'_>,
context: SerializedContext<'_>, context: SerializedContext<'_>,
) -> Result<Output<KEH>, ProtocolError> { ) -> Result<Output<KEH>, ProtocolError> {
@@ -468,14 +468,6 @@ where
} }
} }
impl<SIG: SignatureProtocol, KE: 'static + Group, KEH: Hash> Sealed for SigmaI<SIG, KE, KEH>
where
KEH::Core: ProxyHash,
<KEH::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<KEH::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
{
}
impl<CS: CipherSuite, SIG: SignatureProtocol, KE: Group> Deserialize for Ke2State<CS, SIG, KE> impl<CS: CipherSuite, SIG: SignatureProtocol, KE: Group> Deserialize for Ke2State<CS, SIG, KE>
where where
SIG::VerifyState<CS, KE>: Deserialize, SIG::VerifyState<CS, KE>: Deserialize,
+1 -1
View File
@@ -11,7 +11,7 @@ use digest::{Output, OutputSizeUser};
use generic_array::{ArrayLength, GenericArray}; use generic_array::{ArrayLength, GenericArray};
use crate::errors::ProtocolError; use crate::errors::ProtocolError;
use crate::key_exchange::traits::{Deserialize, Serialize}; use crate::key_exchange::{Deserialize, Serialize};
use crate::serialization::SliceExt; use crate::serialization::SliceExt;
/// Pre-hash of the message to be verified. /// Pre-hash of the message to be verified.
-402
View File
@@ -1,402 +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 core::iter;
use core::ops::Add;
use derive_where::derive_where;
use digest::core_api::{BlockSizeUser, CoreProxy};
use digest::Output;
use generic_array::sequence::Concat;
use generic_array::typenum::{IsLess, Le, NonZero, Sum, U2, U256};
use generic_array::{ArrayLength, GenericArray};
use rand::{CryptoRng, RngCore};
use voprf::{BlindedElement, EvaluationElement};
use zeroize::{Zeroize, ZeroizeOnDrop};
#[cfg(test)]
use crate::ciphersuite::KeHash;
use crate::ciphersuite::{CipherSuite, OprfGroup};
use crate::errors::ProtocolError;
use crate::hash::{Hash, ProxyHash};
use crate::key_exchange::group::Group;
use crate::key_exchange::shared::{NonceLen, STR_CONTEXT};
use crate::keypair::{PrivateKey, PublicKey};
use crate::opaque::{Identifiers, MaskedResponse, MaskedResponseLen};
use crate::serialization::{i2osp, SliceExt};
/// The key exchange trait. This is only exposed so users can use it in generics
/// and qualified bounds.
#[allow(private_bounds)]
pub trait KeyExchange: Sealed
where
<Self::Hash as CoreProxy>::Core: ProxyHash,
<<Self::Hash as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<<Self::Hash as CoreProxy>::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
{
/// The group used for the key exchange.
type Group: Group;
/// The has used for the key exchange.
type Hash: Hash;
#[doc(hidden)]
type KE1State: ZeroizeOnDrop + Clone;
#[doc(hidden)]
type KE2State<CS: CipherSuite>: ZeroizeOnDrop + Clone;
#[doc(hidden)]
type KE1Message: ZeroizeOnDrop + Clone;
#[doc(hidden)]
type KE2Builder<'a, CS: CipherSuite<KeyExchange = Self>>: ZeroizeOnDrop + Clone;
#[doc(hidden)]
type KE2BuilderData<'a, CS: 'static + CipherSuite>;
#[doc(hidden)]
type KE2BuilderInput<CS: CipherSuite>;
#[doc(hidden)]
type KE2Message: ZeroizeOnDrop + Clone;
#[doc(hidden)]
type KE3Message: ZeroizeOnDrop + Clone;
#[doc(hidden)]
fn generate_ke1<R: RngCore + CryptoRng>(
rng: &mut R,
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError>;
#[doc(hidden)]
fn ke2_builder<'a, CS: CipherSuite<KeyExchange = Self>, R: RngCore + CryptoRng>(
rng: &mut R,
credential_request: CredentialRequestParts<CS>,
ke1_message: Self::KE1Message,
credential_response: CredentialResponseParts<CS>,
client_s_pk: PublicKey<Self::Group>,
identifiers: SerializedIdentifiers<'a, Self::Group>,
context: SerializedContext<'a>,
) -> Result<Self::KE2Builder<'a, CS>, ProtocolError>;
#[doc(hidden)]
fn ke2_builder_data<'a, CS: CipherSuite<KeyExchange = Self>>(
builder: &'a Self::KE2Builder<'_, CS>,
) -> Self::KE2BuilderData<'a, CS>;
#[doc(hidden)]
fn generate_ke2_input<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
builder: &Self::KE2Builder<'_, CS>,
rng: &mut R,
server_s_sk: &PrivateKey<Self::Group>,
) -> Self::KE2BuilderInput<CS>;
#[doc(hidden)]
fn build_ke2<CS: CipherSuite<KeyExchange = Self>>(
builder: Self::KE2Builder<'_, CS>,
input: Self::KE2BuilderInput<CS>,
) -> Result<GenerateKe2Result<CS>, ProtocolError>;
#[doc(hidden)]
#[allow(clippy::too_many_arguments)]
fn generate_ke3<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
rng: &mut R,
credential_request: CredentialRequestParts<CS>,
ke1_message: Self::KE1Message,
credential_response: CredentialResponseParts<CS>,
ke2_message: Self::KE2Message,
ke1_state: &Self::KE1State,
server_s_pk: PublicKey<Self::Group>,
client_s_sk: PrivateKey<Self::Group>,
identifiers: SerializedIdentifiers<'_, Self::Group>,
context: SerializedContext<'_>,
) -> Result<GenerateKe3Result<Self>, ProtocolError>;
#[doc(hidden)]
fn finish_ke<CS: CipherSuite<KeyExchange = Self>>(
ke3_message: Self::KE3Message,
ke2_state: &Self::KE2State<CS>,
identifiers: Identifiers<'_>,
context: SerializedContext<'_>,
) -> Result<Output<Self::Hash>, ProtocolError>;
}
pub(super) trait Sealed {}
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize)]
pub struct CredentialRequestParts<CS: CipherSuite>(
GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ElemLen>,
);
impl<CS: CipherSuite> CredentialRequestParts<CS> {
pub(crate) fn new(blinded_element: &BlindedElement<CS::OprfCs>) -> Self {
Self(blinded_element.serialize())
}
pub fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
iter::once(self.0.as_slice())
}
pub fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
Ok(Self(input.take_array("blinded element")?))
}
}
pub type CredentialRequestPartsLen<CS: CipherSuite> = <OprfGroup<CS> as voprf::Group>::ElemLen;
impl<CS: CipherSuite> Serialize for CredentialRequestParts<CS> {
type Len = CredentialRequestPartsLen<CS>;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.0.clone()
}
}
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
pub struct CredentialResponseParts<CS: CipherSuite> {
evaluation_element: GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ElemLen>,
masking_nonce: GenericArray<u8, NonceLen>,
masked_response: MaskedResponse<CS>,
}
impl<CS: CipherSuite> CredentialResponseParts<CS> {
pub(crate) fn new(
evaluation_element: &EvaluationElement<CS::OprfCs>,
masking_nonce: GenericArray<u8, NonceLen>,
masked_response: MaskedResponse<CS>,
) -> Self {
Self {
evaluation_element: evaluation_element.serialize(),
masking_nonce,
masked_response,
}
}
pub fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
[self.evaluation_element.as_slice(), &self.masking_nonce]
.into_iter()
.chain(self.masked_response.iter())
}
pub fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
Ok(Self {
evaluation_element: input.take_array("evaluation element")?,
masking_nonce: input.take_array("masking nonce")?,
masked_response: MaskedResponse::deserialize_take(input)?,
})
}
}
pub type CredentialResponsePartsLen<CS: CipherSuite> =
Sum<Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>, MaskedResponseLen<CS>>;
impl<CS: CipherSuite> Serialize for CredentialResponseParts<CS>
where
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
CredentialResponsePartsLen<CS>: ArrayLength<u8>,
{
type Len = CredentialResponsePartsLen<CS>;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.evaluation_element
.clone()
.concat(self.masking_nonce)
.concat(self.masked_response.serialize())
}
}
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
#[derive(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
pub struct SerializedContext<'a> {
length: GenericArray<u8, U2>,
#[zeroize(skip)]
context: &'a [u8],
}
impl<'a> SerializedContext<'a> {
pub(crate) fn from(context: Option<&'a [u8]>) -> Result<Self, ProtocolError> {
let context = context.unwrap_or(&[]);
Ok(Self {
length: i2osp::<U2>(context.len())?,
context,
})
}
pub fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
iter::once(STR_CONTEXT).chain([self.length.as_slice(), self.context])
}
}
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound(deserialize = "'de: 'a", serialize = ""))
)]
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
pub struct SerializedIdentifiers<'a, G: Group> {
pub client: SerializedIdentifier<'a, G>,
pub server: SerializedIdentifier<'a, G>,
}
/// Computes `I2OSP(len(input), max_bytes) || input` and helps hold output
/// without allocation.
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound(deserialize = "'de: 'a", serialize = ""))
)]
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
pub struct SerializedIdentifier<'a, G: Group> {
length: GenericArray<u8, U2>,
identifier: Identifier<'a, G>,
}
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
enum Identifier<'a, G: Group> {
Owned(GenericArray<u8, G::PkLen>),
#[derive_where(skip_inner(Zeroize))]
Borrowed(&'a [u8]),
}
impl<'a, G: Group> SerializedIdentifiers<'a, G> {
pub(crate) fn from_identifiers(
ids: Identifiers<'a>,
client_s_pk: GenericArray<u8, G::PkLen>,
server_s_pk: GenericArray<u8, G::PkLen>,
) -> Result<Self, ProtocolError> {
let client = SerializedIdentifier::from_identifier(ids.client, client_s_pk)?;
let server = SerializedIdentifier::from_identifier(ids.server, server_s_pk)?;
Ok(Self { client, server })
}
}
impl<'a, G: Group> SerializedIdentifier<'a, G> {
pub fn from_identifier(
id: Option<&'a [u8]>,
s_pk: GenericArray<u8, G::PkLen>,
) -> Result<Self, ProtocolError> {
if let Some(id) = id {
Ok(SerializedIdentifier {
length: i2osp::<U2>(id.len())?,
identifier: Identifier::Borrowed(id),
})
} else {
Ok(SerializedIdentifier {
length: i2osp::<U2>(s_pk.len())?,
identifier: Identifier::Owned(s_pk),
})
}
}
pub(crate) fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
// Some magic to make it output the same type in all branches.
[self.length.as_slice()]
.into_iter()
.chain(match &self.identifier {
Identifier::Owned(bytes) => [bytes.as_slice()],
Identifier::Borrowed(bytes) => [*bytes],
})
}
}
pub trait Deserialize: Sized {
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError>;
}
pub trait Serialize {
type Len: ArrayLength<u8>;
fn serialize(&self) -> GenericArray<u8, Self::Len>;
}
#[cfg(not(test))]
pub type GenerateKe2Result<CS: CipherSuite> = (
<CS::KeyExchange as KeyExchange>::KE2State<CS>,
<CS::KeyExchange as KeyExchange>::KE2Message,
);
#[cfg(test)]
pub type GenerateKe2Result<CS: CipherSuite> = (
<CS::KeyExchange as KeyExchange>::KE2State<CS>,
<CS::KeyExchange as KeyExchange>::KE2Message,
Output<KeHash<CS>>,
Output<KeHash<CS>>,
);
#[cfg(not(test))]
pub type GenerateKe3Result<K: KeyExchange> = (Output<K::Hash>, K::KE3Message);
#[cfg(test)]
pub type GenerateKe3Result<K: KeyExchange> = (
Output<K::Hash>,
K::KE3Message,
Output<K::Hash>,
Output<K::Hash>,
);
pub type Ke1StateLen<CS: CipherSuite> =
<<CS::KeyExchange as KeyExchange>::KE1State as Serialize>::Len;
pub type Ke1MessageLen<CS: CipherSuite> =
<<CS::KeyExchange as KeyExchange>::KE1Message as Serialize>::Len;
pub type Ke2StateLen<CS: CipherSuite> =
<<CS::KeyExchange as KeyExchange>::KE2State<CS> as Serialize>::Len;
pub type Ke2MessageLen<CS: CipherSuite> =
<<CS::KeyExchange as KeyExchange>::KE2Message as Serialize>::Len;
pub type Ke3MessageLen<CS: CipherSuite> =
<<CS::KeyExchange as KeyExchange>::KE3Message as Serialize>::Len;
//////////////////////////
// Test Implementations //
//===================== //
//////////////////////////
#[cfg(test)]
use crate::serialization::AssertZeroized;
#[cfg(test)]
impl<CS: CipherSuite> AssertZeroized for CredentialRequestParts<CS> {
fn assert_zeroized(&self) {
let Self(blinded_element) = self;
for byte in blinded_element.iter() {
assert_eq!(byte, &0);
}
}
}
#[cfg(test)]
impl<CS: CipherSuite> AssertZeroized for CredentialResponseParts<CS> {
fn assert_zeroized(&self) {
let Self {
evaluation_element,
masking_nonce,
masked_response,
} = self;
for byte in evaluation_element
.iter()
.chain(masking_nonce)
.chain(masked_response.iter().flatten())
{
assert_eq!(byte, &0);
}
}
}
+24 -31
View File
@@ -22,16 +22,17 @@ use rand::{CryptoRng, RngCore};
use subtle::{ConstantTimeEq, CtOption}; use subtle::{ConstantTimeEq, CtOption};
use zeroize::{Zeroize, ZeroizeOnDrop}; use zeroize::{Zeroize, ZeroizeOnDrop};
use super::{
Deserialize, GenerateKe1Result, GenerateKe2Result, GenerateKe3Result, KeyExchange, Serialize,
SerializedContext, SerializedCredentialRequest, SerializedCredentialResponse,
SerializedIdentifiers,
};
use crate::ciphersuite::{CipherSuite, KeGroup}; use crate::ciphersuite::{CipherSuite, KeGroup};
use crate::errors::{InternalError, ProtocolError}; use crate::errors::{InternalError, ProtocolError};
use crate::hash::{Hash, OutputSize, ProxyHash}; use crate::hash::{Hash, OutputSize, ProxyHash};
use crate::key_exchange::group::Group; use crate::key_exchange::group::Group;
use crate::key_exchange::shared::{self, NonceLen}; use crate::key_exchange::shared::{self, NonceLen};
pub use crate::key_exchange::shared::{DiffieHellman, Ke1Message, Ke1State}; pub use crate::key_exchange::shared::{DiffieHellman, Ke1Message, Ke1State};
use crate::key_exchange::traits::{
CredentialRequestParts, CredentialResponseParts, Deserialize, GenerateKe2Result,
GenerateKe3Result, KeyExchange, Sealed, Serialize, SerializedContext, SerializedIdentifiers,
};
use crate::keypair::{KeyPair, PrivateKey, PublicKey}; use crate::keypair::{KeyPair, PrivateKey, PublicKey};
use crate::opaque::Identifiers; use crate::opaque::Identifiers;
use crate::serialization::SliceExt; use crate::serialization::SliceExt;
@@ -151,15 +152,15 @@ where
fn generate_ke1<R: RngCore + CryptoRng>( fn generate_ke1<R: RngCore + CryptoRng>(
rng: &mut R, rng: &mut R,
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> { ) -> Result<GenerateKe1Result<Self>, ProtocolError> {
shared::generate_ke1(rng) shared::generate_ke1(rng)
} }
fn ke2_builder<'a, CS: CipherSuite<KeyExchange = Self>, R: RngCore + CryptoRng>( fn ke2_builder<'a, CS: CipherSuite<KeyExchange = Self>, R: RngCore + CryptoRng>(
rng: &mut R, rng: &mut R,
credential_request: CredentialRequestParts<CS>, credential_request: SerializedCredentialRequest<CS>,
ke1_message: Self::KE1Message, ke1_message: Self::KE1Message,
credential_response: CredentialResponseParts<CS>, credential_response: SerializedCredentialResponse<CS>,
client_s_pk: PublicKey<G>, client_s_pk: PublicKey<G>,
identifiers: SerializedIdentifiers<'_, KeGroup<CS>>, identifiers: SerializedIdentifiers<'_, KeGroup<CS>>,
context: SerializedContext<'a>, context: SerializedContext<'a>,
@@ -240,30 +241,30 @@ where
); );
let expected_mac = mac_hasher.finalize().into_bytes(); let expected_mac = mac_hasher.finalize().into_bytes();
Ok(( Ok(GenerateKe2Result {
Ke2State { state: Ke2State {
session_key: derived_keys.session_key, session_key: derived_keys.session_key,
expected_mac, expected_mac,
}, },
Ke2Message { message: Ke2Message {
server_nonce: builder.server_nonce, server_nonce: builder.server_nonce,
server_e_pk: builder.server_e_pk.clone(), server_e_pk: builder.server_e_pk.clone(),
mac, mac,
}, },
#[cfg(test)] #[cfg(test)]
derived_keys.handshake_secret, handshake_secret: derived_keys.handshake_secret,
#[cfg(test)] #[cfg(test)]
derived_keys.km2, km2: derived_keys.km2,
)) })
} }
fn generate_ke3<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>( fn generate_ke3<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
_: &mut R, _: &mut R,
credential_request: CredentialRequestParts<CS>, credential_request: SerializedCredentialRequest<CS>,
ke1_message: Self::KE1Message, ke1_message: Self::KE1Message,
credential_response: CredentialResponseParts<CS>, credential_response: SerializedCredentialResponse<CS>,
ke2_message: Self::KE2Message,
ke1_state: &Self::KE1State, ke1_state: &Self::KE1State,
ke2_message: Self::KE2Message,
server_s_pk: PublicKey<G>, server_s_pk: PublicKey<G>,
client_s_sk: PrivateKey<G>, client_s_sk: PrivateKey<G>,
identifiers: SerializedIdentifiers<'_, KeGroup<CS>>, identifiers: SerializedIdentifiers<'_, KeGroup<CS>>,
@@ -309,21 +310,21 @@ where
Hmac::<H>::new_from_slice(&derived_keys.km3).map_err(|_| InternalError::HmacError)?; Hmac::<H>::new_from_slice(&derived_keys.km3).map_err(|_| InternalError::HmacError)?;
Mac::update(&mut client_mac, &transcript_hasher.finalize()); Mac::update(&mut client_mac, &transcript_hasher.finalize());
Ok(( Ok(GenerateKe3Result {
derived_keys.session_key, session_key: derived_keys.session_key,
Ke3Message { message: Ke3Message {
mac: client_mac.finalize().into_bytes(), mac: client_mac.finalize().into_bytes(),
}, },
#[cfg(test)] #[cfg(test)]
derived_keys.handshake_secret, handshake_secret: derived_keys.handshake_secret,
#[cfg(test)] #[cfg(test)]
derived_keys.km3, km3: derived_keys.km3,
)) })
} }
fn finish_ke<CS: CipherSuite>( fn finish_ke<CS: CipherSuite>(
ke3_message: Self::KE3Message,
ke2_state: &Self::KE2State<CS>, ke2_state: &Self::KE2State<CS>,
ke3_message: Self::KE3Message,
_: Identifiers<'_>, _: Identifiers<'_>,
_: SerializedContext<'_>, _: SerializedContext<'_>,
) -> Result<Output<H>, ProtocolError> { ) -> Result<Output<H>, ProtocolError> {
@@ -336,14 +337,6 @@ where
} }
} }
impl<G: Group + 'static, H: Hash> Sealed for TripleDh<G, H>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
{
}
//////////////////////////////////////////////// ////////////////////////////////////////////////
// Trait Implementations // // Trait Implementations //
// ========================================== // // ========================================== //
+8 -3
View File
@@ -97,7 +97,8 @@ impl<G: Group> PrivateKey<G> {
PublicKey(G::public_key(self.0)) PublicKey(G::public_key(self.0))
} }
pub(crate) fn serialize(&self) -> GenericArray<u8, G::SkLen> { /// Serializes this private key to a fixed-length byte array.
pub fn serialize(&self) -> GenericArray<u8, G::SkLen> {
G::serialize_sk(self.0) G::serialize_sk(self.0)
} }
@@ -149,8 +150,10 @@ pub trait PrivateKeySerialization<G: Group>: Clone {
fn serialize_key_pair(key_pair: &KeyPair<G, Self>) -> GenericArray<u8, Self::Len>; fn serialize_key_pair(key_pair: &KeyPair<G, Self>) -> GenericArray<u8, Self::Len>;
/// Deserialization from bytes /// Deserialization from bytes
///
/// The deserialized bytes must be taken from `bytes`.
fn deserialize_take_key_pair( fn deserialize_take_key_pair(
input: &mut &[u8], bytes: &mut &[u8],
) -> Result<KeyPair<G, Self>, ProtocolError<Self::Error>>; ) -> Result<KeyPair<G, Self>, ProtocolError<Self::Error>>;
} }
@@ -281,7 +284,9 @@ pub trait OprfSeedSerialization<H, E>: Sized {
fn serialize(&self) -> GenericArray<u8, Self::Len>; fn serialize(&self) -> GenericArray<u8, Self::Len>;
/// Deserialization from bytes /// Deserialization from bytes
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError<E>>; ///
/// The deserialized bytes must be taken from `bytes`.
fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError<E>>;
} }
impl<H: OutputSizeUser, E> OprfSeedSerialization<H, E> for OprfSeed<H> { impl<H: OutputSizeUser, E> OprfSeedSerialization<H, E> for OprfSeed<H> {
+1 -1
View File
@@ -1349,9 +1349,9 @@ mod tests;
// Exports // Exports
pub use ciphersuite::CipherSuite;
pub use rand; pub use rand;
pub use crate::ciphersuite::CipherSuite;
#[cfg(feature = "curve25519")] #[cfg(feature = "curve25519")]
pub use crate::key_exchange::group::curve25519::Curve25519; pub use crate::key_exchange::group::curve25519::Curve25519;
#[cfg(feature = "ed25519")] #[cfg(feature = "ed25519")]
+7 -7
View File
@@ -25,9 +25,9 @@ use crate::errors::ProtocolError;
use crate::hash::OutputSize; use crate::hash::OutputSize;
use crate::key_exchange::group::Group; use crate::key_exchange::group::Group;
use crate::key_exchange::shared::NonceLen; use crate::key_exchange::shared::NonceLen;
use crate::key_exchange::traits::{ use crate::key_exchange::{
CredentialRequestParts, CredentialResponseParts, Deserialize, Ke1MessageLen, Ke2MessageLen, Deserialize, Ke1MessageLen, Ke2MessageLen, Ke3MessageLen, KeyExchange, Serialize,
Ke3MessageLen, KeyExchange, Serialize, SerializedCredentialRequest, SerializedCredentialResponse,
}; };
use crate::keypair::PublicKey; use crate::keypair::PublicKey;
use crate::opaque::{ use crate::opaque::{
@@ -359,8 +359,8 @@ impl<CS: CipherSuite> CredentialRequest<CS> {
}) })
} }
pub(crate) fn to_parts(&self) -> CredentialRequestParts<CS> { pub(crate) fn to_parts(&self) -> SerializedCredentialRequest<CS> {
CredentialRequestParts::new(&self.blinded_element) SerializedCredentialRequest::new(&self.blinded_element)
} }
/// Only used for testing purposes /// Only used for testing purposes
@@ -415,8 +415,8 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
}) })
} }
pub(crate) fn to_parts(&self) -> CredentialResponseParts<CS> { pub(crate) fn to_parts(&self) -> SerializedCredentialResponse<CS> {
CredentialResponseParts::new( SerializedCredentialResponse::new(
&self.evaluation_element, &self.evaluation_element,
self.masking_nonce, self.masking_nonce,
self.masked_response.clone(), self.masked_response.clone(),
+17 -17
View File
@@ -27,9 +27,9 @@ use crate::errors::{InternalError, ProtocolError};
use crate::hash::OutputSize; use crate::hash::OutputSize;
use crate::key_exchange::group::Group; use crate::key_exchange::group::Group;
use crate::key_exchange::shared::NonceLen; use crate::key_exchange::shared::NonceLen;
use crate::key_exchange::traits::{ use crate::key_exchange::{
CredentialResponseParts, Deserialize, Ke1MessageLen, Ke1StateLen, Ke2StateLen, KeyExchange, Deserialize, Ke1MessageLen, Ke1StateLen, Ke2StateLen, KeyExchange, Serialize,
Serialize, SerializedContext, SerializedIdentifiers, SerializedContext, SerializedCredentialResponse, SerializedIdentifiers,
}; };
use crate::keypair::{ use crate::keypair::{
KeyPair, OprfSeed, OprfSeedSerialization, PrivateKey, PrivateKeySerialization, PublicKey, KeyPair, OprfSeed, OprfSeedSerialization, PrivateKey, PrivateKeySerialization, PublicKey,
@@ -503,18 +503,18 @@ impl<CS: CipherSuite> ClientLogin<CS> {
password: &[u8], password: &[u8],
) -> Result<ClientLoginStartResult<CS>, ProtocolError> { ) -> Result<ClientLoginStartResult<CS>, ProtocolError> {
let blind_result = blind::<CS, _>(rng, password)?; let blind_result = blind::<CS, _>(rng, password)?;
let (ke1_state, ke1_message) = CS::KeyExchange::generate_ke1(rng)?; let ke1_result = CS::KeyExchange::generate_ke1(rng)?;
let credential_request = CredentialRequest { let credential_request = CredentialRequest {
blinded_element: blind_result.message, blinded_element: blind_result.message,
ke1_message, ke1_message: ke1_result.message,
}; };
Ok(ClientLoginStartResult { Ok(ClientLoginStartResult {
message: credential_request.clone(), message: credential_request.clone(),
state: Self { state: Self {
oprf_client: blind_result.state, oprf_client: blind_result.state,
ke1_state, ke1_state: ke1_result.state,
credential_request, credential_request,
}, },
}) })
@@ -582,8 +582,8 @@ impl<CS: CipherSuite> ClientLogin<CS> {
self.credential_request.to_parts(), self.credential_request.to_parts(),
self.credential_request.ke1_message.clone(), self.credential_request.ke1_message.clone(),
credential_response.to_parts(), credential_response.to_parts(),
credential_response.ke2_message,
&self.ke1_state, &self.ke1_state,
credential_response.ke2_message,
server_s_pk.clone(), server_s_pk.clone(),
opened_envelope.client_static_keypair.private().clone(), opened_envelope.client_static_keypair.private().clone(),
opened_envelope.identifiers, opened_envelope.identifiers,
@@ -592,17 +592,17 @@ impl<CS: CipherSuite> ClientLogin<CS> {
Ok(ClientLoginFinishResult { Ok(ClientLoginFinishResult {
message: CredentialFinalization { message: CredentialFinalization {
ke3_message: result.1, ke3_message: result.message,
}, },
session_key: result.0, session_key: result.session_key,
export_key: opened_envelope.export_key, export_key: opened_envelope.export_key,
server_s_pk, server_s_pk,
#[cfg(test)] #[cfg(test)]
state: self, state: self,
#[cfg(test)] #[cfg(test)]
handshake_secret: result.2, handshake_secret: result.handshake_secret,
#[cfg(test)] #[cfg(test)]
client_mac_key: result.3, client_mac_key: result.km3,
}) })
} }
} }
@@ -679,7 +679,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
let server = voprf::OprfServer::new_with_key(&oprf_key).map_err(ProtocolError::from)?; let server = voprf::OprfServer::new_with_key(&oprf_key).map_err(ProtocolError::from)?;
let evaluation_element = server.blind_evaluate(&credential_request.blinded_element); let evaluation_element = server.blind_evaluate(&credential_request.blinded_element);
let credential_response = CredentialResponseParts::new( let credential_response = SerializedCredentialResponse::new(
&evaluation_element, &evaluation_element,
masking_nonce, masking_nonce,
masked_response.clone(), masked_response.clone(),
@@ -743,18 +743,18 @@ impl<CS: CipherSuite> ServerLogin<CS> {
evaluation_element: builder.evaluation_element.clone(), evaluation_element: builder.evaluation_element.clone(),
masking_nonce: *builder.masking_nonce.deref(), masking_nonce: *builder.masking_nonce.deref(),
masked_response: builder.masked_response.clone(), masked_response: builder.masked_response.clone(),
ke2_message: result.1, ke2_message: result.message,
}; };
Ok(ServerLoginStartResult { Ok(ServerLoginStartResult {
message: credential_response, message: credential_response,
state: Self { state: Self {
ke2_state: result.0, ke2_state: result.state,
}, },
#[cfg(test)] #[cfg(test)]
handshake_secret: result.2, handshake_secret: result.handshake_secret,
#[cfg(test)] #[cfg(test)]
server_mac_key: result.3, server_mac_key: result.km2,
#[cfg(test)] #[cfg(test)]
oprf_key: builder.oprf_key.deref().clone(), oprf_key: builder.oprf_key.deref().clone(),
}) })
@@ -797,8 +797,8 @@ impl<CS: CipherSuite> ServerLogin<CS> {
let context = SerializedContext::from(parameters.context)?; let context = SerializedContext::from(parameters.context)?;
let session_key = <CS::KeyExchange as KeyExchange>::finish_ke( let session_key = <CS::KeyExchange as KeyExchange>::finish_ke(
message.ke3_message,
&self.ke2_state, &self.ke2_state,
message.ke3_message,
parameters.identifiers, parameters.identifiers,
context, context,
)?; )?;
+1 -1
View File
@@ -25,7 +25,7 @@ use crate::errors::*;
use crate::hash::OutputSize; use crate::hash::OutputSize;
use crate::key_exchange::group::Group; use crate::key_exchange::group::Group;
use crate::key_exchange::shared::NonceLen; use crate::key_exchange::shared::NonceLen;
use crate::key_exchange::traits::{ use crate::key_exchange::{
Deserialize, Ke1MessageLen, Ke1StateLen, Ke2MessageLen, KeyExchange, Serialize, Deserialize, Ke1MessageLen, Ke1StateLen, Ke2MessageLen, KeyExchange, Serialize,
}; };
use crate::keypair::KeyPair; use crate::keypair::KeyPair;
+1 -1
View File
@@ -27,7 +27,7 @@ use crate::errors::*;
use crate::hash::OutputSize; use crate::hash::OutputSize;
use crate::key_exchange::group::Group; use crate::key_exchange::group::Group;
use crate::key_exchange::shared::NonceLen; use crate::key_exchange::shared::NonceLen;
use crate::key_exchange::traits::{ use crate::key_exchange::{
Deserialize, Ke1MessageLen, Ke1StateLen, Ke2MessageLen, KeyExchange, Serialize, Deserialize, Ke1MessageLen, Ke1StateLen, Ke2MessageLen, KeyExchange, Serialize,
}; };
use crate::ksf::Identity; use crate::ksf::Identity;
+1 -3
View File
@@ -23,9 +23,7 @@ use crate::errors::*;
use crate::hash::OutputSize; use crate::hash::OutputSize;
use crate::key_exchange::group::Group; use crate::key_exchange::group::Group;
use crate::key_exchange::shared::NonceLen; use crate::key_exchange::shared::NonceLen;
use crate::key_exchange::traits::{ use crate::key_exchange::{Deserialize, Ke1MessageLen, Ke2MessageLen, KeyExchange, Serialize};
Deserialize, Ke1MessageLen, Ke2MessageLen, KeyExchange, Serialize,
};
use crate::ksf::Identity; use crate::ksf::Identity;
use crate::messages::{ use crate::messages::{
CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen, CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen,