// 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. //! Contains the messages used for OPAQUE use core::ops::Add; use derive_where::derive_where; use digest::Output; use generic_array::sequence::Concat; use generic_array::typenum::{Sum, Unsigned}; use generic_array::{ArrayLength, GenericArray}; use rand::{CryptoRng, RngCore}; use subtle::ConstantTimeEq; use voprf::Group; use zeroize::Zeroizing; use crate::ciphersuite::{CipherSuite, OprfGroup, OprfHash}; use crate::envelope::{Envelope, EnvelopeLen}; use crate::errors::utils::{check_slice_size, check_slice_size_atleast}; use crate::errors::ProtocolError; use crate::hash::OutputSize; use crate::key_exchange::group::KeGroup; use crate::key_exchange::traits::{ Deserialize, Ke1MessageLen, Ke2MessageLen, Ke3MessageLen, KeyExchange, Serialize, }; use crate::key_exchange::tripledh::NonceLen; use crate::keypair::PublicKey; use crate::opaque::{ MaskedResponse, MaskedResponseLen, ServerLogin, ServerLoginStartResult, ServerSetup, }; //////////////////////////// // High-level API Structs // // ====================== // //////////////////////////// /// The message sent by the client to the server, to initiate registration #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound = "") )] #[derive_where(Clone)] #[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; voprf::BlindedElement)] pub struct RegistrationRequest { /// blinded password information pub(crate) blinded_element: voprf::BlindedElement, } /// The answer sent by the server to the user, upon reception of the /// registration attempt #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound = "") )] #[derive_where(Clone)] #[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; voprf::EvaluationElement, ::Pk)] pub struct RegistrationResponse { /// The server's oprf output pub(crate) evaluation_element: voprf::EvaluationElement, /// Server's static public key pub(crate) server_s_pk: PublicKey, } /// The final message from the client, containing sealed cryptographic /// identifiers #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound = "") )] #[derive_where(Clone, ZeroizeOnDrop)] #[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Pk)] pub struct RegistrationUpload { /// The "envelope" generated by the user, containing sealed cryptographic /// identifiers pub(crate) envelope: Envelope, /// The masking key used to mask the envelope pub(crate) masking_key: Output>, /// The user's public key pub(crate) client_s_pk: PublicKey, } /// The message sent by the user to the server, to initiate registration #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound( deserialize = ", CS::KeGroup>>::KE1Message: \ serde::Deserialize<'de>", serialize = ", CS::KeGroup>>::KE1Message: \ serde::Serialize" )) )] #[derive_where(Clone, ZeroizeOnDrop)] #[derive_where( Debug, Eq, Hash, PartialEq; voprf::BlindedElement, , CS::KeGroup>>::KE1Message, )] pub struct CredentialRequest { pub(crate) blinded_element: voprf::BlindedElement, pub(crate) ke1_message: , CS::KeGroup>>::KE1Message, } /// Builder for [`ServerLogin`](crate::ServerLogin) when using remote keys. #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound( deserialize = "SK: serde::Deserialize<'de>, , \ CS::KeGroup>>::KE2Builder: serde::Deserialize<'de>", serialize = "SK: serde::Serialize, , \ CS::KeGroup>>::KE2Builder: serde::Serialize" )) )] #[derive_where(Clone)] #[derive_where( Debug, Eq, PartialEq; SK, voprf::EvaluationElement, , CS::KeGroup>>::KE2Builder, )] pub struct ServerLoginBuilder { pub(crate) server_s_sk: SK, pub(crate) evaluation_element: voprf::EvaluationElement, pub(crate) masking_nonce: Zeroizing>, pub(crate) masked_response: MaskedResponse, #[cfg(test)] pub(crate) oprf_key: Zeroizing as Group>::ScalarLen>>, pub(crate) ke2_builder: , CS::KeGroup>>::KE2Builder, } impl ServerLoginBuilder { /// The returned data here has to be processed and the result given as an /// input to [`ServerLoginBuilder::build()`]. To understand what kind of /// output is expected here and how to process it, refer to the /// documentation of your chosen [`CipherSuite::KeyExchange`]. pub fn data( &self, ) -> , CS::KeGroup>>::KE2BuilderData<'_> { CS::KeyExchange::ke2_builder_data(&self.ke2_builder) } /// The handle to the corresponding [`ServerSetup`]s private key. pub fn private_key(&self) -> &SK { &self.server_s_sk } /// Build [`ServerLogin`] after attaining the input for the key exchange. To /// understand what kind of input is expected here, refer to the /// documentation of your chosen [`CipherSuite::KeyExchange`]. /// /// See [`ServerLogin::start()`] for the regular path. pub fn build( self, input: , CS::KeGroup>>::KE2BuilderInput, ) -> Result, ProtocolError> { ServerLogin::build(self, input) } } /// The answer sent by the server to the user, upon reception of the login /// attempt #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound( deserialize = ", CS::KeGroup>>::KE2Message: \ serde::Deserialize<'de>", serialize = ", CS::KeGroup>>::KE2Message: \ serde::Serialize" )) )] #[derive_where(Clone)] #[derive_where( Debug, Eq, Hash, PartialEq; voprf::EvaluationElement, , CS::KeGroup>>::KE2Message, )] pub struct CredentialResponse { /// the server's oprf output pub(crate) evaluation_element: voprf::EvaluationElement, pub(crate) masking_nonce: GenericArray, pub(crate) masked_response: MaskedResponse, pub(crate) ke2_message: , CS::KeGroup>>::KE2Message, } /// The answer sent by the client to the server, upon reception of the sealed /// envelope #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound( deserialize = ", CS::KeGroup>>::KE3Message: \ serde::Deserialize<'de>", serialize = ", CS::KeGroup>>::KE3Message: \ serde::Serialize" )) )] #[derive_where(Clone)] #[derive_where( Debug, Eq, Hash, PartialEq; , CS::KeGroup>>::KE3Message, )] pub struct CredentialFinalization { pub(crate) ke3_message: , CS::KeGroup>>::KE3Message, } //////////////////////////////// // High-level Implementations // // ========================== // //////////////////////////////// /// Length of [`RegistrationRequest`] in bytes for serialization. pub type RegistrationRequestLen = as Group>::ElemLen; impl RegistrationRequest { /// Only used for testing purposes #[cfg(test)] pub fn get_blinded_element_for_testing(&self) -> voprf::BlindedElement { self.blinded_element.clone() } /// Serialization into bytes pub fn serialize(&self) -> GenericArray> { as Group>::serialize_elem(self.blinded_element.value()) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { Ok(Self { blinded_element: voprf::BlindedElement::deserialize(input)?, }) } } /// Length of [`RegistrationResponse`] in bytes for serialization. pub type RegistrationResponseLen = Sum< as Group>::ElemLen, ::PkLen>; impl RegistrationResponse { /// Serialization into bytes pub fn serialize(&self) -> GenericArray> where // RegistrationResponse: KgPk + KePk as Group>::ElemLen: Add<::PkLen>, RegistrationResponseLen: ArrayLength, { as Group>::serialize_elem(self.evaluation_element.value()) .concat(self.server_s_pk.serialize()) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let elem_len = as Group>::ElemLen::USIZE; let key_len = ::PkLen::USIZE; let checked_slice = check_slice_size(input, elem_len + key_len, "registration_response_bytes")?; // Ensure that public key is valid let server_s_pk = PublicKey::deserialize(&checked_slice[elem_len..])?; Ok(Self { evaluation_element: voprf::EvaluationElement::deserialize(&checked_slice[..elem_len])?, server_s_pk, }) } #[cfg(test)] /// Only used for tests, where we can set the beta value to test for the /// reflection error case pub fn set_evaluation_element_for_testing(&self, beta: as Group>::Elem) -> Self { Self { evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta), server_s_pk: self.server_s_pk.clone(), } } } /// Length of [`RegistrationUpload`] in bytes for serialization. pub type RegistrationUploadLen = Sum::PkLen, OutputSize>>, EnvelopeLen>; impl RegistrationUpload { /// Serialization into bytes pub fn serialize(&self) -> GenericArray> where // Envelope: Nonce + Hash NonceLen: Add>>, EnvelopeLen: ArrayLength, // RegistrationUpload: (KePk + Hash) + Envelope ::PkLen: Add>>, Sum<::PkLen, OutputSize>>: ArrayLength + Add>, RegistrationUploadLen: ArrayLength, { self.client_s_pk .serialize() .concat(self.masking_key.clone()) .concat(self.envelope.serialize()) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let key_len = ::PkLen::USIZE; let hash_len = OutputSize::>::USIZE; let checked_slice = check_slice_size_atleast(input, key_len + hash_len, "registration_upload_bytes")?; let envelope = Envelope::::deserialize(&checked_slice[key_len + hash_len..])?; Ok(Self { envelope, masking_key: GenericArray::clone_from_slice( &checked_slice[key_len..key_len + hash_len], ), client_s_pk: PublicKey::deserialize(&checked_slice[..key_len])?, }) } // Creates a dummy instance used for faking a [CredentialResponse] pub(crate) fn dummy( rng: &mut R, server_setup: &ServerSetup, ) -> Self { let mut masking_key = Output::>::default(); rng.fill_bytes(&mut masking_key); Self { envelope: Envelope::::dummy(), masking_key, client_s_pk: server_setup.fake_keypair.public().clone(), } } } /// Length of [`CredentialRequest`] in bytes for serialization. pub type CredentialRequestLen = Sum< as Group>::ElemLen, Ke1MessageLen>; impl CredentialRequest { /// Serialization into bytes pub fn serialize(&self) -> GenericArray> where // CredentialRequest: KgPk + Ke1Message as Group>::ElemLen: Add>, CredentialRequestLen: ArrayLength, { as Group>::serialize_elem(self.blinded_element.value()) .concat(self.ke1_message.serialize()) } pub(crate) fn serialize_iter<'a>( blinded_element: &'a GenericArray as Group>::ElemLen>, ke1_message: &'a GenericArray>, ) -> impl Iterator { [blinded_element.as_slice(), ke1_message].into_iter() } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let elem_len = as Group>::ElemLen::USIZE; let checked_slice = check_slice_size_atleast(input, elem_len, "login_first_message_bytes")?; // Check that the message is actually containing an element of the correct // subgroup let blinded_element = voprf::BlindedElement::::deserialize(&checked_slice[..elem_len])?; // Throw an error if the identity group element is encountered if bool::from( as Group>::identity_elem().ct_eq(&blinded_element.value())) { return Err(ProtocolError::IdentityGroupElementError); } let ke1_message = , CS::KeGroup>>::KE1Message::deserialize( &checked_slice[elem_len..], )?; Ok(Self { blinded_element, ke1_message, }) } /// Only used for testing purposes #[cfg(test)] pub fn get_blinded_element_for_testing(&self) -> voprf::BlindedElement { self.blinded_element.clone() } } /// Length of [`CredentialResponse`] in bytes for serialization. pub type CredentialResponseLen = Sum, Ke2MessageLen>; pub(crate) type CredentialResponseWithoutKeLen = Sum as Group>::ElemLen, NonceLen>, MaskedResponseLen>; impl CredentialResponse { /// Serialization into bytes pub fn serialize(&self) -> GenericArray> where // CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse as Group>::ElemLen: Add, Sum< as Group>::ElemLen, NonceLen>: ArrayLength + Add>, CredentialResponseWithoutKeLen: ArrayLength, // MaskedResponse: (Nonce + Hash) + KePk NonceLen: Add>>, Sum>>: ArrayLength + Add<::PkLen>, MaskedResponseLen: ArrayLength, // CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message CredentialResponseWithoutKeLen: Add>, CredentialResponseLen: ArrayLength, { as Group>::serialize_elem(self.evaluation_element.value()) .concat(self.masking_nonce) .concat(self.masked_response.serialize()) .concat(self.ke2_message.serialize()) } pub(crate) fn serialize_without_ke<'a>( beta: &'a GenericArray as Group>::ElemLen>, masking_nonce: &'a GenericArray, masked_response: &'a MaskedResponse, ) -> impl Iterator { [beta.as_slice(), masking_nonce.as_slice()] .into_iter() .chain(masked_response.iter()) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let elem_len = as Group>::ElemLen::USIZE; let key_len = ::PkLen::USIZE; let nonce_len = NonceLen::USIZE; let envelope_len = Envelope::::len(); let masked_response_len = key_len + envelope_len; let ke2_message_len = Ke2MessageLen::::USIZE; let checked_slice = check_slice_size_atleast( input, elem_len + nonce_len + masked_response_len + ke2_message_len, "credential_response_bytes", )?; // Check that the message is actually containing an element of the correct // subgroup let beta_bytes = &checked_slice[..elem_len]; let evaluation_element = voprf::EvaluationElement::::deserialize(beta_bytes)?; // Throw an error if the identity group element is encountered if bool::from( as Group>::identity_elem().ct_eq(&evaluation_element.value())) { return Err(ProtocolError::IdentityGroupElementError); } let masking_nonce = GenericArray::clone_from_slice(&checked_slice[elem_len..elem_len + nonce_len]); let masked_response = MaskedResponse::deserialize( &checked_slice[elem_len + nonce_len..elem_len + nonce_len + masked_response_len], ); let ke2_message = , CS::KeGroup>>::KE2Message::deserialize( &checked_slice[elem_len + nonce_len + masked_response_len..], )?; Ok(Self { evaluation_element, masking_nonce, masked_response, ke2_message, }) } #[cfg(test)] /// Only used for tests, where we can set the beta value to test for the /// reflection error case pub fn set_evaluation_element_for_testing(&self, beta: as Group>::Elem) -> Self { Self { evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta), masking_nonce: self.masking_nonce, masked_response: self.masked_response.clone(), ke2_message: self.ke2_message.clone(), } } } /// Length of [`CredentialFinalization`] in bytes for serialization. pub type CredentialFinalizationLen = Ke3MessageLen; impl CredentialFinalization { /// Serialization into bytes pub fn serialize(&self) -> GenericArray> { self.ke3_message.serialize() } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let ke3_message = , CS::KeGroup>>::KE3Message::deserialize( input, )?; Ok(Self { ke3_message }) } }