2020-06-05 09:35:14 -07:00
|
|
|
// Copyright (c) Facebook, Inc. and its affiliates.
|
|
|
|
|
//
|
|
|
|
|
// This source code is licensed under the MIT license found in the
|
|
|
|
|
// LICENSE file in the root directory of this source tree.
|
|
|
|
|
|
|
|
|
|
//! Provides the main OPAQUE API
|
|
|
|
|
|
|
|
|
|
use crate::{
|
2020-06-14 23:25:31 -07:00
|
|
|
ciphersuite::CipherSuite,
|
2021-04-30 15:56:51 -07:00
|
|
|
envelope::Envelope,
|
2021-08-22 12:28:19 -07:00
|
|
|
errors::{utils::check_slice_size, InternalError, ProtocolError},
|
2020-07-27 15:25:04 -07:00
|
|
|
hash::Hash,
|
2021-10-25 02:54:32 -07:00
|
|
|
key_exchange::{
|
|
|
|
|
group::KeGroup,
|
|
|
|
|
traits::{FromBytes, KeyExchange, ToBytes},
|
|
|
|
|
},
|
2021-07-16 13:54:10 +02:00
|
|
|
keypair::{KeyPair, PrivateKey, PublicKey, SecretKey},
|
2021-01-26 09:27:30 -08:00
|
|
|
serialization::{serialize, tokenize},
|
2020-06-08 21:02:01 -07:00
|
|
|
slow_hash::SlowHash,
|
2020-12-12 21:53:33 -08:00
|
|
|
CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest,
|
|
|
|
|
RegistrationResponse, RegistrationUpload,
|
2020-06-05 09:35:14 -07:00
|
|
|
};
|
2021-08-12 06:25:07 +02:00
|
|
|
use alloc::vec;
|
|
|
|
|
use alloc::vec::Vec;
|
|
|
|
|
use core::marker::PhantomData;
|
2021-01-26 09:27:30 -08:00
|
|
|
use digest::Digest;
|
2020-07-02 12:24:53 -07:00
|
|
|
use generic_array::{typenum::Unsigned, GenericArray};
|
2021-04-16 01:07:07 -07:00
|
|
|
use hkdf::Hkdf;
|
2021-02-11 18:10:48 -08:00
|
|
|
use rand::{CryptoRng, RngCore};
|
2021-10-25 02:54:32 -07:00
|
|
|
use subtle::ConstantTimeEq;
|
|
|
|
|
use voprf::group::Group;
|
2020-06-05 09:35:14 -07:00
|
|
|
use zeroize::Zeroize;
|
|
|
|
|
|
2021-09-25 16:36:00 -07: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";
|
2021-04-16 01:07:07 -07:00
|
|
|
|
2021-09-25 16:36:00 -07:00
|
|
|
////////////////////////////
|
|
|
|
|
// High-level API Structs //
|
|
|
|
|
// ====================== //
|
|
|
|
|
////////////////////////////
|
2021-04-16 01:07:07 -07:00
|
|
|
|
|
|
|
|
/// The state elements the server holds upon setup
|
2021-07-16 13:54:10 +02:00
|
|
|
#[cfg_attr(
|
|
|
|
|
feature = "serialize",
|
|
|
|
|
derive(serde::Deserialize, serde::Serialize),
|
|
|
|
|
serde(bound(
|
2021-08-04 21:24:46 +02:00
|
|
|
deserialize = "KeyPair<CS::KeGroup, S>: serde::Deserialize<'de>",
|
|
|
|
|
serialize = "KeyPair<CS::KeGroup, S>: serde::Serialize"
|
2021-07-16 13:54:10 +02:00
|
|
|
))
|
|
|
|
|
)]
|
2021-07-16 14:31:44 +02:00
|
|
|
pub struct ServerSetup<
|
|
|
|
|
CS: CipherSuite,
|
2021-08-04 21:24:46 +02:00
|
|
|
S: SecretKey<CS::KeGroup> = PrivateKey<<CS as CipherSuite>::KeGroup>,
|
2021-07-16 14:31:44 +02:00
|
|
|
> {
|
2021-04-16 01:07:07 -07:00
|
|
|
oprf_seed: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
2021-08-04 21:24:46 +02:00
|
|
|
keypair: KeyPair<CS::KeGroup, S>,
|
|
|
|
|
pub(crate) fake_keypair: KeyPair<CS::KeGroup>,
|
2021-04-16 01:07:07 -07:00
|
|
|
}
|
|
|
|
|
|
2021-09-25 16:36:00 -07:00
|
|
|
// Cannot be derived because it would require for CS to be bound.
|
|
|
|
|
impl_clone_for!(
|
|
|
|
|
struct ServerSetup<CS: CipherSuite>,
|
|
|
|
|
[oprf_seed, keypair, fake_keypair],
|
|
|
|
|
);
|
|
|
|
|
impl_debug_eq_hash_for!(
|
|
|
|
|
struct ServerSetup<CS: CipherSuite>,
|
|
|
|
|
[oprf_seed, oprf_seed, fake_keypair],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/// The state elements the client holds to register itself
|
|
|
|
|
pub struct ClientRegistration<CS: CipherSuite> {
|
2021-10-25 02:54:32 -07:00
|
|
|
pub(crate) oprf_client: voprf::NonVerifiableClient<CS::OprfGroup, CS::Hash>,
|
|
|
|
|
pub(crate) blinded_element: voprf::BlindedElement<CS::OprfGroup, CS::Hash>,
|
2021-09-25 16:36:00 -07:00
|
|
|
}
|
|
|
|
|
|
2021-10-25 02:54:32 -07:00
|
|
|
impl_clone_for!(struct ClientRegistration<CS: CipherSuite>, [oprf_client, blinded_element]);
|
2021-09-25 16:36:00 -07:00
|
|
|
impl_debug_eq_hash_for!(
|
|
|
|
|
struct ClientRegistration<CS: CipherSuite>,
|
2021-10-25 02:54:32 -07:00
|
|
|
[oprf_client],
|
|
|
|
|
[voprf::NonVerifiableClient<CS::OprfGroup, CS::Hash>],
|
2021-09-25 16:36:00 -07:00
|
|
|
);
|
|
|
|
|
impl_serialize_and_deserialize_for!(ClientRegistration);
|
|
|
|
|
|
|
|
|
|
/// The state elements the server holds to record a registration
|
|
|
|
|
pub struct ServerRegistration<CS: CipherSuite>(RegistrationUpload<CS>);
|
|
|
|
|
|
|
|
|
|
impl_clone_for!(tuple ServerRegistration<CS: CipherSuite>, [0]);
|
|
|
|
|
impl_debug_eq_hash_for!(
|
|
|
|
|
tuple ServerRegistration<CS: CipherSuite>,
|
|
|
|
|
[0],
|
|
|
|
|
);
|
|
|
|
|
impl_serialize_and_deserialize_for!(ServerRegistration);
|
|
|
|
|
|
|
|
|
|
/// The state elements the client holds to perform a login
|
|
|
|
|
pub struct ClientLogin<CS: CipherSuite> {
|
2021-10-25 02:54:32 -07:00
|
|
|
oprf_client: voprf::NonVerifiableClient<CS::OprfGroup, CS::Hash>,
|
2021-09-25 16:36:00 -07:00
|
|
|
ke1_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State,
|
|
|
|
|
serialized_credential_request: Vec<u8>,
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-25 02:54:32 -07:00
|
|
|
impl_clone_for!(struct ClientLogin<CS: CipherSuite>, [oprf_client, ke1_state, serialized_credential_request]);
|
2021-09-25 16:36:00 -07:00
|
|
|
impl_debug_eq_hash_for!(
|
|
|
|
|
struct ClientLogin<CS: CipherSuite>,
|
2021-10-25 02:54:32 -07:00
|
|
|
[oprf_client, ke1_state, serialized_credential_request],
|
|
|
|
|
[voprf::NonVerifiableClient<CS::OprfGroup, CS::Hash>, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State],
|
2021-09-25 16:36:00 -07:00
|
|
|
);
|
2021-10-25 02:54:32 -07:00
|
|
|
impl_serialize_and_deserialize_for!(ClientLogin);
|
2021-09-25 16:36:00 -07:00
|
|
|
|
|
|
|
|
/// The state elements the server holds to record a login
|
|
|
|
|
pub struct ServerLogin<CS: CipherSuite> {
|
|
|
|
|
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2State,
|
|
|
|
|
_cs: PhantomData<CS>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl_clone_for!(struct ServerLogin<CS: CipherSuite>, [ke2_state, _cs]);
|
|
|
|
|
impl_debug_eq_hash_for!(
|
|
|
|
|
struct ServerLogin<CS: CipherSuite>,
|
|
|
|
|
[ke2_state, _cs],
|
|
|
|
|
[<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2State],
|
|
|
|
|
);
|
|
|
|
|
impl_serialize_and_deserialize_for!(ServerLogin);
|
|
|
|
|
|
|
|
|
|
////////////////////////////////
|
|
|
|
|
// High-level Implementations //
|
|
|
|
|
// ========================== //
|
|
|
|
|
////////////////////////////////
|
|
|
|
|
|
|
|
|
|
// Server Setup
|
|
|
|
|
// ============
|
|
|
|
|
|
2021-08-04 21:24:46 +02:00
|
|
|
impl<CS: CipherSuite> ServerSetup<CS, PrivateKey<CS::KeGroup>> {
|
2021-04-16 01:07:07 -07:00
|
|
|
/// Generate a new instance of server setup
|
2021-10-25 02:54:32 -07:00
|
|
|
pub fn new<R: CryptoRng + RngCore>(rng: &mut R) -> Result<Self, InternalError> {
|
|
|
|
|
let keypair = KeyPair::<CS::KeGroup>::generate_random(rng)?;
|
2021-07-20 14:16:53 +02:00
|
|
|
Self::new_with_key(rng, keypair)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-04 21:24:46 +02:00
|
|
|
impl<CS: CipherSuite, S: SecretKey<CS::KeGroup>> ServerSetup<CS, S> {
|
2021-07-20 14:16:53 +02:00
|
|
|
/// Create [`ServerSetup`] with the given keypair
|
|
|
|
|
pub fn new_with_key<R: CryptoRng + RngCore>(
|
|
|
|
|
rng: &mut R,
|
2021-08-04 21:24:46 +02:00
|
|
|
keypair: KeyPair<CS::KeGroup, S>,
|
2021-10-25 02:54:32 -07:00
|
|
|
) -> Result<Self, InternalError> {
|
2021-08-17 05:11:53 +02:00
|
|
|
let mut seed = vec![0u8; <CS::Hash as Digest>::OutputSize::USIZE];
|
2021-04-16 01:07:07 -07:00
|
|
|
rng.fill_bytes(&mut seed);
|
|
|
|
|
|
2021-10-25 02:54:32 -07:00
|
|
|
Ok(Self {
|
2021-04-16 01:07:07 -07:00
|
|
|
oprf_seed: GenericArray::clone_from_slice(&seed[..]),
|
2021-07-20 14:16:53 +02:00
|
|
|
keypair,
|
2021-10-25 02:54:32 -07:00
|
|
|
fake_keypair: KeyPair::<CS::KeGroup>::generate_random(rng)?,
|
|
|
|
|
})
|
2021-04-16 01:07:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Serialization into bytes
|
2021-10-25 02:54:32 -07:00
|
|
|
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
|
|
|
|
|
Ok([
|
2021-04-16 01:07:07 -07:00
|
|
|
self.oprf_seed.to_vec(),
|
2021-07-16 13:54:10 +02:00
|
|
|
self.keypair.private().serialize(),
|
|
|
|
|
self.fake_keypair.private().serialize(),
|
2021-04-16 01:07:07 -07:00
|
|
|
]
|
2021-10-25 02:54:32 -07:00
|
|
|
.concat())
|
2021-04-16 01:07:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Deserialization from bytes
|
2021-07-20 11:49:37 +02:00
|
|
|
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError<S::Error>> {
|
2021-08-17 05:11:53 +02:00
|
|
|
let seed_len = <CS::Hash as Digest>::OutputSize::USIZE;
|
2021-10-25 02:54:32 -07:00
|
|
|
let key_len = <CS::KeGroup as KeGroup>::SkLen::USIZE;
|
2021-04-30 15:56:51 -07:00
|
|
|
let checked_slice = check_slice_size(input, seed_len + key_len + key_len, "server_setup")?;
|
2021-04-16 01:07:07 -07:00
|
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
|
oprf_seed: GenericArray::clone_from_slice(&checked_slice[..seed_len]),
|
2021-04-30 15:56:51 -07:00
|
|
|
keypair: KeyPair::from_private_key_slice(&checked_slice[seed_len..seed_len + key_len])?,
|
2021-07-20 11:49:37 +02:00
|
|
|
fake_keypair: KeyPair::from_private_key_slice(&checked_slice[seed_len + key_len..])
|
|
|
|
|
.map_err(ProtocolError::into_custom)?,
|
2021-04-16 01:07:07 -07:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns the keypair
|
2021-08-04 21:24:46 +02:00
|
|
|
pub fn keypair(&self) -> &KeyPair<CS::KeGroup, S> {
|
2021-04-16 01:07:07 -07:00
|
|
|
&self.keypair
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-05 09:35:14 -07:00
|
|
|
// Registration
|
|
|
|
|
// ============
|
|
|
|
|
|
2021-02-17 02:47:00 -08:00
|
|
|
impl<CS: CipherSuite> ClientRegistration<CS> {
|
|
|
|
|
/// Serialization into bytes
|
2021-10-25 02:54:32 -07:00
|
|
|
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
|
|
|
|
|
Ok([
|
|
|
|
|
serialize(&self.oprf_client.serialize(), 2)?,
|
|
|
|
|
serialize(&self.blinded_element.serialize(), 2)?,
|
2021-02-17 02:47:00 -08:00
|
|
|
]
|
2021-10-25 02:54:32 -07:00
|
|
|
.concat())
|
2021-02-17 02:47:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Deserialization from bytes
|
|
|
|
|
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
|
2021-10-25 02:54:32 -07:00
|
|
|
let (serialized_oprf_client, remainder) = tokenize(input, 2)?;
|
|
|
|
|
let (serialized_blinded_element, remainder) = tokenize(&remainder, 2)?;
|
2021-07-12 12:33:19 -07:00
|
|
|
|
2021-10-25 02:54:32 -07:00
|
|
|
if !remainder.is_empty() {
|
|
|
|
|
return Err(ProtocolError::SerializationError);
|
|
|
|
|
}
|
2021-07-12 12:33:19 -07:00
|
|
|
|
2020-06-05 09:35:14 -07:00
|
|
|
Ok(Self {
|
2021-10-25 02:54:32 -07:00
|
|
|
oprf_client: voprf::NonVerifiableClient::deserialize(&serialized_oprf_client)?,
|
|
|
|
|
blinded_element: voprf::BlindedElement::deserialize(&serialized_blinded_element)?,
|
2020-06-05 09:35:14 -07:00
|
|
|
})
|
|
|
|
|
}
|
2021-06-04 16:29:58 -07:00
|
|
|
|
|
|
|
|
#[cfg(test)]
|
2021-10-25 02:54:32 -07:00
|
|
|
/// Only used for testing zeroize
|
|
|
|
|
pub(crate) fn to_vec(&self) -> Result<Vec<u8>, ProtocolError> {
|
|
|
|
|
Ok([
|
|
|
|
|
self.oprf_client.serialize(),
|
|
|
|
|
self.blinded_element.serialize(),
|
2021-06-04 16:29:58 -07:00
|
|
|
]
|
2021-10-25 02:54:32 -07:00
|
|
|
.concat())
|
2021-06-04 16:29:58 -07:00
|
|
|
}
|
2021-06-15 00:36:17 +02:00
|
|
|
|
2020-06-05 09:35:14 -07:00
|
|
|
/// Returns an initial "blinded" request to send to the server, as well as a ClientRegistration
|
|
|
|
|
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 {
|
2021-10-25 02:54:32 -07:00
|
|
|
message: RegistrationRequest::<CS> {
|
|
|
|
|
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
|
|
|
|
|
/// cryptographic identifiers, to be sent to the server on setup finalization
|
2020-06-14 23:25:31 -07:00
|
|
|
pub fn finish<R: CryptoRng + RngCore>(
|
2020-10-09 10:51:58 -07:00
|
|
|
self,
|
|
|
|
|
rng: &mut R,
|
2021-10-25 02:54:32 -07:00
|
|
|
registration_response: RegistrationResponse<CS>,
|
2021-09-02 11:28:21 +02:00
|
|
|
params: ClientRegistrationFinishParameters<CS>,
|
2021-02-17 02:13:03 -08:00
|
|
|
) -> 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(®istration_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>(
|
|
|
|
|
self.oprf_client.clone(),
|
|
|
|
|
registration_response.evaluation_element,
|
|
|
|
|
params.slow_hash,
|
|
|
|
|
)?;
|
|
|
|
|
|
2021-08-17 05:11:53 +02:00
|
|
|
let mut masking_key = vec![0u8; <CS::Hash as Digest>::OutputSize::USIZE];
|
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-04-16 01:07:07 -07:00
|
|
|
|
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,
|
|
|
|
|
®istration_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,
|
2021-04-16 01:07:07 -07:00
|
|
|
masking_key: GenericArray::clone_from_slice(&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,
|
2021-06-04 16:29:58 -07:00
|
|
|
#[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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-17 02:47:00 -08:00
|
|
|
impl<CS: CipherSuite> ServerRegistration<CS> {
|
|
|
|
|
/// Serialization into bytes
|
2021-10-25 02:54:32 -07:00
|
|
|
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
|
2021-04-16 01:07:07 -07:00
|
|
|
self.0.serialize()
|
2021-02-17 02:47:00 -08:00
|
|
|
}
|
2020-06-05 09:35:14 -07:00
|
|
|
|
2021-02-17 02:47:00 -08:00
|
|
|
/// Deserialization from bytes
|
|
|
|
|
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
|
2021-04-16 01:07:07 -07:00
|
|
|
Ok(Self(RegistrationUpload::deserialize(input)?))
|
2020-06-05 09:35:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// From the client's "blinded" password, returns a response to be
|
|
|
|
|
/// sent back to the client, as well as a ServerRegistration
|
2021-08-04 21:24:46 +02:00
|
|
|
pub fn start<S: SecretKey<CS::KeGroup>>(
|
2021-07-20 14:16:53 +02:00
|
|
|
server_setup: &ServerSetup<CS, S>,
|
2021-02-17 02:13:03 -08:00
|
|
|
message: RegistrationRequest<CS>,
|
2021-04-16 01:07:07 -07:00
|
|
|
credential_identifier: &[u8],
|
2020-12-12 21:53:33 -08:00
|
|
|
) -> Result<ServerRegistrationStartResult<CS>, ProtocolError> {
|
2021-08-04 21:24:46 +02:00
|
|
|
let oprf_key = oprf_key_from_seed::<CS::OprfGroup, CS::Hash>(
|
2021-04-16 01:07:07 -07:00
|
|
|
&server_setup.oprf_seed,
|
|
|
|
|
credential_identifier,
|
|
|
|
|
)?;
|
2020-06-05 09:35:14 -07:00
|
|
|
|
2021-10-25 02:54:32 -07:00
|
|
|
let server = voprf::NonVerifiableServer::new_with_key(&oprf_key)?;
|
|
|
|
|
let evaluate_result = server.evaluate(message.blinded_element, None)?;
|
2020-06-05 09:35:14 -07:00
|
|
|
|
2020-12-12 21:53:33 -08:00
|
|
|
Ok(ServerRegistrationStartResult {
|
|
|
|
|
message: RegistrationResponse {
|
2021-10-25 02:54:32 -07:00
|
|
|
evaluation_element: evaluate_result.message,
|
2021-04-16 01:07:07 -07:00
|
|
|
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)]
|
2021-10-25 02:54:32 -07:00
|
|
|
oprf_key: GenericArray::clone_from_slice(&oprf_key),
|
2020-12-12 21:53:33 -08:00
|
|
|
})
|
2020-06-05 09:35:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// From the client's cryptographic identifiers, fully populates and
|
|
|
|
|
/// returns a ServerRegistration
|
2021-04-16 01:07:07 -07:00
|
|
|
pub fn finish(message: RegistrationUpload<CS>) -> Self {
|
|
|
|
|
Self(message)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Creates a dummy instance used for faking a [CredentialResponse]
|
2021-08-04 21:24:46 +02:00
|
|
|
pub(crate) fn dummy<R: RngCore + CryptoRng, S: SecretKey<CS::KeGroup>>(
|
2021-04-30 15:56:51 -07:00
|
|
|
rng: &mut R,
|
2021-07-20 11:49:37 +02:00
|
|
|
server_setup: &ServerSetup<CS, S>,
|
2021-04-30 15:56:51 -07:00
|
|
|
) -> Self {
|
|
|
|
|
Self(RegistrationUpload::dummy(rng, server_setup))
|
2020-06-05 09:35:14 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Login
|
|
|
|
|
// =====
|
|
|
|
|
|
2021-02-17 02:47:00 -08:00
|
|
|
impl<CS: CipherSuite> ClientLogin<CS> {
|
|
|
|
|
/// Serialization into bytes
|
2021-07-08 11:04:32 -07:00
|
|
|
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
|
2021-02-17 02:47:00 -08:00
|
|
|
let output: Vec<u8> = [
|
2021-10-25 02:54:32 -07:00
|
|
|
serialize(&self.oprf_client.serialize(), 2)?,
|
|
|
|
|
serialize(&self.serialized_credential_request, 2)?,
|
|
|
|
|
serialize(&self.ke1_state.to_bytes(), 2)?,
|
2021-02-17 02:47:00 -08:00
|
|
|
]
|
|
|
|
|
.concat();
|
2021-07-08 11:04:32 -07:00
|
|
|
Ok(output)
|
2021-02-17 02:47:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Deserialization from bytes
|
|
|
|
|
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
|
2021-10-25 02:54:32 -07:00
|
|
|
let (serialized_oprf_client, remainder) = tokenize(input, 2)?;
|
|
|
|
|
let (serialized_credential_request, remainder) = tokenize(&remainder, 2)?;
|
|
|
|
|
let (ke1_state_bytes, remainder) = tokenize(&remainder, 2)?;
|
2020-09-02 10:29:22 -04:00
|
|
|
|
2021-10-25 02:54:32 -07:00
|
|
|
if !remainder.is_empty() {
|
|
|
|
|
return Err(ProtocolError::SerializationError);
|
|
|
|
|
}
|
2021-01-26 09:27:30 -08:00
|
|
|
|
2021-06-14 21:22:21 -07:00
|
|
|
let ke1_state =
|
2021-08-04 21:24:46 +02:00
|
|
|
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State::from_bytes::<CS>(
|
2021-06-14 21:22:21 -07:00
|
|
|
&ke1_state_bytes[..],
|
|
|
|
|
)?;
|
2020-06-05 09:35:14 -07:00
|
|
|
Ok(Self {
|
2021-10-25 02:54:32 -07:00
|
|
|
oprf_client: voprf::NonVerifiableClient::deserialize(&serialized_oprf_client)?,
|
2020-06-05 09:35:14 -07:00
|
|
|
ke1_state,
|
2021-02-02 12:12:20 -08:00
|
|
|
serialized_credential_request,
|
2020-06-05 09:35:14 -07:00
|
|
|
})
|
|
|
|
|
}
|
2021-06-04 16:29:58 -07:00
|
|
|
|
|
|
|
|
#[cfg(test)]
|
2021-10-25 02:54:32 -07:00
|
|
|
/// Only used for testing zeroize
|
|
|
|
|
pub(crate) fn to_vec(&self) -> Result<Vec<u8>, ProtocolError> {
|
|
|
|
|
Ok([
|
|
|
|
|
self.oprf_client.serialize(),
|
|
|
|
|
self.serialized_credential_request.clone(),
|
|
|
|
|
self.ke1_state.to_bytes(),
|
|
|
|
|
]
|
|
|
|
|
.concat())
|
2021-06-04 16:29:58 -07:00
|
|
|
}
|
2020-06-05 09:35:14 -07:00
|
|
|
}
|
|
|
|
|
|
2020-06-14 23:25:31 -07:00
|
|
|
impl<CS: CipherSuite> ClientLogin<CS> {
|
2020-06-05 09:35:14 -07:00
|
|
|
/// Returns an initial "blinded" password request to send to the server, as well as a ClientLogin
|
|
|
|
|
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)?;
|
2021-04-30 15:56:51 -07:00
|
|
|
let (ke1_state, ke1_message) = CS::KeyExchange::generate_ke1(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,
|
|
|
|
|
};
|
|
|
|
|
let serialized_credential_request = credential_request.serialize()?;
|
2020-06-05 09:35:14 -07:00
|
|
|
|
2020-11-16 14:05:43 -08:00
|
|
|
Ok(ClientLoginStartResult {
|
2021-02-02 12:12:20 -08:00
|
|
|
message: credential_request,
|
|
|
|
|
state: Self {
|
2021-10-25 02:54:32 -07:00
|
|
|
oprf_client: blind_result.state,
|
2021-02-02 12:12:20 -08:00
|
|
|
ke1_state,
|
|
|
|
|
serialized_credential_request,
|
|
|
|
|
},
|
2020-11-16 14:05:43 -08:00
|
|
|
})
|
2020-06-05 09:35:14 -07:00
|
|
|
}
|
|
|
|
|
|
2020-07-02 12:24:53 -07:00
|
|
|
/// "Unblinds" the server's answer and returns the opened assets from
|
2020-06-05 09:35:14 -07:00
|
|
|
/// the server
|
2020-11-16 14:05:43 -08:00
|
|
|
pub fn finish(
|
2020-06-05 09:35:14 -07:00
|
|
|
self,
|
2021-04-16 01:07:07 -07:00
|
|
|
credential_response: CredentialResponse<CS>,
|
2021-09-02 11:28:21 +02:00
|
|
|
params: ClientLoginFinishParameters<CS>,
|
2020-07-13 15:23:29 -07:00
|
|
|
) -> Result<ClientLoginFinishResult<CS>, ProtocolError> {
|
2021-07-12 12:33:19 -07:00
|
|
|
// Check if beta value from server is equal to alpha value from client
|
|
|
|
|
let credential_request =
|
|
|
|
|
CredentialRequest::<CS>::deserialize(&self.serialized_credential_request[..])?;
|
2021-10-25 02:54:32 -07:00
|
|
|
if credential_request
|
|
|
|
|
.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>(
|
|
|
|
|
self.oprf_client.clone(),
|
|
|
|
|
credential_response.evaluation_element.clone(),
|
2021-09-02 11:28:21 +02:00
|
|
|
params.slow_hash,
|
2021-04-16 01:07:07 -07:00
|
|
|
)?;
|
|
|
|
|
|
2021-08-17 05:11:53 +02:00
|
|
|
let mut masking_key = vec![0u8; <CS::Hash as Digest>::OutputSize::USIZE];
|
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
|
|
|
|
2021-04-30 15:56:51 -07:00
|
|
|
let (server_s_pk, envelope) = unmask_response::<CS>(
|
2021-04-16 01:07:07 -07:00
|
|
|
&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,
|
2021-04-16 01:07:07 -07:00
|
|
|
err => err,
|
|
|
|
|
})?;
|
|
|
|
|
let server_s_pk_bytes = server_s_pk.to_arr().to_vec();
|
|
|
|
|
|
|
|
|
|
let opened_envelope = &envelope
|
2021-09-02 11:28:21 +02:00
|
|
|
.open(
|
2021-10-25 02:54:32 -07:00
|
|
|
randomized_pwd_hasher,
|
2021-09-02 11:28:21 +02:00
|
|
|
&server_s_pk_bytes,
|
|
|
|
|
¶ms.identifiers,
|
|
|
|
|
)
|
2020-07-02 12:24:53 -07: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-07-02 12:24:53 -07:00
|
|
|
})?;
|
2020-06-05 09:35:14 -07:00
|
|
|
|
2021-04-16 01:07:07 -07:00
|
|
|
let credential_response_component = CredentialResponse::<CS>::serialize_without_ke(
|
2021-10-25 02:54:32 -07:00
|
|
|
&credential_response.evaluation_element.value(),
|
2021-04-16 01:07:07 -07:00
|
|
|
&credential_response.masking_nonce,
|
|
|
|
|
&credential_response.masked_response,
|
|
|
|
|
);
|
2021-01-04 14:27:20 -08:00
|
|
|
|
2021-07-30 12:54:16 +02:00
|
|
|
let result = CS::KeyExchange::generate_ke3(
|
2021-02-19 12:02:44 -08:00
|
|
|
credential_response_component,
|
2021-04-16 01:07:07 -07:00
|
|
|
credential_response.ke2_message,
|
2021-01-14 15:30:37 -08:00
|
|
|
&self.ke1_state,
|
2021-02-02 12:12:20 -08:00
|
|
|
&self.serialized_credential_request,
|
2021-04-16 01:07:07 -07:00
|
|
|
server_s_pk.clone(),
|
2021-04-30 15:56:51 -07:00
|
|
|
opened_envelope.client_static_keypair.private().clone(),
|
|
|
|
|
opened_envelope.id_u.clone(),
|
|
|
|
|
opened_envelope.id_s.clone(),
|
2021-09-02 11:28:21 +02:00
|
|
|
params.context.unwrap_or_default(),
|
2021-01-14 15:30:37 -08:00
|
|
|
)?;
|
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,
|
2021-01-26 09:27:30 -08:00
|
|
|
export_key: opened_envelope.export_key.clone(),
|
2021-04-16 01:07:07 -07:00
|
|
|
server_s_pk,
|
2021-06-04 16:29:58 -07:00
|
|
|
#[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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-13 15:23:29 -07:00
|
|
|
impl<CS: CipherSuite> ServerLogin<CS> {
|
2021-02-17 02:47:00 -08:00
|
|
|
/// Serialization into bytes
|
2021-10-25 02:54:32 -07:00
|
|
|
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
|
|
|
|
|
Ok(self.ke2_state.to_bytes())
|
2020-06-05 09:35:14 -07:00
|
|
|
}
|
|
|
|
|
|
2021-02-17 02:47:00 -08:00
|
|
|
/// Deserialization from bytes
|
|
|
|
|
pub fn deserialize(bytes: &[u8]) -> Result<Self, ProtocolError> {
|
|
|
|
|
Ok(Self {
|
|
|
|
|
_cs: PhantomData,
|
2021-08-04 21:24:46 +02:00
|
|
|
ke2_state:
|
|
|
|
|
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2State::from_bytes::<CS>(
|
|
|
|
|
bytes,
|
|
|
|
|
)?,
|
2021-02-17 02:47:00 -08:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-16 01:07:07 -07:00
|
|
|
/// From the client's "blinded" password, returns a challenge to be
|
2020-06-05 09:35:14 -07:00
|
|
|
/// sent back to the client, as well as a ServerLogin
|
2021-08-04 21:24:46 +02:00
|
|
|
pub fn start<R: RngCore + CryptoRng, S: SecretKey<CS::KeGroup>>(
|
2020-12-12 21:53:33 -08:00
|
|
|
rng: &mut R,
|
2021-07-20 11:49:37 +02:00
|
|
|
server_setup: &ServerSetup<CS, S>,
|
2021-04-16 01:07:07 -07:00
|
|
|
password_file: Option<ServerRegistration<CS>>,
|
2021-10-25 02:54:32 -07:00
|
|
|
credential_request: CredentialRequest<CS>,
|
2021-04-16 01:07:07 -07:00
|
|
|
credential_identifier: &[u8],
|
2020-11-16 14:05:43 -08:00
|
|
|
params: ServerLoginStartParameters,
|
2021-07-20 11:49:37 +02:00
|
|
|
) -> Result<ServerLoginStartResult<CS>, ProtocolError<S::Error>> {
|
2021-04-16 01:07:07 -07:00
|
|
|
let record = match password_file {
|
|
|
|
|
Some(x) => x,
|
2021-04-30 15:56:51 -07:00
|
|
|
None => ServerRegistration::dummy(rng, server_setup),
|
2021-04-16 01:07:07 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let client_s_pk = record.0.client_s_pk.clone();
|
2020-06-05 09:35:14 -07:00
|
|
|
|
2021-04-30 15:56:51 -07:00
|
|
|
let (context, optional_ids) = match params {
|
|
|
|
|
ServerLoginStartParameters::WithContext(context) => (context, None),
|
|
|
|
|
ServerLoginStartParameters::WithIdentifiers(ids) => (Vec::new(), Some(ids)),
|
|
|
|
|
ServerLoginStartParameters::WithContextAndIdentifiers(context, ids) => {
|
|
|
|
|
(context, Some(ids))
|
2020-11-16 14:05:43 -08:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-04-16 01:07:07 -07:00
|
|
|
let server_s_sk = server_setup.keypair.private();
|
2021-07-16 13:54:10 +02:00
|
|
|
let server_s_pk = server_s_sk.public_key()?;
|
2021-04-16 01:07:07 -07:00
|
|
|
|
|
|
|
|
let mut masking_nonce = vec![0u8; 32];
|
|
|
|
|
rng.fill_bytes(&mut masking_nonce);
|
|
|
|
|
|
|
|
|
|
let masked_response = mask_response(
|
|
|
|
|
&record.0.masking_key,
|
|
|
|
|
&masking_nonce,
|
|
|
|
|
&server_s_pk,
|
2021-04-30 15:56:51 -07:00
|
|
|
&record.0.envelope,
|
2021-07-20 11:49:37 +02:00
|
|
|
)
|
|
|
|
|
.map_err(ProtocolError::into_custom)?;
|
2021-04-16 01:07:07 -07:00
|
|
|
|
2021-04-30 15:56:51 -07:00
|
|
|
let (id_u, id_s) = bytestrings_from_identifiers(
|
|
|
|
|
&optional_ids,
|
|
|
|
|
&client_s_pk.to_arr(),
|
|
|
|
|
&server_s_pk.to_arr(),
|
2021-07-20 11:49:37 +02:00
|
|
|
)
|
|
|
|
|
.map_err(ProtocolError::into_custom)?;
|
2020-11-16 14:05:43 -08:00
|
|
|
|
2021-10-25 02:54:32 -07:00
|
|
|
let credential_request_bytes = credential_request
|
|
|
|
|
.serialize()
|
|
|
|
|
.map_err(ProtocolError::into_custom)?;
|
2021-04-16 01:07:07 -07:00
|
|
|
|
2021-08-04 21:24:46 +02:00
|
|
|
let oprf_key = oprf_key_from_seed::<CS::OprfGroup, CS::Hash>(
|
2021-04-16 01:07:07 -07:00
|
|
|
&server_setup.oprf_seed,
|
|
|
|
|
credential_identifier,
|
2021-07-20 11:49:37 +02:00
|
|
|
)
|
|
|
|
|
.map_err(ProtocolError::into_custom)?;
|
2021-10-25 02:54:32 -07:00
|
|
|
let server = voprf::NonVerifiableServer::new_with_key(&oprf_key)
|
|
|
|
|
.map_err(|e| ProtocolError::into_custom(e.into()))?;
|
|
|
|
|
let evaluate_result = server
|
|
|
|
|
.evaluate(credential_request.blinded_element, None)
|
|
|
|
|
.map_err(|e| ProtocolError::into_custom(e.into()))?;
|
|
|
|
|
let evaluation_element = evaluate_result.message;
|
2021-01-04 14:27:20 -08:00
|
|
|
|
2021-10-25 02:54:32 -07:00
|
|
|
let credential_response_component = CredentialResponse::<CS>::serialize_without_ke(
|
|
|
|
|
&evaluation_element.value(),
|
|
|
|
|
&masking_nonce,
|
|
|
|
|
&masked_response,
|
|
|
|
|
);
|
2020-06-05 09:35:14 -07:00
|
|
|
|
2021-07-30 12:54:16 +02:00
|
|
|
let result = CS::KeyExchange::generate_ke2(
|
2020-06-05 09:35:14 -07:00
|
|
|
rng,
|
2021-10-25 02:54:32 -07:00
|
|
|
credential_request_bytes,
|
2021-02-02 12:12:20 -08:00
|
|
|
credential_response_component,
|
2021-10-25 02:54:32 -07:00
|
|
|
credential_request.ke1_message,
|
2021-01-05 10:13:00 -08:00
|
|
|
client_s_pk,
|
2020-06-05 09:35:14 -07:00
|
|
|
server_s_sk.clone(),
|
2020-11-16 14:05:43 -08:00
|
|
|
id_u,
|
|
|
|
|
id_s,
|
2021-04-30 15:56:51 -07:00
|
|
|
context,
|
2020-06-05 09:35:14 -07:00
|
|
|
)?;
|
|
|
|
|
|
2021-02-02 12:12:20 -08:00
|
|
|
let credential_response = CredentialResponse {
|
2021-10-25 02:54:32 -07:00
|
|
|
evaluation_element,
|
2021-04-16 01:07:07 -07:00
|
|
|
masking_nonce,
|
|
|
|
|
masked_response,
|
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 {
|
2021-02-02 12:12:20 -08:00
|
|
|
message: credential_response,
|
2020-12-12 21:53:33 -08:00
|
|
|
state: Self {
|
2020-07-13 15:23:29 -07:00
|
|
|
_cs: PhantomData,
|
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)]
|
2021-10-25 02:54:32 -07:00
|
|
|
oprf_key: GenericArray::clone_from_slice(&oprf_key),
|
2020-11-16 14:05:43 -08:00
|
|
|
})
|
2020-06-05 09:35:14 -07:00
|
|
|
}
|
|
|
|
|
|
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(
|
2021-06-04 16:29:58 -07:00
|
|
|
self,
|
2020-12-12 21:53:33 -08:00
|
|
|
message: CredentialFinalization<CS>,
|
2021-06-04 16:29:58 -07:00
|
|
|
) -> Result<ServerLoginFinishResult<CS>, ProtocolError> {
|
2021-08-04 21:24:46 +02:00
|
|
|
let session_key = <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::finish_ke(
|
2021-01-14 15:30:37 -08:00
|
|
|
message.ke3_message,
|
|
|
|
|
&self.ke2_state,
|
2021-08-22 12:28:19 -07:00
|
|
|
)?;
|
2020-11-16 14:05:43 -08:00
|
|
|
|
2021-06-04 16:29:58 -07:00
|
|
|
Ok(ServerLoginFinishResult {
|
|
|
|
|
session_key,
|
|
|
|
|
_cs: PhantomData,
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
state: self,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-25 16:36:00 -07:00
|
|
|
/////////////////////////
|
|
|
|
|
// Convenience Structs //
|
|
|
|
|
//==================== //
|
|
|
|
|
/////////////////////////
|
2021-06-10 21:48:38 +02:00
|
|
|
|
2021-09-25 16:36:00 -07:00
|
|
|
/// Options for specifying custom identifiers
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub enum Identifiers {
|
|
|
|
|
/// Supply only a client identifier
|
|
|
|
|
ClientIdentifier(Vec<u8>),
|
|
|
|
|
/// Supply only a server identifier
|
|
|
|
|
ServerIdentifier(Vec<u8>),
|
|
|
|
|
/// Supply a client and server identifier
|
|
|
|
|
ClientAndServerIdentifiers(Vec<u8>, Vec<u8>),
|
|
|
|
|
}
|
2021-06-04 16:29:58 -07:00
|
|
|
|
2021-09-25 16:36:00 -07:00
|
|
|
/// Optional parameters for client registration finish
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct ClientRegistrationFinishParameters<'h, CS: CipherSuite> {
|
|
|
|
|
/// Specifying the identifiers idU and idS
|
|
|
|
|
pub identifiers: Option<Identifiers>,
|
|
|
|
|
/// Specifying a configuration for the slow hash
|
|
|
|
|
pub slow_hash: Option<&'h CS::SlowHash>,
|
2021-06-04 16:29:58 -07:00
|
|
|
}
|
|
|
|
|
|
2021-09-25 16:36:00 -07:00
|
|
|
impl<'h, CS: CipherSuite> Default for ClientRegistrationFinishParameters<'h, CS> {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
identifiers: None,
|
|
|
|
|
slow_hash: None,
|
|
|
|
|
}
|
2021-06-04 16:29:58 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-25 16:36:00 -07:00
|
|
|
impl<'h, CS: CipherSuite> ClientRegistrationFinishParameters<'h, CS> {
|
|
|
|
|
/// Create a new [`ClientRegistrationFinishParameters`]
|
|
|
|
|
pub fn new(identifiers: Option<Identifiers>, slow_hash: Option<&'h CS::SlowHash>) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
identifiers,
|
|
|
|
|
slow_hash,
|
|
|
|
|
}
|
2021-06-04 16:29:58 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-25 16:36:00 -07:00
|
|
|
/// Contains the fields that are returned by a client registration start
|
|
|
|
|
pub struct ClientRegistrationStartResult<CS: CipherSuite> {
|
|
|
|
|
/// The registration request message to be sent to the server
|
|
|
|
|
pub message: RegistrationRequest<CS>,
|
|
|
|
|
/// The client state that must be persisted in order to complete registration
|
|
|
|
|
pub state: ClientRegistration<CS>,
|
2021-06-04 16:29:58 -07:00
|
|
|
}
|
|
|
|
|
|
2021-09-25 16:36:00 -07:00
|
|
|
// Cannot be derived because it would require for CS to be Clone.
|
|
|
|
|
impl<CS: CipherSuite> Clone for ClientRegistrationStartResult<CS> {
|
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
message: self.message.clone(),
|
|
|
|
|
state: self.state.clone(),
|
|
|
|
|
}
|
2021-06-04 16:29:58 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-25 16:36:00 -07:00
|
|
|
/// Contains the fields that are returned by a client registration finish
|
|
|
|
|
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
|
|
|
|
|
pub export_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
|
|
|
|
/// The server's static public key
|
|
|
|
|
pub server_s_pk: PublicKey<CS::KeGroup>,
|
|
|
|
|
/// Instance of the ClientRegistration, only used in tests for checking zeroize
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub state: ClientRegistration<CS>,
|
|
|
|
|
/// AuthKey, only used in tests
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub auth_key: Vec<u8>,
|
|
|
|
|
/// Password derived key, only used in tests
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub randomized_pwd: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cannot be derived because it would require for CS to be Clone.
|
|
|
|
|
impl<CS: CipherSuite> Clone for ClientRegistrationFinishResult<CS> {
|
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
message: self.message.clone(),
|
|
|
|
|
export_key: self.export_key.clone(),
|
|
|
|
|
server_s_pk: self.server_s_pk.clone(),
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
state: self.state.clone(),
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
auth_key: self.auth_key.clone(),
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
randomized_pwd: self.randomized_pwd.clone(),
|
|
|
|
|
}
|
2021-06-04 16:29:58 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-25 16:36:00 -07:00
|
|
|
/// Contains the fields that are returned by a server registration start.
|
|
|
|
|
/// Note that there is no state output in this step
|
|
|
|
|
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)]
|
|
|
|
|
pub oprf_key: GenericArray<u8, <CS::OprfGroup as Group>::ScalarLen>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cannot be derived because it would require for CS to be Clone.
|
|
|
|
|
impl<CS: CipherSuite> Clone for ServerRegistrationStartResult<CS> {
|
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
message: self.message.clone(),
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
oprf_key: self.oprf_key.clone(),
|
|
|
|
|
}
|
2021-06-04 16:29:58 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-25 16:36:00 -07:00
|
|
|
/// Contains the fields that are returned by a client login start
|
|
|
|
|
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>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cannot be derived because it would require for CS to be Clone.
|
|
|
|
|
impl<CS: CipherSuite> Clone for ClientLoginStartResult<CS> {
|
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
message: self.message.clone(),
|
|
|
|
|
state: self.state.clone(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Optional parameters for client login finish
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct ClientLoginFinishParameters<'h, CS: CipherSuite> {
|
|
|
|
|
/// Specifying a context field that the server must agree on
|
|
|
|
|
pub context: Option<Vec<u8>>,
|
|
|
|
|
/// Specifying a user identifier and server identifier that will be matched against the server
|
|
|
|
|
pub identifiers: Option<Identifiers>,
|
|
|
|
|
/// Specifying a configuration for the slow hash
|
|
|
|
|
pub slow_hash: Option<&'h CS::SlowHash>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'h, CS: CipherSuite> Default for ClientLoginFinishParameters<'h, CS> {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
context: None,
|
|
|
|
|
identifiers: None,
|
|
|
|
|
slow_hash: None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'h, CS: CipherSuite> ClientLoginFinishParameters<'h, CS> {
|
|
|
|
|
/// Create a new [`ClientLoginFinishParameters`]
|
|
|
|
|
pub fn new(
|
|
|
|
|
context: Option<Vec<u8>>,
|
|
|
|
|
identifiers: Option<Identifiers>,
|
|
|
|
|
slow_hash: Option<&'h CS::SlowHash>,
|
|
|
|
|
) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
context,
|
|
|
|
|
identifiers,
|
|
|
|
|
slow_hash,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Contains the fields that are returned by a client login finish
|
|
|
|
|
pub struct ClientLoginFinishResult<CS: CipherSuite> {
|
|
|
|
|
/// The message to send to the server to complete the protocol
|
|
|
|
|
pub message: CredentialFinalization<CS>,
|
|
|
|
|
/// The session key
|
|
|
|
|
pub session_key: Vec<u8>,
|
|
|
|
|
/// The client-side export key
|
|
|
|
|
pub export_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
|
|
|
|
/// 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)]
|
|
|
|
|
pub handshake_secret: Vec<u8>,
|
|
|
|
|
/// Client MAC key, only used in tests
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub client_mac_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cannot be derived because it would require for CS to be Clone.
|
|
|
|
|
impl<CS: CipherSuite> Clone for ClientLoginFinishResult<CS> {
|
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
message: self.message.clone(),
|
|
|
|
|
session_key: self.session_key.clone(),
|
|
|
|
|
export_key: self.export_key.clone(),
|
|
|
|
|
server_s_pk: self.server_s_pk.clone(),
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
state: self.state.clone(),
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
handshake_secret: self.handshake_secret.clone(),
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
client_mac_key: self.client_mac_key.clone(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Contains the fields that are returned by a server login finish
|
|
|
|
|
pub struct ServerLoginFinishResult<CS: CipherSuite> {
|
|
|
|
|
/// The session key between client and server
|
|
|
|
|
pub session_key: Vec<u8>,
|
|
|
|
|
_cs: PhantomData<CS>,
|
|
|
|
|
/// Instance of the ClientRegistration, only used in tests for checking zeroize
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub state: ServerLogin<CS>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cannot be derived because it would require for CS to be Clone.
|
|
|
|
|
impl<CS: CipherSuite> Clone for ServerLoginFinishResult<CS> {
|
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
session_key: self.session_key.clone(),
|
|
|
|
|
_cs: PhantomData,
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
state: self.state.clone(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Optional parameters for server login start
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub enum ServerLoginStartParameters {
|
|
|
|
|
/// Specifying a context field that the client must agree on
|
|
|
|
|
WithContext(Vec<u8>),
|
|
|
|
|
/// Specifying a user identifier and server identifier that will be matched against the client
|
|
|
|
|
WithIdentifiers(Identifiers),
|
|
|
|
|
/// Specifying a context field that the client must agree on,
|
|
|
|
|
/// along with a user identifier and and server identifier that will be matched against the client
|
|
|
|
|
/// (in that order)
|
|
|
|
|
WithContextAndIdentifiers(Vec<u8>, Identifiers),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for ServerLoginStartParameters {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self::WithContext(Vec::new())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Contains the fields that are returned by a server login start
|
|
|
|
|
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)]
|
|
|
|
|
pub handshake_secret: Vec<u8>,
|
|
|
|
|
/// Server MAC key, only used in tests
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub server_mac_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
|
|
|
|
/// OPRF key, only used in tests
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
pub oprf_key: GenericArray<u8, <CS::OprfGroup as Group>::ScalarLen>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cannot be derived because it would require for CS to be Clone.
|
|
|
|
|
impl<CS: CipherSuite> Clone for ServerLoginStartResult<CS> {
|
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
message: self.message.clone(),
|
|
|
|
|
state: self.state.clone(),
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
handshake_secret: self.handshake_secret.clone(),
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
server_mac_key: self.server_mac_key.clone(),
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
oprf_key: self.oprf_key.clone(),
|
|
|
|
|
}
|
2020-06-05 09:35:14 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-25 16:36:00 -07:00
|
|
|
////////////////////////////////////////////////
|
|
|
|
|
// Helper functions and Trait Implementations //
|
|
|
|
|
// ========================================== //
|
|
|
|
|
////////////////////////////////////////////////
|
|
|
|
|
|
2020-06-05 09:35:14 -07:00
|
|
|
// Helper functions
|
2021-06-04 16:29:58 -07:00
|
|
|
|
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>(
|
2021-10-25 02:54:32 -07:00
|
|
|
oprf_client: voprf::NonVerifiableClient<CS::OprfGroup, CS::Hash>,
|
|
|
|
|
evaluation_element: voprf::EvaluationElement<CS::OprfGroup, CS::Hash>,
|
2021-09-02 11:28:21 +02:00
|
|
|
slow_hash: Option<&CS::SlowHash>,
|
2021-10-25 02:54:32 -07:00
|
|
|
) -> Result<
|
|
|
|
|
(
|
|
|
|
|
GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
|
|
|
|
Hkdf<CS::Hash>,
|
|
|
|
|
),
|
|
|
|
|
ProtocolError,
|
|
|
|
|
> {
|
|
|
|
|
let oprf_output = oprf_client.finalize(evaluation_element, None)?;
|
2021-09-02 11:28:21 +02:00
|
|
|
|
2021-10-25 02:54:32 -07:00
|
|
|
let hardened_output = if let Some(slow_hash) = slow_hash {
|
|
|
|
|
slow_hash.hash(oprf_output.clone())
|
2021-09-02 11:28:21 +02:00
|
|
|
} else {
|
2021-10-25 02:54:32 -07:00
|
|
|
CS::SlowHash::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)?;
|
|
|
|
|
|
|
|
|
|
Ok(Hkdf::<CS::Hash>::extract(
|
|
|
|
|
None,
|
|
|
|
|
&[oprf_output.to_vec(), hardened_output].concat(),
|
|
|
|
|
))
|
2020-06-05 09:35:14 -07:00
|
|
|
}
|
2021-04-16 01:07:07 -07:00
|
|
|
|
2021-08-02 02:48:56 +02:00
|
|
|
fn oprf_key_from_seed<G: Group, D: Hash>(
|
2021-04-16 01:07:07 -07:00
|
|
|
oprf_seed: &GenericArray<u8, D::OutputSize>,
|
|
|
|
|
credential_identifier: &[u8],
|
2021-10-25 02:54:32 -07:00
|
|
|
) -> Result<Vec<u8>, ProtocolError> {
|
2021-08-17 05:11:53 +02:00
|
|
|
let mut ikm = vec![0u8; G::ScalarLen::USIZE];
|
2021-04-16 01:07:07 -07:00
|
|
|
Hkdf::<D>::from_prk(oprf_seed)
|
2021-08-22 12:28:19 -07:00
|
|
|
.map_err(|_| InternalError::HkdfError)?
|
2021-07-12 15:07:29 -07:00
|
|
|
.expand(&[credential_identifier, STR_OPRF_KEY].concat(), &mut ikm)
|
2021-08-22 12:28:19 -07:00
|
|
|
.map_err(|_| InternalError::HkdfError)?;
|
2021-10-25 02:54:32 -07:00
|
|
|
Ok(G::scalar_as_bytes(G::hash_to_scalar::<D, _, _>(
|
|
|
|
|
Some(&ikm[..]),
|
|
|
|
|
GenericArray::from(*STR_OPAQUE_DERIVE_KEY_PAIR),
|
|
|
|
|
)?)
|
|
|
|
|
.to_vec())
|
2021-04-16 01:07:07 -07:00
|
|
|
}
|
|
|
|
|
|
2021-04-30 15:56:51 -07:00
|
|
|
fn mask_response<CS: CipherSuite>(
|
2021-04-16 01:07:07 -07:00
|
|
|
masking_key: &[u8],
|
|
|
|
|
masking_nonce: &[u8],
|
2021-08-04 21:24:46 +02:00
|
|
|
server_s_pk: &PublicKey<CS::KeGroup>,
|
2021-04-30 15:56:51 -07:00
|
|
|
envelope: &Envelope<CS>,
|
2021-04-16 01:07:07 -07:00
|
|
|
) -> Result<Vec<u8>, ProtocolError> {
|
2021-10-25 02:54:32 -07:00
|
|
|
let mut xor_pad = vec![0u8; <CS::KeGroup as KeGroup>::PkLen::USIZE + Envelope::<CS>::len()];
|
2021-04-30 15:56:51 -07:00
|
|
|
Hkdf::<CS::Hash>::from_prk(masking_key)
|
2021-08-22 12:28:19 -07:00
|
|
|
.map_err(|_| InternalError::HkdfError)?
|
2021-04-16 01:07:07 -07:00
|
|
|
.expand(
|
2021-04-30 15:56:51 -07:00
|
|
|
&[masking_nonce, STR_CREDENTIAL_RESPONSE_PAD].concat(),
|
2021-04-16 01:07:07 -07:00
|
|
|
&mut xor_pad,
|
|
|
|
|
)
|
2021-08-22 12:28:19 -07:00
|
|
|
.map_err(|_| InternalError::HkdfError)?;
|
2021-04-16 01:07:07 -07:00
|
|
|
|
|
|
|
|
let plaintext = [&server_s_pk.to_arr()[..], &envelope.serialize()].concat();
|
|
|
|
|
|
|
|
|
|
Ok(xor_pad
|
|
|
|
|
.iter()
|
|
|
|
|
.zip(plaintext.iter())
|
|
|
|
|
.map(|(&x1, &x2)| x1 ^ x2)
|
|
|
|
|
.collect())
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-30 15:56:51 -07:00
|
|
|
fn unmask_response<CS: CipherSuite>(
|
2021-04-16 01:07:07 -07:00
|
|
|
masking_key: &[u8],
|
|
|
|
|
masking_nonce: &[u8],
|
|
|
|
|
masked_response: &[u8],
|
2021-08-04 21:24:46 +02:00
|
|
|
) -> Result<(PublicKey<CS::KeGroup>, Envelope<CS>), ProtocolError> {
|
2021-10-25 02:54:32 -07:00
|
|
|
let mut xor_pad = vec![0u8; <CS::KeGroup as KeGroup>::PkLen::USIZE + Envelope::<CS>::len()];
|
2021-04-30 15:56:51 -07:00
|
|
|
Hkdf::<CS::Hash>::from_prk(masking_key)
|
2021-08-22 12:28:19 -07:00
|
|
|
.map_err(|_| InternalError::HkdfError)?
|
2021-04-16 01:07:07 -07:00
|
|
|
.expand(
|
2021-04-30 15:56:51 -07:00
|
|
|
&[masking_nonce, STR_CREDENTIAL_RESPONSE_PAD].concat(),
|
2021-04-16 01:07:07 -07:00
|
|
|
&mut xor_pad,
|
|
|
|
|
)
|
2021-08-22 12:28:19 -07:00
|
|
|
.map_err(|_| InternalError::HkdfError)?;
|
2021-04-16 01:07:07 -07:00
|
|
|
let plaintext: Vec<u8> = xor_pad
|
|
|
|
|
.iter()
|
|
|
|
|
.zip(masked_response.iter())
|
|
|
|
|
.map(|(&x1, &x2)| x1 ^ x2)
|
|
|
|
|
.collect();
|
2021-10-25 02:54:32 -07:00
|
|
|
let key_len = <CS::KeGroup as KeGroup>::PkLen::USIZE;
|
2021-08-17 05:11:53 +02:00
|
|
|
let unchecked_server_s_pk = PublicKey::from_bytes(&plaintext[..key_len])?;
|
2021-04-16 01:07:07 -07:00
|
|
|
let envelope = Envelope::deserialize(&plaintext[key_len..])?;
|
|
|
|
|
|
2021-04-30 15:56:51 -07:00
|
|
|
// Ensure that public key is valid
|
2021-08-04 21:24:46 +02:00
|
|
|
let server_s_pk = KeyPair::<CS::KeGroup>::check_public_key(unchecked_server_s_pk)
|
2021-08-22 12:28:19 -07:00
|
|
|
.map_err(|_| ProtocolError::SerializationError)?;
|
2021-04-30 15:56:51 -07:00
|
|
|
|
|
|
|
|
Ok((server_s_pk, envelope))
|
2021-04-16 01:07:07 -07:00
|
|
|
}
|
2021-09-25 16:36:00 -07:00
|
|
|
|
|
|
|
|
pub(crate) fn bytestrings_from_identifiers(
|
|
|
|
|
ids: &Option<Identifiers>,
|
|
|
|
|
client_s_pk: &[u8],
|
|
|
|
|
server_s_pk: &[u8],
|
|
|
|
|
) -> Result<(Vec<u8>, Vec<u8>), ProtocolError> {
|
|
|
|
|
let (client_identity, server_identity): (Vec<u8>, Vec<u8>) = match ids {
|
|
|
|
|
None => (client_s_pk.to_vec(), server_s_pk.to_vec()),
|
|
|
|
|
Some(Identifiers::ClientIdentifier(id_u)) => (id_u.clone(), server_s_pk.to_vec()),
|
|
|
|
|
Some(Identifiers::ServerIdentifier(id_s)) => (client_s_pk.to_vec(), id_s.clone()),
|
|
|
|
|
Some(Identifiers::ClientAndServerIdentifiers(id_u, id_s)) => (id_u.clone(), id_s.clone()),
|
|
|
|
|
};
|
|
|
|
|
Ok((
|
|
|
|
|
serialize(&client_identity, 2)?,
|
|
|
|
|
serialize(&server_identity, 2)?,
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-25 02:54:32 -07: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.
|
|
|
|
|
fn blind<CS: CipherSuite, R: RngCore + CryptoRng>(
|
|
|
|
|
rng: &mut R,
|
|
|
|
|
password: &[u8],
|
|
|
|
|
) -> Result<
|
|
|
|
|
voprf::NonVerifiableClientBlindResult<CS::OprfGroup, CS::Hash>,
|
|
|
|
|
voprf::errors::InternalError,
|
|
|
|
|
> {
|
|
|
|
|
#[cfg(not(test))]
|
|
|
|
|
let result = voprf::NonVerifiableClient::blind(password.to_vec(), rng)?;
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
let result = {
|
|
|
|
|
let mut blind_bytes = vec![0u8; <CS::OprfGroup as Group>::ScalarLen::USIZE];
|
|
|
|
|
let blind = loop {
|
|
|
|
|
rng.fill_bytes(&mut blind_bytes);
|
|
|
|
|
let scalar = <CS::OprfGroup as Group>::from_scalar_slice_unchecked(
|
|
|
|
|
&GenericArray::clone_from_slice(&blind_bytes),
|
|
|
|
|
)?;
|
|
|
|
|
match scalar
|
|
|
|
|
.ct_eq(&<CS::OprfGroup as Group>::scalar_zero())
|
|
|
|
|
.into()
|
|
|
|
|
{
|
|
|
|
|
false => break scalar,
|
|
|
|
|
true => (),
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
voprf::NonVerifiableClient::deterministic_blind_unchecked(password.to_vec(), blind)?
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Ok(result)
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-25 16:36:00 -07:00
|
|
|
// Zeroize on drop implementations
|
|
|
|
|
|
|
|
|
|
// This can't be derived because of the use of a phantom parameter
|
|
|
|
|
impl<CS: CipherSuite> Zeroize for ClientRegistration<CS> {
|
|
|
|
|
fn zeroize(&mut self) {
|
2021-10-25 02:54:32 -07:00
|
|
|
self.oprf_client.zeroize();
|
|
|
|
|
self.blinded_element.zeroize();
|
2021-09-25 16:36:00 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<CS: CipherSuite> Drop for ClientRegistration<CS> {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
self.zeroize();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This can't be derived because of the use of a phantom parameter
|
|
|
|
|
impl<CS: CipherSuite> Zeroize for ServerRegistration<CS> {
|
|
|
|
|
fn zeroize(&mut self) {
|
|
|
|
|
self.0.envelope.zeroize();
|
|
|
|
|
self.0.masking_key.zeroize();
|
|
|
|
|
self.0.client_s_pk.zeroize();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<CS: CipherSuite> Drop for ServerRegistration<CS> {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
self.zeroize();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This can't be derived because of the use of a phantom parameter
|
|
|
|
|
impl<CS: CipherSuite> Zeroize for ClientLogin<CS> {
|
|
|
|
|
fn zeroize(&mut self) {
|
2021-10-25 02:54:32 -07:00
|
|
|
self.oprf_client.zeroize();
|
2021-09-25 16:36:00 -07:00
|
|
|
self.ke1_state.zeroize();
|
|
|
|
|
self.serialized_credential_request.zeroize();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<CS: CipherSuite> Drop for ClientLogin<CS> {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
self.zeroize();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This can't be derived because of the use of a phantom parameter
|
|
|
|
|
impl<CS: CipherSuite> Zeroize for ServerLogin<CS> {
|
|
|
|
|
fn zeroize(&mut self) {
|
|
|
|
|
self.ke2_state.zeroize();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<CS: CipherSuite> Drop for ServerLogin<CS> {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
self.zeroize();
|
|
|
|
|
}
|
|
|
|
|
}
|