Files
opaque-vx/src/opaque.rs
T

1142 lines
40 KiB
Rust
Raw Normal View History

2023-05-22 23:04:26 -07:00
// Copyright (c) Meta Platforms, Inc. and affiliates.
2020-06-05 09:35:14 -07:00
//
2023-05-22 23:04:26 -07:00
// 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
2021-12-03 14:38:11 -08:00
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
2023-05-22 23:04:26 -07:00
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
2020-06-05 09:35:14 -07:00
//! Provides the main OPAQUE API
use core::ops::{Add, Deref};
2022-01-06 06:19:02 +01:00
2022-02-25 07:13:22 +01:00
use derive_where::derive_where;
2025-04-15 22:31:37 +02:00
use digest::Output;
2022-01-04 00:50:40 +01:00
use generic_array::sequence::Concat;
2025-04-15 22:31:37 +02:00
use generic_array::typenum::{Sum, Unsigned, U2};
2022-01-06 06:19:02 +01:00
use generic_array::{ArrayLength, GenericArray};
2022-01-04 00:50:40 +01:00
use hkdf::{Hkdf, HkdfExtract};
2021-02-11 18:10:48 -08:00
use rand::{CryptoRng, RngCore};
2025-04-28 21:47:39 +02:00
use subtle::{Choice, ConstantTimeEq, CtOption};
2022-01-06 00:10:57 +01:00
use voprf::Group;
use zeroize::Zeroizing;
2020-06-05 09:35:14 -07:00
2022-02-25 07:13:22 +01:00
use crate::ciphersuite::{CipherSuite, OprfGroup, OprfHash};
2022-01-06 06:19:02 +01:00
use crate::envelope::{Envelope, EnvelopeLen};
use crate::errors::utils::check_slice_size;
use crate::errors::{InternalError, ProtocolError};
2025-04-15 22:31:37 +02:00
use crate::hash::OutputSize;
2022-01-06 06:19:02 +01:00
use crate::key_exchange::group::KeGroup;
use crate::key_exchange::traits::{
2022-04-02 01:10:00 +02:00
Deserialize, Ke1MessageLen, Ke1StateLen, Ke2StateLen, KeyExchange, Serialize,
2022-01-06 06:19:02 +01:00
};
use crate::key_exchange::tripledh::NonceLen;
use crate::keypair::{KeyPair, PrivateKey, PrivateKeySerialization, PublicKey};
2022-04-02 01:10:00 +02:00
use crate::ksf::Ksf;
2022-01-06 06:19:02 +01:00
use crate::messages::{CredentialRequestLen, RegistrationUploadLen};
2022-04-02 01:10:00 +02:00
use crate::serialization::Input;
2022-01-06 06:19:02 +01:00
use crate::{
CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest,
RegistrationResponse, RegistrationUpload, ServerLoginBuilder,
2022-01-06 06:19:02 +01:00
};
///////////////
// Constants //
// ========= //
///////////////
2021-10-25 02:54:32 -07:00
const STR_CREDENTIAL_RESPONSE_PAD: &[u8; 21] = b"CredentialResponsePad";
const STR_MASKING_KEY: &[u8; 10] = b"MaskingKey";
const STR_OPRF_KEY: &[u8; 7] = b"OprfKey";
const STR_OPAQUE_DERIVE_KEY_PAIR: &[u8; 20] = b"OPAQUE-DeriveKeyPair";
////////////////////////////
// High-level API Structs //
// ====================== //
////////////////////////////
/// The state elements the server holds upon setup
#[cfg_attr(
2022-01-04 00:50:40 +01:00
feature = "serde",
2022-04-02 01:10:00 +02:00
derive(serde::Deserialize, serde::Serialize),
2023-02-04 22:25:41 +01:00
serde(bound(
deserialize = "S: serde::Deserialize<'de>",
serialize = "S: serde::Serialize"
))
)]
2022-01-04 00:50:40 +01:00
#[derive_where(Clone)]
#[derive_where(Debug, Eq, PartialEq; <CS::KeGroup as KeGroup>::Pk, <CS::KeGroup as KeGroup>::Sk, S)]
pub struct ServerSetup<CS: CipherSuite, S: Clone = PrivateKey<<CS as CipherSuite>::KeGroup>> {
oprf_seed: Zeroizing<Output<OprfHash<CS>>>,
2021-08-04 21:24:46 +02:00
keypair: KeyPair<CS::KeGroup, S>,
pub(crate) fake_keypair: KeyPair<CS::KeGroup>,
}
/// The state elements the client holds to register itself
2022-04-02 01:10:00 +02:00
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
2023-02-04 22:25:41 +01:00
serde(bound = "")
2022-04-02 01:10:00 +02:00
)]
2022-02-25 07:13:22 +01:00
#[derive_where(Clone, ZeroizeOnDrop)]
2022-01-04 00:50:40 +01:00
#[derive_where(
Debug, Eq, Hash, PartialEq;
2022-04-17 16:23:31 -07:00
voprf::OprfClient<CS::OprfCs>,
2022-04-02 01:10:00 +02:00
voprf::BlindedElement<CS::OprfCs>,
2022-01-04 00:50:40 +01:00
)]
2025-04-15 22:31:37 +02:00
pub struct ClientRegistration<CS: CipherSuite> {
2022-04-17 16:23:31 -07:00
pub(crate) oprf_client: voprf::OprfClient<CS::OprfCs>,
2022-04-02 01:10:00 +02:00
pub(crate) blinded_element: voprf::BlindedElement<CS::OprfCs>,
}
/// The state elements the server holds to record a registration
2022-04-02 01:10:00 +02:00
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
2023-02-04 22:25:41 +01:00
serde(bound = "")
2022-04-02 01:10:00 +02:00
)]
2022-02-25 07:13:22 +01:00
#[derive_where(Clone, ZeroizeOnDrop)]
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::KeGroup as KeGroup>::Pk)]
2025-04-15 22:31:37 +02:00
pub struct ServerRegistration<CS: CipherSuite>(pub(crate) RegistrationUpload<CS>);
/// The state elements the client holds to perform a login
2022-04-02 01:10:00 +02:00
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
2023-02-04 22:25:41 +01:00
serde(bound(
deserialize = "<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1Message: \
serde::Deserialize<'de>, <CS::KeyExchange as KeyExchange<OprfHash<CS>, \
CS::KeGroup>>::KE1State: serde::Deserialize<'de>",
serialize = "<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1Message: \
serde::Serialize, <CS::KeyExchange as KeyExchange<OprfHash<CS>, \
CS::KeGroup>>::KE1State: serde::Serialize"
))
2022-04-02 01:10:00 +02:00
)]
2022-02-25 07:13:22 +01:00
#[derive_where(Clone, ZeroizeOnDrop)]
2022-01-04 00:50:40 +01:00
#[derive_where(
Debug, Eq, Hash, PartialEq;
2022-04-17 16:23:31 -07:00
voprf::OprfClient<CS::OprfCs>,
2022-02-25 07:13:22 +01:00
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1State,
2022-01-04 00:50:40 +01:00
CredentialRequest<CS>,
)]
2025-04-15 22:31:37 +02:00
pub struct ClientLogin<CS: CipherSuite> {
2022-04-17 16:23:31 -07:00
pub(crate) oprf_client: voprf::OprfClient<CS::OprfCs>,
2022-02-25 07:13:22 +01:00
pub(crate) ke1_state: <CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1State,
pub(crate) credential_request: CredentialRequest<CS>,
}
/// The state elements the server holds to record a login
2022-04-02 01:10:00 +02:00
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
2023-02-04 22:25:41 +01:00
serde(bound(
deserialize = "<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2State: \
serde::Deserialize<'de>",
serialize = "<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2State: \
serde::Serialize"
))
2022-04-02 01:10:00 +02:00
)]
2022-02-25 07:13:22 +01:00
#[derive_where(Clone, ZeroizeOnDrop)]
2022-01-04 00:50:40 +01:00
#[derive_where(
Debug, Eq, Hash, PartialEq;
2022-02-25 07:13:22 +01:00
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2State,
2022-01-04 00:50:40 +01:00
)]
2025-04-15 22:31:37 +02:00
pub struct ServerLogin<CS: CipherSuite> {
2022-02-25 07:13:22 +01:00
ke2_state: <CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2State,
}
////////////////////////////////
// High-level Implementations //
// ========================== //
////////////////////////////////
// Server Setup
// ============
2025-04-15 22:31:37 +02:00
impl<CS: CipherSuite> ServerSetup<CS, PrivateKey<CS::KeGroup>> {
/// Generate a new instance of server setup
2022-01-04 00:50:40 +01:00
pub fn new<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
let keypair = KeyPair::generate_random::<CS::OprfCs, _>(rng);
Self::new_with_key_pair(rng, keypair)
2021-07-20 14:16:53 +02:00
}
}
2022-01-04 00:50:40 +01:00
/// Length of [`ServerSetup`] in bytes for serialization.
pub type ServerSetupLen<CS: CipherSuite, S: PrivateKeySerialization<CS::KeGroup>> =
2022-02-25 07:13:22 +01:00
Sum<Sum<OutputSize<OprfHash<CS>>, S::Len>, <CS::KeGroup as KeGroup>::SkLen>;
2022-01-04 00:50:40 +01:00
impl<CS: CipherSuite, S: Clone> ServerSetup<CS, S> {
2021-07-20 14:16:53 +02:00
/// Create [`ServerSetup`] with the given keypair
///
/// This function should not be used to restore a previously-existing
2023-10-08 21:48:43 +02:00
/// instance of [`ServerSetup`]. Instead, use [`ServerSetup::serialize`] and
/// [`ServerSetup::deserialize`] for this purpose.
pub fn new_with_key_pair<R: CryptoRng + RngCore>(
2021-07-20 14:16:53 +02:00
rng: &mut R,
2021-08-04 21:24:46 +02:00
keypair: KeyPair<CS::KeGroup, S>,
2022-01-04 00:50:40 +01:00
) -> Self {
let mut oprf_seed = GenericArray::default();
rng.fill_bytes(&mut oprf_seed);
2022-01-04 00:50:40 +01:00
Self {
oprf_seed: Zeroizing::new(oprf_seed),
2021-07-20 14:16:53 +02:00
keypair,
fake_keypair: KeyPair::<CS::KeGroup>::generate_random::<CS::OprfCs, _>(rng),
2022-01-04 00:50:40 +01:00
}
}
/// Serialization into bytes
2022-01-04 00:50:40 +01:00
pub fn serialize(&self) -> GenericArray<u8, ServerSetupLen<CS, S>>
where
S: PrivateKeySerialization<CS::KeGroup>,
2022-01-04 00:50:40 +01:00
// ServerSetup: Hash + KeSk + KeSk
2022-02-25 07:13:22 +01:00
OutputSize<OprfHash<CS>>: Add<S::Len>,
Sum<OutputSize<OprfHash<CS>>, S::Len>:
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::SkLen>,
2022-01-04 00:50:40 +01:00
ServerSetupLen<CS, S>: ArrayLength<u8>,
{
self.oprf_seed
.deref()
2022-01-04 00:50:40 +01:00
.clone()
.concat(S::serialize_key_pair(&self.keypair))
2022-02-25 07:13:22 +01:00
.concat(self.fake_keypair.private().serialize())
}
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError<S::Error>>
where
S: PrivateKeySerialization<CS::KeGroup>,
{
2022-02-25 07:13:22 +01:00
let seed_len = OutputSize::<OprfHash<CS>>::USIZE;
2021-10-25 02:54:32 -07:00
let key_len = <CS::KeGroup as KeGroup>::SkLen::USIZE;
let checked_slice = check_slice_size(input, seed_len + key_len + key_len, "server_setup")
.map_err(ProtocolError::into_custom)?;
Ok(Self {
oprf_seed: Zeroizing::new(GenericArray::clone_from_slice(&checked_slice[..seed_len])),
keypair: S::deserialize_key_pair(&checked_slice[seed_len..seed_len + key_len])?,
fake_keypair: PrivateKey::deserialize_key_pair(&checked_slice[seed_len + key_len..])
2021-07-20 11:49:37 +02:00
.map_err(ProtocolError::into_custom)?,
})
}
/// Returns the keypair
2021-08-04 21:24:46 +02:00
pub fn keypair(&self) -> &KeyPair<CS::KeGroup, S> {
&self.keypair
}
}
2020-06-05 09:35:14 -07:00
// Registration
// ============
2022-01-06 00:10:57 +01:00
pub(crate) type ClientRegistrationLen<CS: CipherSuite> =
2022-02-25 07:13:22 +01:00
Sum<<OprfGroup<CS> as Group>::ScalarLen, <OprfGroup<CS> as Group>::ElemLen>;
2022-01-06 00:10:57 +01:00
2025-04-15 22:31:37 +02:00
impl<CS: CipherSuite> ClientRegistration<CS> {
/// Serialization into bytes
2022-01-06 00:10:57 +01:00
pub fn serialize(&self) -> GenericArray<u8, ClientRegistrationLen<CS>>
where
// ClientRegistration: KgSk + KgPk
2022-02-25 07:13:22 +01:00
<OprfGroup<CS> as Group>::ScalarLen: Add<<OprfGroup<CS> as Group>::ElemLen>,
2022-01-06 00:10:57 +01:00
ClientRegistrationLen<CS>: ArrayLength<u8>,
{
self.oprf_client
.serialize()
.concat(self.blinded_element.serialize())
}
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
2022-02-25 07:13:22 +01:00
let client_len = <OprfGroup<CS> as Group>::ScalarLen::USIZE;
let element_len = <OprfGroup<CS> as Group>::ElemLen::USIZE;
2022-01-06 00:10:57 +01:00
let checked_slice =
check_slice_size(input, client_len + element_len, "client_registration")?;
2021-07-12 12:33:19 -07:00
2020-06-05 09:35:14 -07:00
Ok(Self {
2022-04-17 16:23:31 -07:00
oprf_client: voprf::OprfClient::deserialize(&checked_slice[..client_len])?,
2022-01-06 00:10:57 +01:00
blinded_element: voprf::BlindedElement::deserialize(&checked_slice[client_len..])?,
2020-06-05 09:35:14 -07:00
})
}
2021-10-25 02:54:32 -07:00
/// Only used for testing zeroize
2022-01-04 00:50:40 +01:00
#[cfg(test)]
2022-01-06 00:10:57 +01:00
pub(crate) fn to_vec(&self) -> std::vec::Vec<u8> {
2022-01-04 00:50:40 +01:00
[
2022-01-06 00:10:57 +01:00
self.oprf_client.serialize().to_vec(),
self.blinded_element.serialize().to_vec(),
]
2022-01-04 00:50:40 +01:00
.concat()
}
2021-06-15 00:36:17 +02:00
2022-01-06 06:19:02 +01:00
/// Returns an initial "blinded" request to send to the server, as well as a
2023-10-08 21:48:43 +02:00
/// [`ClientRegistration`]
2020-06-05 09:35:14 -07:00
pub fn start<R: RngCore + CryptoRng>(
2020-12-12 21:53:33 -08:00
blinding_factor_rng: &mut R,
2020-06-05 09:35:14 -07:00
password: &[u8],
2020-12-12 21:53:33 -08:00
) -> Result<ClientRegistrationStartResult<CS>, ProtocolError> {
2021-10-25 02:54:32 -07:00
let blind_result = blind::<CS, _>(blinding_factor_rng, password)?;
2020-06-05 09:35:14 -07:00
2020-12-12 21:53:33 -08:00
Ok(ClientRegistrationStartResult {
2022-01-04 00:50:40 +01:00
message: RegistrationRequest {
2021-10-25 02:54:32 -07:00
blinded_element: blind_result.message.clone(),
},
state: Self {
oprf_client: blind_result.state,
blinded_element: blind_result.message,
},
2020-12-12 21:53:33 -08:00
})
2020-06-05 09:35:14 -07:00
}
2021-06-15 00:36:17 +02:00
2020-06-05 09:35:14 -07:00
/// "Unblinds" the server's answer and returns a final message containing
2022-01-06 06:19:02 +01:00
/// cryptographic identifiers, to be sent to the server on setup
/// finalization
pub fn finish<R: CryptoRng + RngCore>(
self,
rng: &mut R,
2022-01-06 00:10:57 +01:00
password: &[u8],
2021-10-25 02:54:32 -07:00
registration_response: RegistrationResponse<CS>,
2021-09-02 11:28:21 +02:00
params: ClientRegistrationFinishParameters<CS>,
) -> Result<ClientRegistrationFinishResult<CS>, ProtocolError> {
2021-07-12 12:33:19 -07:00
// Check for reflected value from server and halt if detected
2021-10-25 02:54:32 -07:00
if self
.blinded_element
.value()
.ct_eq(&registration_response.evaluation_element.value())
.into()
{
2021-07-12 12:33:19 -07:00
return Err(ProtocolError::ReflectedValueError);
}
2021-07-30 02:13:27 +02:00
#[cfg_attr(not(test), allow(unused_variables))]
2021-10-25 02:54:32 -07:00
let (randomized_pwd, randomized_pwd_hasher) = get_password_derived_key::<CS>(
2022-01-06 00:10:57 +01:00
password,
2021-10-25 02:54:32 -07:00
self.oprf_client.clone(),
registration_response.evaluation_element,
2022-04-02 01:10:00 +02:00
params.ksf,
2021-10-25 02:54:32 -07:00
)?;
2022-02-25 07:13:22 +01:00
let mut masking_key = Output::<OprfHash<CS>>::default();
2021-10-25 02:54:32 -07:00
randomized_pwd_hasher
.expand(STR_MASKING_KEY, &mut masking_key)
2021-08-22 12:28:19 -07:00
.map_err(|_| InternalError::HkdfError)?;
2021-09-02 11:28:21 +02:00
let result = Envelope::<CS>::seal(
rng,
2021-10-25 02:54:32 -07:00
randomized_pwd_hasher,
&registration_response.server_s_pk,
2021-09-02 11:28:21 +02:00
params.identifiers,
)?;
2020-06-05 09:35:14 -07:00
2020-12-12 21:53:33 -08:00
Ok(ClientRegistrationFinishResult {
message: RegistrationUpload {
2021-07-30 12:06:46 +02:00
envelope: result.0,
2022-01-04 00:50:40 +01:00
masking_key,
2021-07-30 12:06:46 +02:00
client_s_pk: result.1,
2020-06-05 09:35:14 -07:00
},
2021-07-30 12:06:46 +02:00
export_key: result.2,
2021-10-25 02:54:32 -07:00
server_s_pk: registration_response.server_s_pk,
#[cfg(test)]
state: self,
2021-07-30 00:31:12 +02:00
#[cfg(test)]
2021-07-30 12:06:46 +02:00
auth_key: result.3,
#[cfg(test)]
2021-07-30 02:13:27 +02:00
randomized_pwd,
2020-12-12 21:53:33 -08:00
})
2020-06-05 09:35:14 -07:00
}
}
2022-01-04 00:50:40 +01:00
/// Length of [`ServerRegistration`] in bytes for serialization.
pub type ServerRegistrationLen<CS> = RegistrationUploadLen<CS>;
2025-04-15 22:31:37 +02:00
impl<CS: CipherSuite> ServerRegistration<CS> {
/// Serialization into bytes
2022-01-04 00:50:40 +01:00
pub fn serialize(&self) -> GenericArray<u8, ServerRegistrationLen<CS>>
where
// Envelope: Nonce + Hash
2022-02-25 07:13:22 +01:00
NonceLen: Add<OutputSize<OprfHash<CS>>>,
2022-01-04 00:50:40 +01:00
EnvelopeLen<CS>: ArrayLength<u8>,
// RegistrationUpload: (KePk + Hash) + Envelope
2022-02-25 07:13:22 +01:00
<CS::KeGroup as KeGroup>::PkLen: Add<OutputSize<OprfHash<CS>>>,
Sum<<CS::KeGroup as KeGroup>::PkLen, OutputSize<OprfHash<CS>>>:
2022-01-04 00:50:40 +01:00
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength<u8>,
// ServerRegistration = RegistrationUpload
{
self.0.serialize()
}
2020-06-05 09:35:14 -07:00
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
Ok(Self(RegistrationUpload::deserialize(input)?))
2020-06-05 09:35:14 -07:00
}
2022-01-06 06:19:02 +01:00
/// From the client's "blinded" password, returns a response to be sent back
2023-10-08 21:48:43 +02:00
/// to the client, as well as a [`ServerRegistration`]
pub fn start<S: Clone>(
2021-07-20 14:16:53 +02:00
server_setup: &ServerSetup<CS, S>,
message: RegistrationRequest<CS>,
credential_identifier: &[u8],
2020-12-12 21:53:33 -08:00
) -> Result<ServerRegistrationStartResult<CS>, ProtocolError> {
2025-04-15 22:31:37 +02:00
let oprf_key = oprf_key_from_seed::<CS>(&server_setup.oprf_seed, credential_identifier)?;
2020-06-05 09:35:14 -07:00
2022-04-17 16:23:31 -07:00
let server = voprf::OprfServer::new_with_key(&oprf_key)?;
2022-12-11 06:30:05 +01:00
let evaluation_element = server.blind_evaluate(&message.blinded_element);
2020-06-05 09:35:14 -07:00
2020-12-12 21:53:33 -08:00
Ok(ServerRegistrationStartResult {
message: RegistrationResponse {
2022-02-25 07:13:22 +01:00
evaluation_element,
server_s_pk: server_setup.keypair.public().clone(),
2020-06-05 09:35:14 -07:00
},
2021-07-30 14:00:23 +02:00
#[cfg(test)]
2022-01-04 00:50:40 +01:00
oprf_key,
2020-12-12 21:53:33 -08:00
})
2020-06-05 09:35:14 -07:00
}
2022-01-06 06:19:02 +01:00
/// From the client's cryptographic identifiers, fully populates and returns
2023-10-08 21:48:43 +02:00
/// a [`ServerRegistration`]
pub fn finish(message: RegistrationUpload<CS>) -> Self {
Self(message)
}
// Creates a dummy instance used for faking a [CredentialResponse]
pub(crate) fn dummy<R: RngCore + CryptoRng, S: Clone>(
rng: &mut R,
2021-07-20 11:49:37 +02:00
server_setup: &ServerSetup<CS, S>,
) -> Self {
Self(RegistrationUpload::dummy(rng, server_setup))
2020-06-05 09:35:14 -07:00
}
}
// Login
// =====
2022-01-06 00:10:57 +01:00
pub(crate) type ClientLoginLen<CS: CipherSuite> =
2022-02-25 07:13:22 +01:00
Sum<Sum<<OprfGroup<CS> as Group>::ScalarLen, CredentialRequestLen<CS>>, Ke1StateLen<CS>>;
2022-01-06 00:10:57 +01:00
2025-04-15 22:31:37 +02:00
impl<CS: CipherSuite> ClientLogin<CS> {
/// Serialization into bytes
2022-01-06 00:10:57 +01:00
pub fn serialize(&self) -> GenericArray<u8, ClientLoginLen<CS>>
2022-01-04 00:50:40 +01:00
where
// CredentialRequest: KgPk + Ke1Message
2022-02-25 07:13:22 +01:00
<OprfGroup<CS> as Group>::ElemLen: Add<Ke1MessageLen<CS>>,
2022-01-04 00:50:40 +01:00
CredentialRequestLen<CS>: ArrayLength<u8>,
2022-01-06 00:10:57 +01:00
// ClientLogin: KgSk + CredentialRequest + Ke1State
2022-02-25 07:13:22 +01:00
<OprfGroup<CS> as Group>::ScalarLen: Add<CredentialRequestLen<CS>>,
Sum<<OprfGroup<CS> as Group>::ScalarLen, CredentialRequestLen<CS>>:
2022-01-06 00:10:57 +01:00
ArrayLength<u8> + Add<Ke1StateLen<CS>>,
ClientLoginLen<CS>: ArrayLength<u8>,
2022-01-04 00:50:40 +01:00
{
2022-01-06 00:10:57 +01:00
self.oprf_client
.serialize()
.concat(self.credential_request.serialize())
2022-04-02 01:10:00 +02:00
.concat(self.ke1_state.serialize())
}
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
2022-02-25 07:13:22 +01:00
let client_len = <OprfGroup<CS> as Group>::ScalarLen::USIZE;
let request_len = <OprfGroup<CS> as Group>::ElemLen::USIZE + Ke1MessageLen::<CS>::USIZE;
2022-01-06 00:10:57 +01:00
let state_len = Ke1StateLen::<CS>::USIZE;
let checked_slice =
check_slice_size(input, client_len + request_len + state_len, "client_login")?;
let ke1_state =
2022-04-02 01:10:00 +02:00
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1State::deserialize(
2022-01-06 00:10:57 +01:00
&checked_slice[client_len + request_len..],
)?;
2020-06-05 09:35:14 -07:00
Ok(Self {
2022-04-17 16:23:31 -07:00
oprf_client: voprf::OprfClient::deserialize(&checked_slice[..client_len])?,
2022-01-06 00:10:57 +01:00
credential_request: CredentialRequest::deserialize(
&checked_slice[client_len..client_len + request_len],
)?,
2020-06-05 09:35:14 -07:00
ke1_state,
})
}
}
2025-04-15 22:31:37 +02:00
impl<CS: CipherSuite> ClientLogin<CS> {
2022-01-06 06:19:02 +01:00
/// Returns an initial "blinded" password request to send to the server, as
2023-10-08 21:48:43 +02:00
/// well as a [`ClientLogin`]
2020-06-05 09:35:14 -07:00
pub fn start<R: RngCore + CryptoRng>(
rng: &mut R,
2020-12-12 21:53:33 -08:00
password: &[u8],
2020-11-16 14:05:43 -08:00
) -> Result<ClientLoginStartResult<CS>, ProtocolError> {
2021-10-25 02:54:32 -07:00
let blind_result = blind::<CS, _>(rng, password)?;
let (ke1_state, ke1_message) = CS::KeyExchange::generate_ke1::<CS::OprfCs, _>(rng)?;
2020-06-05 09:35:14 -07:00
2021-10-25 02:54:32 -07:00
let credential_request = CredentialRequest {
blinded_element: blind_result.message,
ke1_message,
};
2020-06-05 09:35:14 -07:00
2020-11-16 14:05:43 -08:00
Ok(ClientLoginStartResult {
2022-01-04 00:50:40 +01:00
message: credential_request.clone(),
state: Self {
2021-10-25 02:54:32 -07:00
oprf_client: blind_result.state,
ke1_state,
2022-01-04 00:50:40 +01:00
credential_request,
},
2020-11-16 14:05:43 -08:00
})
2020-06-05 09:35:14 -07:00
}
2022-01-06 06:19:02 +01:00
/// "Unblinds" the server's answer and returns the opened assets from the
/// server
2020-11-16 14:05:43 -08:00
pub fn finish(
2020-06-05 09:35:14 -07:00
self,
2022-01-06 00:10:57 +01:00
password: &[u8],
credential_response: CredentialResponse<CS>,
2021-09-02 11:28:21 +02:00
params: ClientLoginFinishParameters<CS>,
2022-01-04 00:50:40 +01:00
) -> Result<ClientLoginFinishResult<CS>, ProtocolError>
where
// MaskedResponse: (Nonce + Hash) + KePk
2022-02-25 07:13:22 +01:00
NonceLen: Add<OutputSize<OprfHash<CS>>>,
Sum<NonceLen, OutputSize<OprfHash<CS>>>:
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
2022-01-04 00:50:40 +01:00
MaskedResponseLen<CS>: ArrayLength<u8>,
{
2021-07-12 12:33:19 -07:00
// Check if beta value from server is equal to alpha value from client
2022-01-04 00:50:40 +01:00
if self
.credential_request
2021-10-25 02:54:32 -07:00
.blinded_element
.value()
.ct_eq(&credential_response.evaluation_element.value())
.into()
{
2021-07-12 12:33:19 -07:00
return Err(ProtocolError::ReflectedValueError);
}
2021-10-25 02:54:32 -07:00
let (_, randomized_pwd_hasher) = get_password_derived_key::<CS>(
2022-01-06 00:10:57 +01:00
password,
2021-10-25 02:54:32 -07:00
self.oprf_client.clone(),
credential_response.evaluation_element.clone(),
2022-04-02 01:10:00 +02:00
params.ksf,
)?;
2022-02-25 07:13:22 +01:00
let mut masking_key = Output::<OprfHash<CS>>::default();
2021-10-25 02:54:32 -07:00
randomized_pwd_hasher
.expand(STR_MASKING_KEY, &mut masking_key)
2021-08-22 12:28:19 -07:00
.map_err(|_| InternalError::HkdfError)?;
2020-06-05 09:35:14 -07:00
let (server_s_pk, envelope) = unmask_response::<CS>(
&masking_key,
&credential_response.masking_nonce,
&credential_response.masked_response,
)
.map_err(|e| match e {
2021-08-22 12:28:19 -07:00
ProtocolError::SerializationError => ProtocolError::InvalidLoginError,
err => err,
})?;
2022-01-04 00:50:40 +01:00
let opened_envelope = envelope
2021-09-02 11:28:21 +02:00
.open(
2021-10-25 02:54:32 -07:00
randomized_pwd_hasher,
2022-01-04 00:50:40 +01:00
server_s_pk.clone(),
params.identifiers,
2021-09-02 11:28:21 +02:00
)
.map_err(|e| match e {
2021-08-22 12:28:19 -07:00
ProtocolError::LibraryError(InternalError::SealOpenHmacError) => {
ProtocolError::InvalidLoginError
}
2021-07-08 11:04:32 -07:00
err => err,
})?;
2020-06-05 09:35:14 -07:00
2022-02-25 07:13:22 +01:00
let beta = OprfGroup::<CS>::serialize_elem(credential_response.evaluation_element.value());
let credential_response_component = CredentialResponse::<CS>::serialize_without_ke(
2022-01-04 00:50:40 +01:00
&beta,
&credential_response.masking_nonce,
&credential_response.masked_response,
);
2022-02-25 07:13:22 +01:00
let blinded_element =
OprfGroup::<CS>::serialize_elem(self.credential_request.blinded_element.value());
2022-04-02 01:10:00 +02:00
let ke1_message = self.credential_request.ke1_message.serialize();
2022-01-04 00:50:40 +01:00
let serialized_credential_request =
CredentialRequest::<CS>::serialize_iter(&blinded_element, &ke1_message);
2021-07-30 12:54:16 +02:00
let result = CS::KeyExchange::generate_ke3(
credential_response_component,
credential_response.ke2_message,
&self.ke1_state,
2022-01-04 00:50:40 +01:00
serialized_credential_request,
server_s_pk.clone(),
opened_envelope.client_static_keypair.private().clone(),
2022-01-04 00:50:40 +01:00
opened_envelope.id_u.iter(),
opened_envelope.id_s.iter(),
params.context.unwrap_or(&[]),
)?;
2020-11-16 14:05:43 -08:00
Ok(ClientLoginFinishResult {
2021-07-30 12:54:16 +02:00
message: CredentialFinalization {
ke3_message: result.1,
},
session_key: result.0,
2022-01-04 00:50:40 +01:00
export_key: opened_envelope.export_key,
server_s_pk,
#[cfg(test)]
state: self,
2021-07-30 12:54:16 +02:00
#[cfg(test)]
handshake_secret: result.2,
2021-07-30 13:51:02 +02:00
#[cfg(test)]
client_mac_key: result.3,
2020-11-16 14:05:43 -08:00
})
2020-06-05 09:35:14 -07:00
}
}
2025-04-15 22:31:37 +02:00
impl<CS: CipherSuite> ServerLogin<CS> {
/// Serialization into bytes
2022-01-04 00:50:40 +01:00
pub fn serialize(&self) -> GenericArray<u8, Ke2StateLen<CS>> {
2022-04-02 01:10:00 +02:00
self.ke2_state.serialize()
2020-06-05 09:35:14 -07:00
}
/// Deserialization from bytes
pub fn deserialize(bytes: &[u8]) -> Result<Self, ProtocolError> {
Ok(Self {
2021-08-04 21:24:46 +02:00
ke2_state:
2022-04-02 01:10:00 +02:00
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2State::deserialize(
2021-08-04 21:24:46 +02:00
bytes,
)?,
})
}
/// Create a [`ServerLoginBuilder`] to use with a remote private key.
///
/// See [`ServerLogin::start()`] for the regular path.
pub fn builder<R: RngCore + CryptoRng, S: Clone>(
2020-12-12 21:53:33 -08:00
rng: &mut R,
2021-07-20 11:49:37 +02:00
server_setup: &ServerSetup<CS, S>,
password_file: Option<ServerRegistration<CS>>,
2021-10-25 02:54:32 -07:00
credential_request: CredentialRequest<CS>,
credential_identifier: &[u8],
2022-01-04 00:50:40 +01:00
ServerLoginStartParameters {
context,
identifiers,
}: ServerLoginStartParameters,
) -> Result<ServerLoginBuilder<CS, S>, ProtocolError>
2022-01-04 00:50:40 +01:00
where
// MaskedResponse: (Nonce + Hash) + KePk
2022-02-25 07:13:22 +01:00
NonceLen: Add<OutputSize<OprfHash<CS>>>,
Sum<NonceLen, OutputSize<OprfHash<CS>>>:
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
2022-01-04 00:50:40 +01:00
MaskedResponseLen<CS>: ArrayLength<u8>,
{
2025-04-28 21:47:39 +02:00
let record = CtOption::new(
ServerRegistration::dummy(rng, server_setup),
Choice::from(password_file.is_none() as u8),
)
.into_option()
.unwrap_or_else(|| password_file.unwrap());
let client_s_pk = record.0.client_s_pk.clone();
2024-09-16 18:01:45 -07:00
let context = context.unwrap_or(&[]);
let server_s_pk = server_setup.keypair.public();
2022-01-04 00:50:40 +01:00
let mut masking_nonce = GenericArray::<_, NonceLen>::default();
rng.fill_bytes(&mut masking_nonce);
let masked_response = mask_response(
&record.0.masking_key,
2022-01-04 00:50:40 +01:00
masking_nonce.as_slice(),
server_s_pk,
&record.0.envelope,
)?;
2022-01-04 00:50:40 +01:00
let (id_u, id_s) = bytestrings_from_identifiers::<CS::KeGroup>(
identifiers,
2022-04-02 01:10:00 +02:00
client_s_pk.serialize(),
server_s_pk.serialize(),
)?;
2020-11-16 14:05:43 -08:00
2022-02-25 07:13:22 +01:00
let blinded_element =
OprfGroup::<CS>::serialize_elem(credential_request.blinded_element.value());
2022-04-02 01:10:00 +02:00
let ke1_message = credential_request.ke1_message.serialize();
2022-01-04 00:50:40 +01:00
let credential_request_bytes =
CredentialRequest::<CS>::serialize_iter(&blinded_element, &ke1_message);
let oprf_key = oprf_key_from_seed::<CS>(&server_setup.oprf_seed, credential_identifier)?;
let server = voprf::OprfServer::new_with_key(&oprf_key).map_err(ProtocolError::from)?;
2022-12-11 06:30:05 +01:00
let evaluation_element = server.blind_evaluate(&credential_request.blinded_element);
2022-02-25 07:13:22 +01:00
let beta = OprfGroup::<CS>::serialize_elem(evaluation_element.value());
2022-01-04 00:50:40 +01:00
let credential_response_component =
CredentialResponse::<CS>::serialize_without_ke(&beta, &masking_nonce, &masked_response);
2020-06-05 09:35:14 -07:00
let ke2_builder = CS::KeyExchange::ke2_builder::<CS::OprfCs, _>(
2020-06-05 09:35:14 -07:00
rng,
2021-10-25 02:54:32 -07:00
credential_request_bytes,
credential_response_component,
2022-02-25 07:13:22 +01:00
credential_request.ke1_message.clone(),
client_s_pk,
2022-01-04 00:50:40 +01:00
id_u.iter(),
id_s.iter(),
context,
2020-06-05 09:35:14 -07:00
)?;
Ok(ServerLoginBuilder {
server_s_sk: server_setup.keypair().private().clone(),
2021-10-25 02:54:32 -07:00
evaluation_element,
masking_nonce: Zeroizing::new(masking_nonce),
masked_response,
#[cfg(test)]
oprf_key: Zeroizing::new(oprf_key),
ke2_builder,
})
}
pub(crate) fn build<S: Clone>(
builder: ServerLoginBuilder<CS, S>,
input: <CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2BuilderInput,
) -> Result<ServerLoginStartResult<CS>, ProtocolError> {
let result = CS::KeyExchange::build_ke2(builder.ke2_builder.clone(), input)?;
let credential_response = CredentialResponse {
evaluation_element: builder.evaluation_element.clone(),
masking_nonce: *builder.masking_nonce.deref(),
masked_response: builder.masked_response.clone(),
2021-07-30 12:54:16 +02:00
ke2_message: result.1,
2020-06-05 09:35:14 -07:00
};
2020-11-16 14:05:43 -08:00
Ok(ServerLoginStartResult {
message: credential_response,
2020-12-12 21:53:33 -08:00
state: Self {
2021-07-30 12:54:16 +02:00
ke2_state: result.0,
2020-07-13 15:23:29 -07:00
},
2021-07-30 12:54:16 +02:00
#[cfg(test)]
handshake_secret: result.2,
2021-07-30 13:41:51 +02:00
#[cfg(test)]
server_mac_key: result.3,
2021-07-30 14:00:23 +02:00
#[cfg(test)]
oprf_key: builder.oprf_key.deref().clone(),
2020-11-16 14:05:43 -08:00
})
2020-06-05 09:35:14 -07:00
}
/// From the client's "blinded" password, returns a challenge to be sent
/// back to the client, as well as a [`ServerLogin`]
pub fn start<R: RngCore + CryptoRng>(
rng: &mut R,
server_setup: &ServerSetup<CS>,
password_file: Option<ServerRegistration<CS>>,
credential_request: CredentialRequest<CS>,
credential_identifier: &[u8],
parameters: ServerLoginStartParameters,
) -> Result<ServerLoginStartResult<CS>, ProtocolError>
where
// MaskedResponse: (Nonce + Hash) + KePk
NonceLen: Add<OutputSize<OprfHash<CS>>>,
Sum<NonceLen, OutputSize<OprfHash<CS>>>:
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
MaskedResponseLen<CS>: ArrayLength<u8>,
{
let builder = Self::builder(
rng,
server_setup,
password_file,
credential_request,
credential_identifier,
parameters,
)?;
let input = CS::KeyExchange::generate_ke2_input(
&builder.ke2_builder,
server_setup.keypair.private(),
);
Self::build(builder, input)
}
2020-11-16 14:05:43 -08:00
/// From the client's second and final message, check the client's
/// authentication and produce a message transport
pub fn finish(
self,
2020-12-12 21:53:33 -08:00
message: CredentialFinalization<CS>,
) -> Result<ServerLoginFinishResult<CS>, ProtocolError> {
2022-02-25 07:13:22 +01:00
let session_key = <CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::finish_ke(
message.ke3_message,
&self.ke2_state,
2021-08-22 12:28:19 -07:00
)?;
2020-11-16 14:05:43 -08:00
Ok(ServerLoginFinishResult {
session_key,
#[cfg(test)]
state: self,
})
}
}
/////////////////////////
// Convenience Structs //
//==================== //
/////////////////////////
/// Options for specifying custom identifiers
2022-01-04 00:50:40 +01:00
#[derive(Clone, Copy, Debug, Default)]
pub struct Identifiers<'a> {
/// Client identifier
pub client: Option<&'a [u8]>,
/// Server identifier
pub server: Option<&'a [u8]>,
}
/// Optional parameters for client registration finish
2022-01-04 00:50:40 +01:00
#[derive_where(Clone, Default)]
2025-04-15 22:31:37 +02:00
pub struct ClientRegistrationFinishParameters<'i, 'h, CS: CipherSuite> {
/// Specifying the identifiers idU and idS
2022-01-04 00:50:40 +01:00
pub identifiers: Identifiers<'i>,
2022-04-02 01:10:00 +02:00
/// Specifying a configuration for the key stretching function
pub ksf: Option<&'h CS::Ksf>,
}
2025-04-15 22:31:37 +02:00
impl<'i, 'h, CS: CipherSuite> ClientRegistrationFinishParameters<'i, 'h, CS> {
/// Create a new [`ClientRegistrationFinishParameters`]
2022-04-02 01:10:00 +02:00
pub fn new(identifiers: Identifiers<'i>, ksf: Option<&'h CS::Ksf>) -> Self {
Self { identifiers, ksf }
}
}
/// Contains the fields that are returned by a client registration start
2022-01-04 00:50:40 +01:00
#[derive_where(Clone)]
2025-04-15 22:31:37 +02:00
pub struct ClientRegistrationStartResult<CS: CipherSuite> {
/// The registration request message to be sent to the server
pub message: RegistrationRequest<CS>,
2022-01-06 06:19:02 +01:00
/// The client state that must be persisted in order to complete
/// registration
pub state: ClientRegistration<CS>,
}
/// Contains the fields that are returned by a client registration finish
2022-01-04 00:50:40 +01:00
#[derive_where(Clone)]
2025-04-15 22:31:37 +02:00
pub struct ClientRegistrationFinishResult<CS: CipherSuite> {
/// The registration upload message to be sent to the server
pub message: RegistrationUpload<CS>,
/// The export key output by client registration
2022-02-25 07:13:22 +01:00
pub export_key: Output<OprfHash<CS>>,
/// The server's static public key
pub server_s_pk: PublicKey<CS::KeGroup>,
2022-01-06 06:19:02 +01:00
/// Instance of the ClientRegistration, only used in tests for checking
/// zeroize
#[cfg(test)]
pub state: ClientRegistration<CS>,
/// AuthKey, only used in tests
#[cfg(test)]
2022-02-25 07:13:22 +01:00
pub auth_key: Output<OprfHash<CS>>,
/// Password derived key, only used in tests
#[cfg(test)]
2022-02-25 07:13:22 +01:00
pub randomized_pwd: Output<OprfHash<CS>>,
}
2022-01-06 06:19:02 +01:00
/// Contains the fields that are returned by a server registration start. Note
/// that there is no state output in this step
2022-01-04 00:50:40 +01:00
#[derive_where(Clone)]
2025-04-15 22:31:37 +02:00
pub struct ServerRegistrationStartResult<CS: CipherSuite> {
/// The registration resposne message to send to the client
pub message: RegistrationResponse<CS>,
/// OPRF key, only used in tests
#[cfg(test)]
2022-02-25 07:13:22 +01:00
pub oprf_key: GenericArray<u8, <OprfGroup<CS> as Group>::ScalarLen>,
}
/// Contains the fields that are returned by a client login start
2022-01-04 00:50:40 +01:00
#[derive_where(Clone)]
2025-04-15 22:31:37 +02:00
pub struct ClientLoginStartResult<CS: CipherSuite> {
/// The message to send to the server to begin the login protocol
pub message: CredentialRequest<CS>,
/// The state that the client must keep in order to complete the protocol
pub state: ClientLogin<CS>,
}
/// Optional parameters for client login finish
2022-01-04 00:50:40 +01:00
#[derive_where(Clone, Default)]
2025-04-15 22:31:37 +02:00
pub struct ClientLoginFinishParameters<'c, 'i, 'h, CS: CipherSuite> {
/// Specifying a context field that the server must agree on
2022-01-04 00:50:40 +01:00
pub context: Option<&'c [u8]>,
2022-01-06 06:19:02 +01:00
/// Specifying a user identifier and server identifier that will be matched
/// against the server
2022-01-04 00:50:40 +01:00
pub identifiers: Identifiers<'i>,
2022-04-02 01:10:00 +02:00
/// Specifying a configuration for the key stretching hash
pub ksf: Option<&'h CS::Ksf>,
}
2025-04-15 22:31:37 +02:00
impl<'c, 'i, 'h, CS: CipherSuite> ClientLoginFinishParameters<'c, 'i, 'h, CS> {
/// Create a new [`ClientLoginFinishParameters`]
pub fn new(
2022-01-04 00:50:40 +01:00
context: Option<&'c [u8]>,
identifiers: Identifiers<'i>,
2022-04-02 01:10:00 +02:00
ksf: Option<&'h CS::Ksf>,
) -> Self {
Self {
context,
identifiers,
2022-04-02 01:10:00 +02:00
ksf,
}
}
}
/// Contains the fields that are returned by a client login finish
2022-01-04 00:50:40 +01:00
#[derive_where(Clone)]
2025-04-15 22:31:37 +02:00
pub struct ClientLoginFinishResult<CS: CipherSuite> {
/// The message to send to the server to complete the protocol
pub message: CredentialFinalization<CS>,
/// The session key
2022-02-25 07:13:22 +01:00
pub session_key: Output<OprfHash<CS>>,
/// The client-side export key
2022-02-25 07:13:22 +01:00
pub export_key: Output<OprfHash<CS>>,
/// The server's static public key
pub server_s_pk: PublicKey<CS::KeGroup>,
/// Instance of the ClientLogin, only used in tests for checking zeroize
#[cfg(test)]
pub state: ClientLogin<CS>,
/// Handshake secret, only used in tests
#[cfg(test)]
2022-02-25 07:13:22 +01:00
pub handshake_secret: Output<OprfHash<CS>>,
/// Client MAC key, only used in tests
#[cfg(test)]
2022-02-25 07:13:22 +01:00
pub client_mac_key: Output<OprfHash<CS>>,
}
/// Contains the fields that are returned by a server login finish
2022-01-04 00:50:40 +01:00
#[derive_where(Clone)]
#[cfg_attr(not(test), derive_where(Debug))]
#[cfg_attr(test, derive_where(Debug; ServerLogin<CS>))]
2025-04-15 22:31:37 +02:00
pub struct ServerLoginFinishResult<CS: CipherSuite> {
/// The session key between client and server
2022-02-25 07:13:22 +01:00
pub session_key: Output<OprfHash<CS>>,
2022-01-06 06:19:02 +01:00
/// Instance of the ClientRegistration, only used in tests for checking
/// zeroize
#[cfg(test)]
pub state: ServerLogin<CS>,
}
/// Optional parameters for server login start
2022-01-04 00:50:40 +01:00
#[derive(Clone, Debug, Default)]
pub struct ServerLoginStartParameters<'c, 'i> {
/// Specifying a context field that the client must agree on
2022-01-04 00:50:40 +01:00
pub context: Option<&'c [u8]>,
2022-01-06 06:19:02 +01:00
/// Specifying a user identifier and server identifier that will be matched
/// against the client
2022-01-04 00:50:40 +01:00
pub identifiers: Identifiers<'i>,
}
/// Contains the fields that are returned by a server login start
2022-01-04 00:50:40 +01:00
#[derive_where(Clone)]
#[derive_where(
Debug;
2022-04-02 01:10:00 +02:00
voprf::EvaluationElement<CS::OprfCs>,
2022-02-25 07:13:22 +01:00
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2Message,
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2State,
2022-01-04 00:50:40 +01:00
)]
2025-04-15 22:31:37 +02:00
pub struct ServerLoginStartResult<CS: CipherSuite> {
/// The message to send back to the client
pub message: CredentialResponse<CS>,
/// The state that the server must keep in order to finish the protocl
pub state: ServerLogin<CS>,
/// Handshake secret, only used in tests
#[cfg(test)]
2022-02-25 07:13:22 +01:00
pub handshake_secret: Output<OprfHash<CS>>,
/// Server MAC key, only used in tests
#[cfg(test)]
2022-02-25 07:13:22 +01:00
pub server_mac_key: Output<OprfHash<CS>>,
/// OPRF key, only used in tests
#[cfg(test)]
2022-02-25 07:13:22 +01:00
pub oprf_key: GenericArray<u8, <OprfGroup<CS> as Group>::ScalarLen>,
}
////////////////////////////////////////////////
// Helper functions and Trait Implementations //
// ========================================== //
////////////////////////////////////////////////
2020-06-05 09:35:14 -07:00
// Helper functions
2021-10-25 02:54:32 -07:00
#[allow(clippy::type_complexity)]
2021-09-02 11:28:21 +02:00
fn get_password_derived_key<CS: CipherSuite>(
2022-01-06 00:10:57 +01:00
input: &[u8],
2022-04-17 16:23:31 -07:00
oprf_client: voprf::OprfClient<CS::OprfCs>,
2022-04-02 01:10:00 +02:00
evaluation_element: voprf::EvaluationElement<CS::OprfCs>,
ksf: Option<&CS::Ksf>,
2025-04-15 22:31:37 +02:00
) -> Result<(Output<OprfHash<CS>>, Hkdf<OprfHash<CS>>), ProtocolError> {
2022-04-17 16:23:31 -07:00
let oprf_output = oprf_client.finalize(input, &evaluation_element)?;
2021-09-02 11:28:21 +02:00
2022-04-02 01:10:00 +02:00
let hardened_output = if let Some(ksf) = ksf {
ksf.hash(oprf_output.clone())
2021-09-02 11:28:21 +02:00
} else {
2022-04-02 01:10:00 +02:00
CS::Ksf::default().hash(oprf_output.clone())
2021-09-02 11:28:21 +02:00
}
2021-10-25 02:54:32 -07:00
.map_err(ProtocolError::from)?;
2022-02-25 07:13:22 +01:00
let mut hkdf = HkdfExtract::<OprfHash<CS>>::new(None);
2022-01-04 00:50:40 +01:00
hkdf.input_ikm(&oprf_output);
hkdf.input_ikm(&hardened_output);
Ok(hkdf.finalize())
2020-06-05 09:35:14 -07:00
}
2025-04-15 22:31:37 +02:00
fn oprf_key_from_seed<CS: CipherSuite>(
oprf_seed: &Output<OprfHash<CS>>,
credential_identifier: &[u8],
2025-04-15 22:31:37 +02:00
) -> Result<GenericArray<u8, <OprfGroup<CS> as Group>::ScalarLen>, ProtocolError> {
let mut ikm = GenericArray::<_, <OprfGroup<CS> as Group>::ScalarLen>::default();
Hkdf::<OprfHash<CS>>::from_prk(oprf_seed)
2022-01-04 00:50:40 +01:00
.ok()
.and_then(|hkdf| {
hkdf.expand_multi_info(&[credential_identifier, STR_OPRF_KEY], &mut ikm)
.ok()
})
.ok_or(InternalError::HkdfError)?;
2022-04-17 16:23:31 -07:00
2025-04-15 22:31:37 +02:00
Ok(OprfGroup::<CS>::serialize_scalar(voprf::derive_key::<
CS::OprfCs,
>(
2022-04-17 16:23:31 -07:00
ikm.as_slice(),
&GenericArray::from(*STR_OPAQUE_DERIVE_KEY_PAIR),
voprf::Mode::Oprf,
)?))
2022-01-04 00:50:40 +01:00
}
2022-04-02 01:10:00 +02:00
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
2023-02-04 22:25:41 +01:00
serde(bound = "")
2022-04-02 01:10:00 +02:00
)]
#[derive_where(Clone, ZeroizeOnDrop)]
2022-01-04 00:50:40 +01:00
#[derive_where(Debug, Eq, Hash, PartialEq)]
2025-04-15 22:31:37 +02:00
pub(crate) struct MaskedResponse<CS: CipherSuite> {
2022-01-04 00:50:40 +01:00
pub(crate) nonce: GenericArray<u8, NonceLen>,
2022-02-25 07:13:22 +01:00
pub(crate) hash: Output<OprfHash<CS>>,
2022-01-04 00:50:40 +01:00
pub(crate) pk: GenericArray<u8, <CS::KeGroup as KeGroup>::PkLen>,
}
pub(crate) type MaskedResponseLen<CS: CipherSuite> =
2022-02-25 07:13:22 +01:00
Sum<Sum<NonceLen, OutputSize<OprfHash<CS>>>, <CS::KeGroup as KeGroup>::PkLen>;
2022-01-04 00:50:40 +01:00
2025-04-15 22:31:37 +02:00
impl<CS: CipherSuite> MaskedResponse<CS> {
2022-01-04 00:50:40 +01:00
pub(crate) fn serialize(&self) -> GenericArray<u8, MaskedResponseLen<CS>>
where
// MaskedResponse: (Nonce + Hash) + KePk
2022-02-25 07:13:22 +01:00
NonceLen: Add<OutputSize<OprfHash<CS>>>,
Sum<NonceLen, OutputSize<OprfHash<CS>>>:
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
2022-01-04 00:50:40 +01:00
MaskedResponseLen<CS>: ArrayLength<u8>,
{
self.nonce.concat(self.hash.clone()).concat(self.pk.clone())
}
pub(crate) fn deserialize(bytes: &[u8]) -> Self {
let nonce = NonceLen::USIZE;
2022-02-25 07:13:22 +01:00
let hash = nonce + OutputSize::<OprfHash<CS>>::USIZE;
2022-01-04 00:50:40 +01:00
let pk = hash + <CS::KeGroup as KeGroup>::PkLen::USIZE;
Self {
nonce: GenericArray::clone_from_slice(&bytes[..nonce]),
hash: GenericArray::clone_from_slice(&bytes[nonce..hash]),
pk: GenericArray::clone_from_slice(&bytes[hash..pk]),
}
}
pub(crate) fn iter(&self) -> impl Iterator<Item = &[u8]> {
2022-01-06 00:10:57 +01:00
[self.nonce.as_slice(), &self.hash, &self.pk].into_iter()
2022-01-04 00:50:40 +01:00
}
}
fn mask_response<CS: CipherSuite>(
masking_key: &[u8],
masking_nonce: &[u8],
2021-08-04 21:24:46 +02:00
server_s_pk: &PublicKey<CS::KeGroup>,
envelope: &Envelope<CS>,
2022-01-04 00:50:40 +01:00
) -> Result<MaskedResponse<CS>, ProtocolError>
where
// MaskedResponse: (Nonce + Hash) + KePk
2022-02-25 07:13:22 +01:00
NonceLen: Add<OutputSize<OprfHash<CS>>>,
Sum<NonceLen, OutputSize<OprfHash<CS>>>: ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
2022-01-04 00:50:40 +01:00
MaskedResponseLen<CS>: ArrayLength<u8>,
{
let mut xor_pad = GenericArray::<_, MaskedResponseLen<CS>>::default();
2022-02-25 07:13:22 +01:00
Hkdf::<OprfHash<CS>>::from_prk(masking_key)
2021-08-22 12:28:19 -07:00
.map_err(|_| InternalError::HkdfError)?
2022-01-04 00:50:40 +01:00
.expand_multi_info(&[masking_nonce, STR_CREDENTIAL_RESPONSE_PAD], &mut xor_pad)
2021-08-22 12:28:19 -07:00
.map_err(|_| InternalError::HkdfError)?;
2022-01-04 00:50:40 +01:00
for (x1, x2) in xor_pad.iter_mut().zip(
server_s_pk
2022-04-02 01:10:00 +02:00
.serialize()
2022-01-04 00:50:40 +01:00
.as_slice()
.iter()
.chain(envelope.serialize().iter()),
) {
*x1 ^= x2
}
2022-01-04 00:50:40 +01:00
Ok(MaskedResponse::deserialize(&xor_pad))
}
fn unmask_response<CS: CipherSuite>(
masking_key: &[u8],
masking_nonce: &[u8],
2022-01-04 00:50:40 +01:00
masked_response: &MaskedResponse<CS>,
) -> Result<(PublicKey<CS::KeGroup>, Envelope<CS>), ProtocolError>
where
// MaskedResponse: (Nonce + Hash) + KePk
2022-02-25 07:13:22 +01:00
NonceLen: Add<OutputSize<OprfHash<CS>>>,
Sum<NonceLen, OutputSize<OprfHash<CS>>>: ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
2022-01-04 00:50:40 +01:00
MaskedResponseLen<CS>: ArrayLength<u8>,
{
let mut xor_pad = GenericArray::<_, MaskedResponseLen<CS>>::default();
2022-02-25 07:13:22 +01:00
Hkdf::<OprfHash<CS>>::from_prk(masking_key)
2021-08-22 12:28:19 -07:00
.map_err(|_| InternalError::HkdfError)?
2022-01-04 00:50:40 +01:00
.expand_multi_info(&[masking_nonce, STR_CREDENTIAL_RESPONSE_PAD], &mut xor_pad)
2021-08-22 12:28:19 -07:00
.map_err(|_| InternalError::HkdfError)?;
2022-01-04 00:50:40 +01:00
for (x1, x2) in xor_pad.iter_mut().zip(masked_response.iter().flatten()) {
*x1 ^= x2
}
2021-10-25 02:54:32 -07:00
let key_len = <CS::KeGroup as KeGroup>::PkLen::USIZE;
2022-02-25 07:13:22 +01:00
let server_s_pk = PublicKey::deserialize(&xor_pad[..key_len])
2021-08-22 12:28:19 -07:00
.map_err(|_| ProtocolError::SerializationError)?;
2022-02-25 07:13:22 +01:00
let envelope = Envelope::deserialize(&xor_pad[key_len..])?;
Ok((server_s_pk, envelope))
}
2022-01-04 00:50:40 +01:00
#[allow(clippy::type_complexity)]
pub(crate) fn bytestrings_from_identifiers<KG: KeGroup>(
ids: Identifiers,
client_s_pk: GenericArray<u8, KG::PkLen>,
server_s_pk: GenericArray<u8, KG::PkLen>,
2022-04-02 01:10:00 +02:00
) -> Result<(Input<U2, KG::PkLen>, Input<U2, KG::PkLen>), ProtocolError> {
2022-01-04 00:50:40 +01:00
let client_identity = if let Some(client) = ids.client {
2022-04-02 01:10:00 +02:00
Input::<U2, _>::from(client)?
2022-01-04 00:50:40 +01:00
} else {
2022-04-02 01:10:00 +02:00
Input::<U2, _>::from_owned(client_s_pk)?
};
2022-01-04 00:50:40 +01:00
let server_identity = if let Some(server) = ids.server {
2022-04-02 01:10:00 +02:00
Input::<U2, _>::from(server)?
2022-01-04 00:50:40 +01:00
} else {
2022-04-02 01:10:00 +02:00
Input::<U2, _>::from_owned(server_s_pk)?
2022-01-04 00:50:40 +01:00
};
Ok((client_identity, server_identity))
}
2022-01-06 06:19:02 +01:00
/// Internal function for computing the blind result by calling the voprf
/// library. Note that for tests, we use the deterministic blinding in order to
/// be able to set the blinding factor directly from the passed-in rng.
2021-10-25 02:54:32 -07:00
fn blind<CS: CipherSuite, R: RngCore + CryptoRng>(
rng: &mut R,
password: &[u8],
2025-04-15 22:31:37 +02:00
) -> Result<voprf::OprfClientBlindResult<CS::OprfCs>, voprf::Error> {
2021-10-25 02:54:32 -07:00
#[cfg(not(test))]
2022-04-17 16:23:31 -07:00
let result = voprf::OprfClient::blind(password, rng)?;
2021-10-25 02:54:32 -07:00
#[cfg(test)]
let result = {
2022-02-25 07:13:22 +01:00
let mut blind_bytes = GenericArray::<_, <OprfGroup<CS> as Group>::ScalarLen>::default();
2021-10-25 02:54:32 -07:00
let blind = loop {
rng.fill_bytes(&mut blind_bytes);
2022-02-25 07:13:22 +01:00
if let Ok(scalar) = <OprfGroup<CS> as Group>::deserialize_scalar(&blind_bytes) {
break scalar;
2021-10-25 02:54:32 -07:00
}
};
2022-04-17 16:23:31 -07:00
voprf::OprfClient::deterministic_blind_unchecked(password, blind)?
2021-10-25 02:54:32 -07:00
};
Ok(result)
}