Adding client enumeration mitigations (#153)
This commit is contained in:
+56
-39
@@ -10,14 +10,16 @@ use crate::{
|
||||
envelope::Envelope,
|
||||
errors::{
|
||||
utils::{check_slice_size, check_slice_size_atleast},
|
||||
PakeError, ProtocolError,
|
||||
ProtocolError, PakeError,
|
||||
},
|
||||
group::Group,
|
||||
key_exchange::traits::{FromBytes, KeyExchange, ToBytes},
|
||||
keypair::{KeyPair, PublicKey, SizedBytesExt},
|
||||
};
|
||||
use digest::Digest;
|
||||
use generic_array::{typenum::Unsigned, GenericArray};
|
||||
use generic_bytes::SizedBytes;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
// Messages
|
||||
// =========
|
||||
@@ -127,6 +129,8 @@ pub struct RegistrationUpload<CS: CipherSuite> {
|
||||
/// The "envelope" generated by the user, containing sealed
|
||||
/// cryptographic identifiers
|
||||
pub(crate) envelope: Envelope<CS::Hash>,
|
||||
/// The masking key used to mask the envelope
|
||||
pub(crate) masking_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
||||
/// The user's public key
|
||||
pub(crate) client_s_pk: PublicKey,
|
||||
}
|
||||
@@ -136,6 +140,7 @@ impl<CS: CipherSuite> Clone for RegistrationUpload<CS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
envelope: self.envelope.clone(),
|
||||
masking_key: self.masking_key.clone(),
|
||||
client_s_pk: self.client_s_pk.clone(),
|
||||
}
|
||||
}
|
||||
@@ -146,6 +151,7 @@ impl<CS: CipherSuite> RegistrationUpload<CS> {
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
[
|
||||
self.client_s_pk.to_arr().to_vec(),
|
||||
self.masking_key.to_vec(),
|
||||
self.envelope.serialize(),
|
||||
]
|
||||
.concat()
|
||||
@@ -154,22 +160,37 @@ impl<CS: CipherSuite> RegistrationUpload<CS> {
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
|
||||
let key_len = <PublicKey as SizedBytes>::Len::to_usize();
|
||||
|
||||
let checked_slice = check_slice_size_atleast(input, key_len, "registration_upload_bytes")?;
|
||||
|
||||
let (envelope, remainder) = Envelope::<CS::Hash>::deserialize(&checked_slice[key_len..])?;
|
||||
|
||||
if !remainder.is_empty() {
|
||||
return Err(PakeError::SerializationError.into());
|
||||
}
|
||||
|
||||
let hash_len = <CS::Hash as Digest>::OutputSize::to_usize();
|
||||
let checked_slice = check_slice_size(
|
||||
&input,
|
||||
key_len + hash_len + Envelope::<CS::Hash>::len(),
|
||||
"registration_upload_bytes",
|
||||
)?;
|
||||
let envelope = Envelope::<CS::Hash>::deserialize(&checked_slice[key_len + hash_len..])?;
|
||||
Ok(Self {
|
||||
envelope,
|
||||
masking_key: GenericArray::clone_from_slice(
|
||||
&checked_slice[key_len..key_len + hash_len],
|
||||
),
|
||||
client_s_pk: KeyPair::<CS::Group>::check_public_key(PublicKey::from_bytes(
|
||||
&checked_slice[..key_len],
|
||||
)?)?,
|
||||
})
|
||||
}
|
||||
|
||||
// Creates a dummy instance used for faking a [CredentialResponse]
|
||||
pub(crate) fn dummy<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
|
||||
let mut masking_key = vec![0u8; <CS::Hash as Digest>::OutputSize::to_usize()];
|
||||
rng.fill_bytes(&mut masking_key);
|
||||
|
||||
let keypair = KeyPair::<CS::Group>::generate_random(rng);
|
||||
|
||||
Self {
|
||||
envelope: Envelope::<CS::Hash>::dummy(),
|
||||
masking_key: GenericArray::clone_from_slice(&masking_key),
|
||||
client_s_pk: keypair.public().clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_serialize_and_deserialize_for!(RegistrationUpload);
|
||||
@@ -229,9 +250,8 @@ impl_serialize_and_deserialize_for!(CredentialRequest);
|
||||
pub struct CredentialResponse<CS: CipherSuite> {
|
||||
/// the server's oprf output
|
||||
pub(crate) beta: CS::Group,
|
||||
pub(crate) server_s_pk: PublicKey,
|
||||
/// the user's sealed information,
|
||||
pub(crate) envelope: Envelope<CS::Hash>,
|
||||
pub(crate) masking_nonce: Vec<u8>,
|
||||
pub(crate) masked_response: Vec<u8>,
|
||||
pub(crate) ke2_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE2Message,
|
||||
}
|
||||
|
||||
@@ -240,8 +260,8 @@ impl<CS: CipherSuite> Clone for CredentialResponse<CS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
beta: self.beta,
|
||||
server_s_pk: self.server_s_pk.clone(),
|
||||
envelope: self.envelope.clone(),
|
||||
masking_nonce: self.masking_nonce.clone(),
|
||||
masked_response: self.masked_response.clone(),
|
||||
ke2_message: self.ke2_message.clone(),
|
||||
}
|
||||
}
|
||||
@@ -251,7 +271,7 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
[
|
||||
Self::serialize_without_ke(&self.beta, &self.server_s_pk, &self.envelope),
|
||||
Self::serialize_without_ke(&self.beta, &self.masking_nonce, &self.masked_response),
|
||||
self.ke2_message.to_bytes(),
|
||||
]
|
||||
.concat()
|
||||
@@ -259,23 +279,26 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
|
||||
|
||||
pub(crate) fn serialize_without_ke(
|
||||
beta: &CS::Group,
|
||||
server_s_pk: &PublicKey,
|
||||
envelope: &Envelope<CS::Hash>,
|
||||
masking_nonce: &[u8],
|
||||
masked_response: &[u8],
|
||||
) -> Vec<u8> {
|
||||
[
|
||||
&beta.to_arr(),
|
||||
&server_s_pk.to_arr()[..],
|
||||
&envelope.to_bytes(),
|
||||
]
|
||||
.concat()
|
||||
[&beta.to_arr(), masking_nonce, masked_response].concat()
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
|
||||
let elem_len = <CS::Group as Group>::ElemLen::to_usize();
|
||||
let key_len = <PublicKey as SizedBytes>::Len::to_usize();
|
||||
let checked_slice =
|
||||
check_slice_size_atleast(input, elem_len + key_len, "login_second_message_bytes")?;
|
||||
let nonce_len: usize = 32;
|
||||
let envelope_len = Envelope::<CS::Hash>::len();
|
||||
let masked_response_len = key_len + envelope_len;
|
||||
let ke2_message_len = CS::KeyExchange::ke2_message_size();
|
||||
|
||||
let checked_slice = check_slice_size_atleast(
|
||||
input,
|
||||
elem_len + nonce_len + masked_response_len + ke2_message_len,
|
||||
"credential_response_bytes",
|
||||
)?;
|
||||
|
||||
// Check that the message is actually containing an element of the
|
||||
// correct subgroup
|
||||
@@ -288,25 +311,19 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
|
||||
return Err(PakeError::IdentityGroupElementError.into());
|
||||
}
|
||||
|
||||
let unchecked_server_s_pk =
|
||||
PublicKey::from_bytes(&checked_slice[elem_len..elem_len + key_len])?;
|
||||
let server_s_pk = KeyPair::<CS::Group>::check_public_key(unchecked_server_s_pk)?;
|
||||
|
||||
let (envelope, remainder) =
|
||||
Envelope::<CS::Hash>::deserialize(&checked_slice[elem_len + key_len..])?;
|
||||
|
||||
let ke2_message_size = CS::KeyExchange::ke2_message_size();
|
||||
let checked_remainder =
|
||||
check_slice_size_atleast(&remainder, ke2_message_size, "login_second_message_bytes")?;
|
||||
let masking_nonce = checked_slice[elem_len..elem_len + nonce_len].to_vec();
|
||||
let masked_response = checked_slice
|
||||
[elem_len + nonce_len..elem_len + nonce_len + masked_response_len]
|
||||
.to_vec();
|
||||
let ke2_message =
|
||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE2Message::from_bytes::<CS>(
|
||||
checked_remainder,
|
||||
&checked_slice[elem_len + nonce_len + masked_response_len..],
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
beta,
|
||||
server_s_pk,
|
||||
envelope,
|
||||
masking_nonce,
|
||||
masked_response,
|
||||
ke2_message,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user