Skip to main content

opaque_ke/
messages.rs

1// Copyright (c) Meta Platforms, Inc. and affiliates.
2//
3// This source code is dual-licensed under either the MIT license found in the
4// LICENSE-MIT file in the root directory of this source tree or the Apache
5// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
6// of this source tree. You may select, at your option, one of the above-listed
7// licenses.
8
9//! Contains the messages used for OPAQUE
10
11use core::ops::Add;
12
13use derive_where::derive_where;
14use digest::Output;
15use generic_array::sequence::Concat;
16use generic_array::typenum::{Sum, Unsigned};
17use generic_array::{ArrayLength, GenericArray};
18use rand::{CryptoRng, RngCore};
19use voprf::{BlindedElement, BlindedElementLen, EvaluationElement, EvaluationElementLen};
20use zeroize::Zeroizing;
21
22use crate::ciphersuite::{CipherSuite, KeGroup, OprfGroup, OprfHash};
23use crate::envelope::{Envelope, EnvelopeLen};
24use crate::errors::ProtocolError;
25use crate::hash::OutputSize;
26use crate::key_exchange::group::Group;
27use crate::key_exchange::shared::NonceLen;
28use crate::key_exchange::{
29    Deserialize, Ke1MessageLen, Ke2MessageLen, Ke3MessageLen, KeyExchange, Serialize,
30    SerializedCredentialRequest, SerializedCredentialResponse,
31};
32use crate::keypair::PublicKey;
33use crate::opaque::{
34    MaskedResponse, MaskedResponseLen, ServerLogin, ServerLoginStartResult, ServerSetup,
35};
36use crate::serialization::SliceExt;
37
38////////////////////////////
39// High-level API Structs //
40// ====================== //
41////////////////////////////
42
43/// The message sent by the client to the server, to initiate registration
44#[cfg_attr(
45    feature = "serde",
46    derive(serde::Deserialize, serde::Serialize),
47    serde(bound = "")
48)]
49#[derive_where(Clone)]
50#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; voprf::BlindedElement<CS::OprfCs>)]
51pub struct RegistrationRequest<CS: CipherSuite> {
52    /// blinded password information
53    pub(crate) blinded_element: voprf::BlindedElement<CS::OprfCs>,
54}
55
56/// The answer sent by the server to the user, upon reception of the
57/// registration attempt
58#[cfg_attr(
59    feature = "serde",
60    derive(serde::Deserialize, serde::Serialize),
61    serde(bound(
62        deserialize = "<KeGroup<CS> as Group>::Pk: serde::Deserialize<'de>",
63        serialize = "<KeGroup<CS> as Group>::Pk: serde::Serialize"
64    ))
65)]
66#[derive_where(Clone)]
67#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; voprf::EvaluationElement<CS::OprfCs>, <KeGroup<CS> as Group>::Pk)]
68pub struct RegistrationResponse<CS: CipherSuite> {
69    /// The server's oprf output
70    pub(crate) evaluation_element: voprf::EvaluationElement<CS::OprfCs>,
71    /// Server's static public key
72    pub(crate) server_s_pk: PublicKey<KeGroup<CS>>,
73}
74
75/// The final message from the client, containing sealed cryptographic
76/// identifiers
77#[cfg_attr(
78    feature = "serde",
79    derive(serde::Deserialize, serde::Serialize),
80    serde(bound(
81        deserialize = "<KeGroup<CS> as Group>::Pk: serde::Deserialize<'de>",
82        serialize = "<KeGroup<CS> as Group>::Pk: serde::Serialize"
83    ))
84)]
85#[derive_where(Clone, ZeroizeOnDrop)]
86#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <KeGroup<CS> as Group>::Pk)]
87pub struct RegistrationUpload<CS: CipherSuite> {
88    /// The "envelope" generated by the user, containing sealed cryptographic
89    /// identifiers
90    pub(crate) envelope: Envelope<CS>,
91    /// The masking key used to mask the envelope
92    pub(crate) masking_key: Output<OprfHash<CS>>,
93    /// The user's public key
94    #[derive_where(skip(Zeroize))]
95    pub(crate) client_s_pk: PublicKey<KeGroup<CS>>,
96}
97
98/// The message sent by the user to the server, to initiate registration
99#[cfg_attr(
100    feature = "serde",
101    derive(serde::Deserialize, serde::Serialize),
102    serde(bound(
103        deserialize = "<CS::KeyExchange as KeyExchange>::KE1Message: serde::Deserialize<'de>",
104        serialize = "<CS::KeyExchange as KeyExchange>::KE1Message: serde::Serialize"
105    ))
106)]
107#[derive_where(Clone, ZeroizeOnDrop)]
108#[derive_where(
109    Debug, Eq, Hash, PartialEq;
110    voprf::BlindedElement<CS::OprfCs>,
111    <CS::KeyExchange as KeyExchange>::KE1Message,
112)]
113pub struct CredentialRequest<CS: CipherSuite> {
114    pub(crate) blinded_element: voprf::BlindedElement<CS::OprfCs>,
115    pub(crate) ke1_message: <CS::KeyExchange as KeyExchange>::KE1Message,
116}
117
118/// Builder for [`ServerLogin`] when using remote keys.
119#[cfg_attr(
120    feature = "serde",
121    derive(serde::Deserialize, serde::Serialize),
122    serde(bound(
123        deserialize = "SK: serde::Deserialize<'de>, <CS::KeyExchange as \
124                       KeyExchange>::KE2Builder<'a, CS>: serde::Deserialize<'de>",
125        serialize = "SK: serde::Serialize, <CS::KeyExchange as KeyExchange>::KE2Builder<'a, CS>: \
126                     serde::Serialize"
127    ))
128)]
129#[derive_where(Clone)]
130#[derive_where(
131    Debug, Eq, PartialEq;
132    <KeGroup<CS> as Group>::Pk,
133    SK,
134    voprf::EvaluationElement<CS::OprfCs>,
135    <CS::KeyExchange as KeyExchange>::KE2Builder<'a, CS>,
136)]
137pub struct ServerLoginBuilder<'a, CS: CipherSuite, SK: Clone> {
138    pub(crate) server_s_sk: SK,
139    pub(crate) evaluation_element: voprf::EvaluationElement<CS::OprfCs>,
140    pub(crate) masking_nonce: Zeroizing<GenericArray<u8, NonceLen>>,
141    pub(crate) masked_response: MaskedResponse<CS>,
142    #[cfg(test)]
143    pub(crate) oprf_key: Zeroizing<GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>>,
144    pub(crate) ke2_builder: <CS::KeyExchange as KeyExchange>::KE2Builder<'a, CS>,
145}
146
147impl<CS: CipherSuite, SK: Clone> ServerLoginBuilder<'_, CS, SK> {
148    /// The returned data here has to be processed and the result given as an
149    /// input to [`ServerLoginBuilder::build()`]. To understand what kind of
150    /// output is expected here and how to process it, refer to the
151    /// documentation of your chosen [`CipherSuite::KeyExchange`].
152    pub fn data(&self) -> <CS::KeyExchange as KeyExchange>::KE2BuilderData<'_, CS> {
153        CS::KeyExchange::ke2_builder_data(&self.ke2_builder)
154    }
155
156    /// The handle to the corresponding [`ServerSetup`]s private key.
157    pub fn private_key(&self) -> &SK {
158        &self.server_s_sk
159    }
160
161    /// Build [`ServerLogin`] after attaining the input for the key exchange. To
162    /// understand what kind of input is expected here, refer to the
163    /// documentation of your chosen [`CipherSuite::KeyExchange`].
164    ///
165    /// See [`ServerLogin::start()`] for the regular path.
166    pub fn build(
167        self,
168        input: <CS::KeyExchange as KeyExchange>::KE2BuilderInput<CS>,
169    ) -> Result<ServerLoginStartResult<CS>, ProtocolError> {
170        ServerLogin::build(self, input)
171    }
172}
173
174/// The answer sent by the server to the user, upon reception of the login
175/// attempt
176#[cfg_attr(
177    feature = "serde",
178    derive(serde::Deserialize, serde::Serialize),
179    serde(bound(
180        deserialize = "<CS::KeyExchange as KeyExchange>::KE2Message: serde::Deserialize<'de>",
181        serialize = "<CS::KeyExchange as KeyExchange>::KE2Message: serde::Serialize"
182    ))
183)]
184#[derive_where(Clone)]
185#[derive_where(
186    Debug, Eq, Hash, PartialEq;
187    voprf::EvaluationElement<CS::OprfCs>,
188    <CS::KeyExchange as KeyExchange>::KE2Message,
189)]
190pub struct CredentialResponse<CS: CipherSuite> {
191    /// the server's oprf output
192    pub(crate) evaluation_element: voprf::EvaluationElement<CS::OprfCs>,
193    pub(crate) masking_nonce: GenericArray<u8, NonceLen>,
194    pub(crate) masked_response: MaskedResponse<CS>,
195    pub(crate) ke2_message: <CS::KeyExchange as KeyExchange>::KE2Message,
196}
197
198/// The answer sent by the client to the server, upon reception of the sealed
199/// envelope
200#[cfg_attr(
201    feature = "serde",
202    derive(serde::Deserialize, serde::Serialize),
203    serde(bound(
204        deserialize = "<CS::KeyExchange as KeyExchange>::KE3Message: serde::Deserialize<'de>",
205        serialize = "<CS::KeyExchange as KeyExchange>::KE3Message: serde::Serialize"
206    ))
207)]
208#[derive_where(Clone)]
209#[derive_where(
210    Debug, Eq, Hash, PartialEq;
211    <CS::KeyExchange as KeyExchange>::KE3Message,
212)]
213pub struct CredentialFinalization<CS: CipherSuite> {
214    pub(crate) ke3_message: <CS::KeyExchange as KeyExchange>::KE3Message,
215}
216
217////////////////////////////////
218// High-level Implementations //
219// ========================== //
220////////////////////////////////
221
222/// Length of [`RegistrationRequest`] in bytes for serialization.
223pub type RegistrationRequestLen<CS: CipherSuite> = <OprfGroup<CS> as voprf::Group>::ElemLen;
224
225impl<CS: CipherSuite> RegistrationRequest<CS> {
226    /// Only used for testing purposes
227    #[cfg(test)]
228    pub(crate) fn get_blinded_element_for_testing(&self) -> voprf::BlindedElement<CS::OprfCs> {
229        self.blinded_element.clone()
230    }
231
232    /// Serialization into bytes
233    pub fn serialize(&self) -> GenericArray<u8, RegistrationRequestLen<CS>> {
234        <OprfGroup<CS> as voprf::Group>::serialize_elem(self.blinded_element.value())
235    }
236
237    /// Deserialization from bytes
238    pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
239        Ok(Self {
240            blinded_element: voprf::BlindedElement::deserialize(input)?,
241        })
242    }
243}
244
245/// Length of [`RegistrationResponse`] in bytes for serialization.
246pub type RegistrationResponseLen<CS: CipherSuite> =
247    Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, <KeGroup<CS> as Group>::PkLen>;
248
249impl<CS: CipherSuite> RegistrationResponse<CS> {
250    /// Serialization into bytes
251    pub fn serialize(&self) -> GenericArray<u8, RegistrationResponseLen<CS>>
252    where
253        // RegistrationResponse: KgPk + KePk
254        <OprfGroup<CS> as voprf::Group>::ElemLen: Add<<KeGroup<CS> as Group>::PkLen>,
255        RegistrationResponseLen<CS>: ArrayLength<u8>,
256    {
257        <OprfGroup<CS> as voprf::Group>::serialize_elem(self.evaluation_element.value())
258            .concat(self.server_s_pk.serialize())
259    }
260
261    /// Deserialization from bytes
262    pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError> {
263        let evaluation_element = EvaluationElement::deserialize(input)?;
264        input = &input[EvaluationElementLen::<CS::OprfCs>::USIZE..];
265
266        Ok(Self {
267            evaluation_element,
268            server_s_pk: PublicKey::deserialize_take(&mut input)?,
269        })
270    }
271
272    #[cfg(test)]
273    /// Only used for tests, where we can set the beta value to test for the
274    /// reflection error case
275    pub(crate) fn set_evaluation_element_for_testing(
276        &self,
277        beta: <OprfGroup<CS> as voprf::Group>::Elem,
278    ) -> Self {
279        Self {
280            evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta),
281            server_s_pk: self.server_s_pk.clone(),
282        }
283    }
284}
285
286/// Length of [`RegistrationUpload`] in bytes for serialization.
287pub type RegistrationUploadLen<CS: CipherSuite> =
288    Sum<Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>, EnvelopeLen<CS>>;
289
290impl<CS: CipherSuite> RegistrationUpload<CS> {
291    /// Serialization into bytes
292    pub fn serialize(&self) -> GenericArray<u8, RegistrationUploadLen<CS>>
293    where
294        // RegistrationUpload: (KePk + Hash) + Envelope
295        <KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
296        Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
297            ArrayLength<u8> + Add<EnvelopeLen<CS>>,
298        RegistrationUploadLen<CS>: ArrayLength<u8>,
299    {
300        self.client_s_pk
301            .serialize()
302            .concat(self.masking_key.clone())
303            .concat(self.envelope.serialize())
304    }
305
306    /// Deserialization from bytes
307    pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError> {
308        Ok(Self {
309            client_s_pk: PublicKey::deserialize_take(&mut input)?,
310            masking_key: input.take_array("masking key")?,
311            envelope: Envelope::deserialize_take(&mut input)?,
312        })
313    }
314
315    // Creates a dummy instance used for faking a [CredentialResponse]
316    pub(crate) fn dummy<R: RngCore + CryptoRng, SK: Clone, OS: Clone>(
317        rng: &mut R,
318        server_setup: &ServerSetup<CS, SK, OS>,
319    ) -> Self {
320        let mut masking_key = Output::<OprfHash<CS>>::default();
321        rng.fill_bytes(&mut masking_key);
322
323        Self {
324            envelope: Envelope::<CS>::dummy(),
325            masking_key,
326            client_s_pk: server_setup.dummy_pk.clone(),
327        }
328    }
329}
330
331/// Length of [`CredentialRequest`] in bytes for serialization.
332pub type CredentialRequestLen<CS: CipherSuite> =
333    Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, Ke1MessageLen<CS>>;
334
335impl<CS: CipherSuite> CredentialRequest<CS> {
336    /// Serialization into bytes
337    pub fn serialize(&self) -> GenericArray<u8, CredentialRequestLen<CS>>
338    where
339        <CS::KeyExchange as KeyExchange>::KE1Message: Serialize,
340        // CredentialRequest: KgPk + Ke1Message
341        <OprfGroup<CS> as voprf::Group>::ElemLen: Add<Ke1MessageLen<CS>>,
342        CredentialRequestLen<CS>: ArrayLength<u8>,
343    {
344        <OprfGroup<CS> as voprf::Group>::serialize_elem(self.blinded_element.value())
345            .concat(self.ke1_message.serialize())
346    }
347
348    /// Deserialization from bytes
349    pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError>
350    where
351        <CS::KeyExchange as KeyExchange>::KE1Message: Deserialize,
352    {
353        Self::deserialize_take(&mut input)
354    }
355
356    pub(crate) fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError>
357    where
358        <CS::KeyExchange as KeyExchange>::KE1Message: Deserialize,
359    {
360        let blinded_element = BlindedElement::deserialize(input)?;
361        *input = &input[BlindedElementLen::<CS::OprfCs>::USIZE..];
362
363        Ok(Self {
364            blinded_element,
365            ke1_message: <CS::KeyExchange as KeyExchange>::KE1Message::deserialize_take(input)?,
366        })
367    }
368
369    pub(crate) fn to_parts(&self) -> SerializedCredentialRequest<CS> {
370        SerializedCredentialRequest::new(&self.blinded_element)
371    }
372
373    /// Only used for testing purposes
374    #[cfg(test)]
375    pub(crate) fn get_blinded_element_for_testing(&self) -> voprf::BlindedElement<CS::OprfCs> {
376        self.blinded_element.clone()
377    }
378}
379
380/// Length of [`CredentialResponse`] in bytes for serialization.
381pub type CredentialResponseLen<CS: CipherSuite> =
382    Sum<CredentialResponseWithoutKeLen<CS>, Ke2MessageLen<CS>>;
383
384pub(crate) type CredentialResponseWithoutKeLen<CS: CipherSuite> =
385    Sum<Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>, MaskedResponseLen<CS>>;
386
387impl<CS: CipherSuite> CredentialResponse<CS> {
388    /// Serialization into bytes
389    pub fn serialize(&self) -> GenericArray<u8, CredentialResponseLen<CS>>
390    where
391        <CS::KeyExchange as KeyExchange>::KE2Message: Serialize,
392        // CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
393        <OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
394        Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
395            ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
396        CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
397        // CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
398        CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
399        CredentialResponseLen<CS>: ArrayLength<u8>,
400    {
401        <OprfGroup<CS> as voprf::Group>::serialize_elem(self.evaluation_element.value())
402            .concat(self.masking_nonce)
403            .concat(self.masked_response.serialize())
404            .concat(self.ke2_message.serialize())
405    }
406
407    /// Deserialization from bytes
408    pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError>
409    where
410        <CS::KeyExchange as KeyExchange>::KE2Message: Deserialize,
411    {
412        let evaluation_element = EvaluationElement::deserialize(input)?;
413        input = &input[voprf::EvaluationElementLen::<CS::OprfCs>::USIZE..];
414
415        Ok(Self {
416            evaluation_element,
417            masking_nonce: input.take_array("masking nonce")?,
418            masked_response: MaskedResponse::deserialize_take(&mut input)?,
419            ke2_message: <CS::KeyExchange as KeyExchange>::KE2Message::deserialize_take(
420                &mut input,
421            )?,
422        })
423    }
424
425    pub(crate) fn to_parts(&self) -> SerializedCredentialResponse<CS> {
426        SerializedCredentialResponse::new(
427            &self.evaluation_element,
428            self.masking_nonce,
429            self.masked_response.clone(),
430        )
431    }
432
433    #[cfg(test)]
434    /// Only used for tests, where we can set the beta value to test for the
435    /// reflection error case
436    pub(crate) fn set_evaluation_element_for_testing(
437        &self,
438        beta: <OprfGroup<CS> as voprf::Group>::Elem,
439    ) -> Self {
440        Self {
441            evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta),
442            masking_nonce: self.masking_nonce,
443            masked_response: self.masked_response.clone(),
444            ke2_message: self.ke2_message.clone(),
445        }
446    }
447}
448
449/// Length of [`CredentialFinalization`] in bytes for serialization.
450pub type CredentialFinalizationLen<CS: CipherSuite> = Ke3MessageLen<CS>;
451
452impl<CS: CipherSuite> CredentialFinalization<CS> {
453    /// Serialization into bytes
454    pub fn serialize(&self) -> GenericArray<u8, CredentialFinalizationLen<CS>>
455    where
456        <CS::KeyExchange as KeyExchange>::KE3Message: Serialize,
457    {
458        self.ke3_message.serialize()
459    }
460
461    /// Deserialization from bytes
462    pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError>
463    where
464        <CS::KeyExchange as KeyExchange>::KE3Message: Deserialize,
465    {
466        Ok(Self {
467            ke3_message: <CS::KeyExchange as KeyExchange>::KE3Message::deserialize_take(
468                &mut input,
469            )?,
470        })
471    }
472}