Skip to main content

opaque_vx/key_exchange/
mod.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (c) VexaHub and contributors.
3// Copyright (c) Meta Platforms, Inc. and affiliates.
4
5//! Includes instantiations of key exchange protocols used in the login step for
6//! OPAQUE
7
8pub mod group;
9pub(crate) mod shared;
10pub mod sigma_i;
11pub mod tripledh;
12#[cfg(feature = "kem")]
13pub mod tripledh_kem;
14
15use core::iter;
16use core::ops::Add;
17
18use derive_where::derive_where;
19use digest::Output;
20use digest::block_api::{CoreProxy, SmallBlockSizeUser};
21use generic_array::typenum::{IsLess, Le, NonZero, Sum, U2, U256};
22use generic_array::{ArrayLength, GenericArray};
23use hybrid_array::Array;
24use rand::{CryptoRng, Rng};
25use voprf::{BlindedElement, EvaluationElement};
26use zeroize::{Zeroize, ZeroizeOnDrop};
27
28#[cfg(test)]
29use crate::ciphersuite::KeHash;
30use crate::ciphersuite::{CipherSuite, OprfGroup};
31use crate::errors::ProtocolError;
32use crate::hash::{Hash, OutputSize, ProxyHash};
33use crate::key_exchange::group::Group;
34use crate::key_exchange::shared::{NonceLen, STR_CONTEXT};
35use crate::keypair::{PrivateKey, PublicKey};
36use crate::opaque::{Identifiers, MaskedResponse, MaskedResponseLen};
37use crate::serialization::{ConcatExt, SliceExt, i2osp};
38
39/// The key exchange trait.
40pub trait KeyExchange
41where
42    <Self::Hash as CoreProxy>::Core: ProxyHash,
43    <<Self::Hash as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
44    Le<<<Self::Hash as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
45    OutputSize<Self::Hash>: ArrayLength,
46{
47    /// The group used for the key exchange.
48    type Group: Group;
49    /// The hash used for the key exchange.
50    type Hash: Hash;
51
52    /// Client state.
53    type KE1State: ZeroizeOnDrop + Clone;
54    /// Server state.
55    type KE2State<CS: CipherSuite>: ZeroizeOnDrop + Clone;
56    /// First message sent by the client.
57    type KE1Message: ZeroizeOnDrop + Clone;
58    /// Server state builder.
59    type KE2Builder<'a, CS: CipherSuite<KeyExchange = Self>>: ZeroizeOnDrop + Clone;
60    /// Server data for the remote key interaction.
61    type KE2BuilderData<'a, CS: 'static + CipherSuite>;
62    /// Server remote key input.
63    type KE2BuilderInput<CS: CipherSuite>;
64    /// Message sent by the server.
65    type KE2Message: ZeroizeOnDrop + Clone;
66    /// Second message sent by the client.
67    type KE3Message: ZeroizeOnDrop + Clone;
68
69    /// Client generates [`KE1Message`](Self::KE1Message) and
70    /// [`KE1State`](Self::KE1State).
71    fn generate_ke1<R: Rng + CryptoRng>(
72        rng: &mut R,
73    ) -> Result<GenerateKe1Result<Self>, ProtocolError>;
74
75    /// Server generates [`KE2Builder`](Self::KE2Builder).
76    fn ke2_builder<'a, CS: CipherSuite<KeyExchange = Self>, R: Rng + CryptoRng>(
77        rng: &mut R,
78        credential_request: SerializedCredentialRequest<CS>,
79        ke1_message: Self::KE1Message,
80        credential_response: SerializedCredentialResponse<CS>,
81        client_s_pk: PublicKey<Self::Group>,
82        identifiers: SerializedIdentifiers<'a, Self::Group>,
83        context: SerializedContext<'a>,
84    ) -> Result<Self::KE2Builder<'a, CS>, ProtocolError>;
85
86    /// Server returns the data for the remote key interaction.
87    fn ke2_builder_data<'a, CS: CipherSuite<KeyExchange = Self>>(
88        builder: &'a Self::KE2Builder<'_, CS>,
89    ) -> Self::KE2BuilderData<'a, CS>;
90
91    /// Server generates the input without a remote key.
92    fn generate_ke2_input<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + Rng>(
93        builder: &Self::KE2Builder<'_, CS>,
94        rng: &mut R,
95        server_s_sk: &PrivateKey<Self::Group>,
96    ) -> Self::KE2BuilderInput<CS>;
97
98    /// Server generates [`KE2Message`](Self::KE2Message) and
99    /// [`KE2State`](Self::KE2State).
100    fn build_ke2<CS: CipherSuite<KeyExchange = Self>>(
101        builder: Self::KE2Builder<'_, CS>,
102        input: Self::KE2BuilderInput<CS>,
103    ) -> Result<GenerateKe2Result<CS>, ProtocolError>;
104
105    /// Client generates [`KE3Message`](Self::KE3Message) and the session key.
106    #[allow(clippy::too_many_arguments)]
107    fn generate_ke3<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + Rng>(
108        rng: &mut R,
109        credential_request: SerializedCredentialRequest<CS>,
110        ke1_message: Self::KE1Message,
111        credential_response: SerializedCredentialResponse<CS>,
112        ke1_state: &Self::KE1State,
113        ke2_message: Self::KE2Message,
114        server_s_pk: PublicKey<Self::Group>,
115        client_s_sk: PrivateKey<Self::Group>,
116        identifiers: SerializedIdentifiers<'_, Self::Group>,
117        context: SerializedContext<'_>,
118    ) -> Result<GenerateKe3Result<Self>, ProtocolError>;
119
120    /// Server generates the session key.
121    fn finish_ke<CS: CipherSuite<KeyExchange = Self>>(
122        ke2_state: &Self::KE2State<CS>,
123        ke3_message: Self::KE3Message,
124        identifiers: Identifiers<'_>,
125        context: SerializedContext<'_>,
126    ) -> Result<Output<Self::Hash>, ProtocolError>;
127}
128
129/// Serialized form of [`CredentialRequest`](crate::CredentialRequest).
130#[cfg_attr(
131    feature = "serde",
132    derive(serde::Deserialize, serde::Serialize),
133    serde(bound = "")
134)]
135#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize)]
136pub struct SerializedCredentialRequest<CS: CipherSuite>(
137    Array<u8, <OprfGroup<CS> as voprf::Group>::ElemLen>,
138);
139
140impl<CS: CipherSuite> SerializedCredentialRequest<CS> {
141    pub(crate) fn new(blinded_element: &BlindedElement<CS::OprfCs>) -> Self {
142        Self(blinded_element.serialize())
143    }
144
145    /// Returns the serialized form of
146    /// [`CredentialRequest`](crate::CredentialRequest) in multiple byte slices.
147    pub fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
148        iter::once(self.0.as_slice())
149    }
150
151    /// Returns a [`SerializedCredentialRequest`] deserialized from the given
152    /// `bytes`.
153    pub fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError> {
154        Ok(Self(bytes.take_array("blinded element")?.into_ha0_4()))
155    }
156}
157
158type SerializedCredentialRequestLen<CS: CipherSuite> = <OprfGroup<CS> as voprf::Group>::ElemLen;
159
160impl<CS: CipherSuite> Serialize for SerializedCredentialRequest<CS>
161where
162    <OprfGroup<CS> as voprf::Group>::ElemLen: ArrayLength,
163{
164    type Len = SerializedCredentialRequestLen<CS>;
165
166    fn serialize(&self) -> GenericArray<u8, Self::Len> {
167        GenericArray::from_slice(self.0.as_slice()).clone()
168    }
169}
170
171/// Serialized form of [`CredentialResponse`](crate::CredentialResponse).
172#[cfg_attr(
173    feature = "serde",
174    derive(serde::Deserialize, serde::Serialize),
175    serde(bound = "")
176)]
177#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
178pub struct SerializedCredentialResponse<CS: CipherSuite> {
179    evaluation_element: Array<u8, <OprfGroup<CS> as voprf::Group>::ElemLen>,
180    masking_nonce: GenericArray<u8, NonceLen>,
181    masked_response: MaskedResponse<CS>,
182}
183
184impl<CS: CipherSuite> SerializedCredentialResponse<CS> {
185    pub(crate) fn new(
186        evaluation_element: &EvaluationElement<CS::OprfCs>,
187        masking_nonce: GenericArray<u8, NonceLen>,
188        masked_response: MaskedResponse<CS>,
189    ) -> Self {
190        Self {
191            evaluation_element: evaluation_element.serialize(),
192            masking_nonce,
193            masked_response,
194        }
195    }
196
197    /// Returns the serialized form of
198    /// [`CredentialResponse`](crate::CredentialResponse) in multiple byte
199    /// slices.
200    pub fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
201        [self.evaluation_element.as_slice(), &self.masking_nonce]
202            .into_iter()
203            .chain(self.masked_response.iter())
204    }
205
206    /// Returns a [`SerializedCredentialRequest`] deserialized from the given
207    /// `bytes`.
208    pub fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
209        Ok(Self {
210            evaluation_element: input.take_array("evaluation element")?.into_ha0_4(),
211            masking_nonce: input.take_array("masking nonce")?,
212            masked_response: MaskedResponse::deserialize_take(input)?,
213        })
214    }
215}
216
217type SerializedCredentialResponseLen<CS: CipherSuite> =
218    Sum<Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>, MaskedResponseLen<CS>>;
219
220impl<CS: CipherSuite> Serialize for SerializedCredentialResponse<CS>
221where
222    <OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
223    Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
224        ArrayLength + Add<MaskedResponseLen<CS>>,
225    SerializedCredentialResponseLen<CS>: ArrayLength,
226{
227    type Len = SerializedCredentialResponseLen<CS>;
228
229    fn serialize(&self) -> GenericArray<u8, Self::Len> {
230        let elem = GenericArray::<u8, <OprfGroup<CS> as voprf::Group>::ElemLen>::from_slice(
231            self.evaluation_element.as_slice(),
232        )
233        .clone();
234
235        elem.cat(self.masking_nonce)
236            .cat(self.masked_response.serialize())
237    }
238}
239
240/// Serialized form of a `context` given in
241/// [`ClientLoginFinishParameters`](crate::ClientLoginFinishParameters) or
242/// [`ServerLoginParameters`](crate::ServerLoginParameters).
243#[cfg_attr(
244    feature = "serde",
245    derive(serde::Deserialize, serde::Serialize),
246    serde(bound = "")
247)]
248#[derive(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
249#[allow(unused_assignments)]
250pub struct SerializedContext<'a> {
251    length: GenericArray<u8, U2>,
252    #[zeroize(skip)]
253    context: &'a [u8],
254}
255
256impl<'a> SerializedContext<'a> {
257    pub(crate) fn from(context: Option<&'a [u8]>) -> Result<Self, ProtocolError> {
258        let context = context.unwrap_or(&[]);
259
260        Ok(Self {
261            length: i2osp::<U2>(context.len())?,
262            context,
263        })
264    }
265
266    /// Returns the serialized form of `context` in multiple byte slices.
267    pub fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
268        iter::once(STR_CONTEXT).chain([self.length.as_slice(), self.context])
269    }
270}
271
272/// Serialized form of [`Identifiers`].
273#[cfg_attr(
274    feature = "serde",
275    derive(serde::Deserialize, serde::Serialize),
276    serde(bound(deserialize = "'de: 'a", serialize = ""))
277)]
278#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
279pub struct SerializedIdentifiers<'a, G: Group> {
280    /// Client identifiers.
281    pub client: SerializedIdentifier<'a, G>,
282    /// Server identifiers.
283    pub server: SerializedIdentifier<'a, G>,
284}
285
286/// Serialized form of a single identifier from [`Identifiers`].
287#[cfg_attr(
288    feature = "serde",
289    derive(serde::Deserialize, serde::Serialize),
290    serde(bound(deserialize = "'de: 'a", serialize = ""))
291)]
292#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
293pub struct SerializedIdentifier<'a, G: Group> {
294    length: GenericArray<u8, U2>,
295    identifier: Identifier<'a, G>,
296}
297
298#[cfg_attr(
299    feature = "serde",
300    derive(serde::Deserialize, serde::Serialize),
301    serde(bound = "")
302)]
303#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
304enum Identifier<'a, G: Group> {
305    Owned(GenericArray<u8, G::PkLen>),
306    #[derive_where(skip_inner(Zeroize))]
307    Borrowed(&'a [u8]),
308}
309
310impl<'a, G: Group> SerializedIdentifiers<'a, G> {
311    pub(crate) fn from_identifiers(
312        ids: Identifiers<'a>,
313        client_s_pk: GenericArray<u8, G::PkLen>,
314        server_s_pk: GenericArray<u8, G::PkLen>,
315    ) -> Result<Self, ProtocolError> {
316        let client = SerializedIdentifier::from_identifier(ids.client, client_s_pk)?;
317        let server = SerializedIdentifier::from_identifier(ids.server, server_s_pk)?;
318
319        Ok(Self { client, server })
320    }
321}
322
323impl<'a, G: Group> SerializedIdentifier<'a, G> {
324    /// Creates a [`SerializedIdentifier`] an identifier or the corresponding
325    /// static public key.
326    pub fn from_identifier(
327        id: Option<&'a [u8]>,
328        s_pk: GenericArray<u8, G::PkLen>,
329    ) -> Result<Self, ProtocolError> {
330        if let Some(id) = id {
331            Ok(SerializedIdentifier {
332                length: i2osp::<U2>(id.len())?,
333                identifier: Identifier::Borrowed(id),
334            })
335        } else {
336            Ok(SerializedIdentifier {
337                length: i2osp::<U2>(s_pk.len())?,
338                identifier: Identifier::Owned(s_pk),
339            })
340        }
341    }
342
343    /// Returns the serialized form of an identifier in multiple byte slices.
344    pub fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
345        [self.length.as_slice()]
346            .into_iter()
347            .chain(match &self.identifier {
348                Identifier::Owned(bytes) => [bytes.as_slice()],
349                Identifier::Borrowed(bytes) => [*bytes],
350            })
351    }
352}
353
354/// Deserialization trait for key exchange types.
355pub trait Deserialize: Sized {
356    /// Deserialize [`Self`] from the given `bytes`.
357    ///
358    /// The deserialized bytes must be taken from `bytes`.
359    fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError>;
360}
361
362/// Serialization trait for key exchange types.
363pub trait Serialize {
364    /// The length of the serialized types.
365    type Len: ArrayLength;
366
367    /// Serialize [`Self`] to a fixed-length byte array.
368    fn serialize(&self) -> GenericArray<u8, Self::Len>;
369}
370
371/// Result type of [`KeyExchange::generate_ke1()`].
372pub struct GenerateKe1Result<KE: KeyExchange + ?Sized> {
373    /// The client state.
374    pub state: KE::KE1State,
375    /// The first client message.
376    pub message: KE::KE1Message,
377}
378
379/// Result type of [`KeyExchange::build_ke2()`].
380pub struct GenerateKe2Result<CS: CipherSuite> {
381    /// The server state.
382    pub state: <CS::KeyExchange as KeyExchange>::KE2State<CS>,
383    /// The server message.
384    pub message: <CS::KeyExchange as KeyExchange>::KE2Message,
385    #[cfg(test)]
386    pub(crate) handshake_secret: Output<KeHash<CS>>,
387    #[cfg(test)]
388    pub(crate) km2: Output<KeHash<CS>>,
389}
390
391/// Result type of [`KeyExchange::generate_ke3()`].
392pub struct GenerateKe3Result<KE: KeyExchange + ?Sized> {
393    /// The session key.
394    pub session_key: Output<KE::Hash>,
395    /// The second client message.
396    pub message: KE::KE3Message,
397    #[cfg(test)]
398    pub(crate) handshake_secret: Output<KE::Hash>,
399    #[cfg(test)]
400    pub(crate) km3: Output<KE::Hash>,
401}
402
403pub(crate) type Ke1StateLen<CS: CipherSuite> =
404    <<CS::KeyExchange as KeyExchange>::KE1State as Serialize>::Len;
405pub(crate) type Ke1MessageLen<CS: CipherSuite> =
406    <<CS::KeyExchange as KeyExchange>::KE1Message as Serialize>::Len;
407pub(crate) type Ke2StateLen<CS: CipherSuite> =
408    <<CS::KeyExchange as KeyExchange>::KE2State<CS> as Serialize>::Len;
409pub(crate) type Ke2MessageLen<CS: CipherSuite> =
410    <<CS::KeyExchange as KeyExchange>::KE2Message as Serialize>::Len;
411pub(crate) type Ke3MessageLen<CS: CipherSuite> =
412    <<CS::KeyExchange as KeyExchange>::KE3Message as Serialize>::Len;