General cleanups and reorganizing code (#236)
This commit is contained in:
@@ -47,7 +47,8 @@ To learn more about contributing to this project, [see this document](./CONTRIBU
|
|||||||
#### Acknowledgments
|
#### Acknowledgments
|
||||||
|
|
||||||
Special thanks go to Hugo Krawczyk and Chris Wood for helping to clarify discrepancies and making suggestions for improving
|
Special thanks go to Hugo Krawczyk and Chris Wood for helping to clarify discrepancies and making suggestions for improving
|
||||||
this implementation.
|
this implementation. Additional credit goes to @daxpedda for adding no_std support, p256 support, and making other general
|
||||||
|
improvements to the library.
|
||||||
|
|
||||||
|
|
||||||
License
|
License
|
||||||
|
|||||||
+88
-89
@@ -26,45 +26,8 @@ const STR_AUTH_KEY: &[u8] = b"AuthKey";
|
|||||||
const STR_EXPORT_KEY: &[u8] = b"ExportKey";
|
const STR_EXPORT_KEY: &[u8] = b"ExportKey";
|
||||||
const STR_PRIVATE_KEY: &[u8] = b"PrivateKey";
|
const STR_PRIVATE_KEY: &[u8] = b"PrivateKey";
|
||||||
const STR_OPAQUE_DERIVE_AUTH_KEY_PAIR: &[u8] = b"OPAQUE-DeriveAuthKeyPair";
|
const STR_OPAQUE_DERIVE_AUTH_KEY_PAIR: &[u8] = b"OPAQUE-DeriveAuthKeyPair";
|
||||||
|
|
||||||
const NONCE_LEN: usize = 32;
|
const NONCE_LEN: usize = 32;
|
||||||
|
|
||||||
fn build_inner_envelope_internal<CS: CipherSuite>(
|
|
||||||
random_pwd: &[u8],
|
|
||||||
nonce: &[u8],
|
|
||||||
) -> Result<PublicKey<CS::KeGroup>, ProtocolError> {
|
|
||||||
let h = Hkdf::<CS::Hash>::new(None, random_pwd);
|
|
||||||
let mut keypair_seed = vec![0u8; <CS::KeGroup as Group>::ScalarLen::USIZE];
|
|
||||||
h.expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed)
|
|
||||||
.map_err(|_| InternalError::HkdfError)?;
|
|
||||||
let client_static_keypair = KeyPair::<CS::KeGroup>::from_private_key_slice(
|
|
||||||
&CS::OprfGroup::scalar_as_bytes(CS::OprfGroup::hash_to_scalar::<CS::Hash>(
|
|
||||||
&keypair_seed[..],
|
|
||||||
STR_OPAQUE_DERIVE_AUTH_KEY_PAIR,
|
|
||||||
)?),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(client_static_keypair.public().clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn recover_keys_internal<CS: CipherSuite>(
|
|
||||||
random_pwd: &[u8],
|
|
||||||
nonce: &[u8],
|
|
||||||
) -> Result<KeyPair<CS::KeGroup>, ProtocolError> {
|
|
||||||
let h = Hkdf::<CS::Hash>::new(None, random_pwd);
|
|
||||||
let mut keypair_seed = vec![0u8; <CS::KeGroup as Group>::ScalarLen::USIZE];
|
|
||||||
h.expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed)
|
|
||||||
.map_err(|_| InternalError::HkdfError)?;
|
|
||||||
let client_static_keypair = KeyPair::<CS::KeGroup>::from_private_key_slice(
|
|
||||||
&CS::OprfGroup::scalar_as_bytes(CS::OprfGroup::hash_to_scalar::<CS::Hash>(
|
|
||||||
&keypair_seed[..],
|
|
||||||
STR_OPAQUE_DERIVE_AUTH_KEY_PAIR,
|
|
||||||
)?),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(client_static_keypair)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
|
#[derive(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
|
||||||
#[zeroize(drop)]
|
#[zeroize(drop)]
|
||||||
pub(crate) enum InnerEnvelopeMode {
|
pub(crate) enum InnerEnvelopeMode {
|
||||||
@@ -151,58 +114,6 @@ type SealResult<CS> = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
impl<CS: CipherSuite> Envelope<CS> {
|
impl<CS: CipherSuite> Envelope<CS> {
|
||||||
fn hmac_key_size() -> usize {
|
|
||||||
<CS::Hash as Digest>::OutputSize::USIZE
|
|
||||||
}
|
|
||||||
|
|
||||||
fn export_key_size() -> usize {
|
|
||||||
<CS::Hash as Digest>::OutputSize::USIZE
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn len() -> usize {
|
|
||||||
<CS::Hash as Digest>::OutputSize::USIZE + NONCE_LEN
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn serialize(&self) -> Vec<u8> {
|
|
||||||
[&self.nonce[..], &self.hmac[..]].concat()
|
|
||||||
}
|
|
||||||
pub(crate) fn deserialize(bytes: &[u8]) -> Result<Self, ProtocolError> {
|
|
||||||
let mode = InnerEnvelopeMode::Internal; // Better way to hard-code this?
|
|
||||||
|
|
||||||
if bytes.len() < NONCE_LEN {
|
|
||||||
return Err(ProtocolError::SerializationError);
|
|
||||||
}
|
|
||||||
let nonce = bytes[..NONCE_LEN].to_vec();
|
|
||||||
|
|
||||||
let remainder = match mode {
|
|
||||||
InnerEnvelopeMode::Zero => {
|
|
||||||
return Err(InternalError::IncompatibleEnvelopeModeError.into())
|
|
||||||
}
|
|
||||||
InnerEnvelopeMode::Internal => bytes[NONCE_LEN..].to_vec(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let hmac_key_size = Self::hmac_key_size();
|
|
||||||
let hmac = check_slice_size(&remainder, hmac_key_size, "hmac_key_size")?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
mode,
|
|
||||||
nonce,
|
|
||||||
hmac: GenericArray::clone_from_slice(hmac),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a dummy envelope object that serializes to the all-zeros byte string
|
|
||||||
pub(crate) fn dummy() -> Self {
|
|
||||||
Self {
|
|
||||||
mode: InnerEnvelopeMode::Zero,
|
|
||||||
nonce: vec![0u8; NONCE_LEN],
|
|
||||||
hmac: GenericArray::clone_from_slice(&vec![
|
|
||||||
0u8;
|
|
||||||
<CS::Hash as Digest>::OutputSize::USIZE
|
|
||||||
]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub(crate) fn seal<R: RngCore + CryptoRng>(
|
pub(crate) fn seal<R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
@@ -330,6 +241,58 @@ impl<CS: CipherSuite> Envelope<CS> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Creates a dummy envelope object that serializes to the all-zeros byte string
|
||||||
|
pub(crate) fn dummy() -> Self {
|
||||||
|
Self {
|
||||||
|
mode: InnerEnvelopeMode::Zero,
|
||||||
|
nonce: vec![0u8; NONCE_LEN],
|
||||||
|
hmac: GenericArray::clone_from_slice(&vec![
|
||||||
|
0u8;
|
||||||
|
<CS::Hash as Digest>::OutputSize::USIZE
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hmac_key_size() -> usize {
|
||||||
|
<CS::Hash as Digest>::OutputSize::USIZE
|
||||||
|
}
|
||||||
|
|
||||||
|
fn export_key_size() -> usize {
|
||||||
|
<CS::Hash as Digest>::OutputSize::USIZE
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn len() -> usize {
|
||||||
|
<CS::Hash as Digest>::OutputSize::USIZE + NONCE_LEN
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn serialize(&self) -> Vec<u8> {
|
||||||
|
[&self.nonce[..], &self.hmac[..]].concat()
|
||||||
|
}
|
||||||
|
pub(crate) fn deserialize(bytes: &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
let mode = InnerEnvelopeMode::Internal; // Better way to hard-code this?
|
||||||
|
|
||||||
|
if bytes.len() < NONCE_LEN {
|
||||||
|
return Err(ProtocolError::SerializationError);
|
||||||
|
}
|
||||||
|
let nonce = bytes[..NONCE_LEN].to_vec();
|
||||||
|
|
||||||
|
let remainder = match mode {
|
||||||
|
InnerEnvelopeMode::Zero => {
|
||||||
|
return Err(InternalError::IncompatibleEnvelopeModeError.into())
|
||||||
|
}
|
||||||
|
InnerEnvelopeMode::Internal => bytes[NONCE_LEN..].to_vec(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let hmac_key_size = Self::hmac_key_size();
|
||||||
|
let hmac = check_slice_size(&remainder, hmac_key_size, "hmac_key_size")?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
mode,
|
||||||
|
nonce,
|
||||||
|
hmac: GenericArray::clone_from_slice(hmac),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
|
pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
|
||||||
vec![(self.hmac.as_ptr(), self.hmac.len())]
|
vec![(self.hmac.as_ptr(), self.hmac.len())]
|
||||||
@@ -353,6 +316,42 @@ impl<CS: CipherSuite> Drop for Envelope<CS> {
|
|||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
|
|
||||||
|
fn build_inner_envelope_internal<CS: CipherSuite>(
|
||||||
|
random_pwd: &[u8],
|
||||||
|
nonce: &[u8],
|
||||||
|
) -> Result<PublicKey<CS::KeGroup>, ProtocolError> {
|
||||||
|
let h = Hkdf::<CS::Hash>::new(None, random_pwd);
|
||||||
|
let mut keypair_seed = vec![0u8; <CS::KeGroup as Group>::ScalarLen::USIZE];
|
||||||
|
h.expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed)
|
||||||
|
.map_err(|_| InternalError::HkdfError)?;
|
||||||
|
let client_static_keypair = KeyPair::<CS::KeGroup>::from_private_key_slice(
|
||||||
|
&CS::OprfGroup::scalar_as_bytes(CS::OprfGroup::hash_to_scalar::<CS::Hash>(
|
||||||
|
&keypair_seed[..],
|
||||||
|
STR_OPAQUE_DERIVE_AUTH_KEY_PAIR,
|
||||||
|
)?),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(client_static_keypair.public().clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recover_keys_internal<CS: CipherSuite>(
|
||||||
|
random_pwd: &[u8],
|
||||||
|
nonce: &[u8],
|
||||||
|
) -> Result<KeyPair<CS::KeGroup>, ProtocolError> {
|
||||||
|
let h = Hkdf::<CS::Hash>::new(None, random_pwd);
|
||||||
|
let mut keypair_seed = vec![0u8; <CS::KeGroup as Group>::ScalarLen::USIZE];
|
||||||
|
h.expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed)
|
||||||
|
.map_err(|_| InternalError::HkdfError)?;
|
||||||
|
let client_static_keypair = KeyPair::<CS::KeGroup>::from_private_key_slice(
|
||||||
|
&CS::OprfGroup::scalar_as_bytes(CS::OprfGroup::hash_to_scalar::<CS::Hash>(
|
||||||
|
&keypair_seed[..],
|
||||||
|
STR_OPAQUE_DERIVE_AUTH_KEY_PAIR,
|
||||||
|
)?),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(client_static_keypair)
|
||||||
|
}
|
||||||
|
|
||||||
fn construct_aad(id_u: &[u8], id_s: &[u8], server_s_pk: &[u8]) -> Vec<u8> {
|
fn construct_aad(id_u: &[u8], id_s: &[u8], server_s_pk: &[u8]) -> Vec<u8> {
|
||||||
[server_s_pk, id_s, id_u].concat()
|
[server_s_pk, id_s, id_u].concat()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ impl Group for RistrettoPoint {
|
|||||||
) -> Result<Self::Scalar, InternalError> {
|
) -> Result<Self::Scalar, InternalError> {
|
||||||
Ok(Scalar::from_bytes_mod_order(*scalar_bits.as_ref()))
|
Ok(Scalar::from_bytes_mod_order(*scalar_bits.as_ref()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
|
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
|
||||||
loop {
|
loop {
|
||||||
let scalar = {
|
let scalar = {
|
||||||
@@ -78,9 +79,11 @@ impl Group for RistrettoPoint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
||||||
scalar.to_bytes().into()
|
scalar.to_bytes().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar {
|
fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar {
|
||||||
scalar.invert()
|
scalar.invert()
|
||||||
}
|
}
|
||||||
@@ -94,6 +97,7 @@ impl Group for RistrettoPoint {
|
|||||||
.decompress()
|
.decompress()
|
||||||
.ok_or(InternalError::PointError)
|
.ok_or(InternalError::PointError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// serialization of a group element
|
// serialization of a group element
|
||||||
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
|
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
|
||||||
self.compress().to_bytes().into()
|
self.compress().to_bytes().into()
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ impl Group for MontgomeryPoint {
|
|||||||
) -> Result<Self::Scalar, InternalError> {
|
) -> Result<Self::Scalar, InternalError> {
|
||||||
Ok(Scalar::from_bytes_mod_order(*scalar_bits.as_ref()))
|
Ok(Scalar::from_bytes_mod_order(*scalar_bits.as_ref()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
|
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
|
||||||
loop {
|
loop {
|
||||||
let scalar = {
|
let scalar = {
|
||||||
@@ -53,9 +54,11 @@ impl Group for MontgomeryPoint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
||||||
scalar.to_bytes().into()
|
scalar.to_bytes().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scalar_invert(_scalar: &Self::Scalar) -> Self::Scalar {
|
fn scalar_invert(_scalar: &Self::Scalar) -> Self::Scalar {
|
||||||
unreachable!("this algorithm should only be used as the `KeGroup`")
|
unreachable!("this algorithm should only be used as the `KeGroup`")
|
||||||
}
|
}
|
||||||
@@ -67,6 +70,7 @@ impl Group for MontgomeryPoint {
|
|||||||
) -> Result<Self, InternalError> {
|
) -> Result<Self, InternalError> {
|
||||||
Ok(Self(*element_bits.as_ref()))
|
Ok(Self(*element_bits.as_ref()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// serialization of a group element
|
// serialization of a group element
|
||||||
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
|
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
|
||||||
self.to_bytes().into()
|
self.to_bytes().into()
|
||||||
|
|||||||
@@ -100,3 +100,70 @@ macro_rules! impl_clone_for {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inner macro used for deriving `serde`'s `Serialize` and `Deserialize` traits.
|
||||||
|
macro_rules! impl_serialize_and_deserialize_for {
|
||||||
|
($t:ident) => {
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
impl<CS: CipherSuite> serde::Serialize for $t<CS> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
if serializer.is_human_readable() {
|
||||||
|
serializer.serialize_str(&base64::encode(&self.serialize()))
|
||||||
|
} else {
|
||||||
|
serializer.serialize_bytes(&self.serialize())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
impl<'de, CS: CipherSuite> serde::Deserialize<'de> for $t<CS> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
if deserializer.is_human_readable() {
|
||||||
|
let s = <&str>::deserialize(deserializer)?;
|
||||||
|
$t::<CS>::deserialize(&base64::decode(s).map_err(serde::de::Error::custom)?)
|
||||||
|
.map_err(serde::de::Error::custom)
|
||||||
|
} else {
|
||||||
|
struct ByteVisitor<CS: CipherSuite> {
|
||||||
|
marker: core::marker::PhantomData<CS>,
|
||||||
|
}
|
||||||
|
impl<'de, CS: CipherSuite> serde::de::Visitor<'de> for ByteVisitor<CS> {
|
||||||
|
type Value = $t<CS>;
|
||||||
|
fn expecting(
|
||||||
|
&self,
|
||||||
|
formatter: &mut core::fmt::Formatter,
|
||||||
|
) -> core::fmt::Result {
|
||||||
|
formatter.write_str(core::concat!(
|
||||||
|
"the byte representation of a ",
|
||||||
|
core::stringify!($t)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
$t::<CS>::deserialize(value).map_err(|_| {
|
||||||
|
serde::de::Error::invalid_value(
|
||||||
|
serde::de::Unexpected::Bytes(value),
|
||||||
|
&core::concat!(
|
||||||
|
"invalid byte sequence for ",
|
||||||
|
core::stringify!($t)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserializer.deserialize_bytes(ByteVisitor::<CS> {
|
||||||
|
marker: core::marker::PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
+254
-225
@@ -31,8 +31,12 @@ use hmac::{Hmac, Mac, NewMac};
|
|||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
pub(crate) type NonceLen = U32;
|
///////////////
|
||||||
|
// Constants //
|
||||||
|
// ========= //
|
||||||
|
///////////////
|
||||||
|
|
||||||
|
pub(crate) type NonceLen = U32;
|
||||||
static STR_RFC: &[u8] = b"RFCXXXX";
|
static STR_RFC: &[u8] = b"RFCXXXX";
|
||||||
static STR_CLIENT_MAC: &[u8] = b"ClientMAC";
|
static STR_CLIENT_MAC: &[u8] = b"ClientMAC";
|
||||||
static STR_HANDSHAKE_SECRET: &[u8] = b"HandshakeSecret";
|
static STR_HANDSHAKE_SECRET: &[u8] = b"HandshakeSecret";
|
||||||
@@ -40,10 +44,72 @@ static STR_SERVER_MAC: &[u8] = b"ServerMAC";
|
|||||||
static STR_SESSION_KEY: &[u8] = b"SessionKey";
|
static STR_SESSION_KEY: &[u8] = b"SessionKey";
|
||||||
static STR_OPAQUE: &[u8] = b"OPAQUE-";
|
static STR_OPAQUE: &[u8] = b"OPAQUE-";
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
// High-level API Structs //
|
||||||
|
// ====================== //
|
||||||
|
////////////////////////////
|
||||||
|
|
||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
/// The Triple Diffie-Hellman key exchange implementation
|
/// The Triple Diffie-Hellman key exchange implementation
|
||||||
pub struct TripleDH;
|
pub struct TripleDH;
|
||||||
|
|
||||||
|
/// The client state produced after the first key exchange message
|
||||||
|
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
pub struct Ke1State<G: Group> {
|
||||||
|
client_e_sk: PrivateKey<G>,
|
||||||
|
client_nonce: GenericArray<u8, NonceLen>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_clone_for!(
|
||||||
|
struct Ke1State<G: Group>,
|
||||||
|
[client_e_sk, client_nonce],
|
||||||
|
);
|
||||||
|
impl_debug_eq_hash_for!(
|
||||||
|
struct Ke1State<G: Group>,
|
||||||
|
[client_e_sk, client_nonce],
|
||||||
|
);
|
||||||
|
|
||||||
|
/// The first key exchange message
|
||||||
|
#[derive(PartialEq, Eq, Debug, Hash, Clone)]
|
||||||
|
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
pub struct Ke1Message<G: Group> {
|
||||||
|
pub(crate) client_nonce: GenericArray<u8, NonceLen>,
|
||||||
|
pub(crate) client_e_pk: PublicKey<G>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The server state produced after the second key exchange message
|
||||||
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
#[cfg_attr(feature = "serialize", serde(bound = ""))]
|
||||||
|
pub struct Ke2State<HashLen: ArrayLength<u8>> {
|
||||||
|
km3: GenericArray<u8, HashLen>,
|
||||||
|
hashed_transcript: GenericArray<u8, HashLen>,
|
||||||
|
session_key: GenericArray<u8, HashLen>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The second key exchange message
|
||||||
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
#[cfg_attr(feature = "serialize", serde(bound = ""))]
|
||||||
|
pub struct Ke2Message<G: Group, HashLen: ArrayLength<u8>> {
|
||||||
|
server_nonce: GenericArray<u8, NonceLen>,
|
||||||
|
server_e_pk: PublicKey<G>,
|
||||||
|
mac: GenericArray<u8, HashLen>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The third key exchange message
|
||||||
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
#[cfg_attr(feature = "serialize", serde(bound = ""))]
|
||||||
|
pub struct Ke3Message<HashLen: ArrayLength<u8>> {
|
||||||
|
mac: GenericArray<u8, HashLen>,
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// High-level Implementations //
|
||||||
|
// ========================== //
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
|
impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
|
||||||
type KE1State = Ke1State<G>;
|
type KE1State = Ke1State<G>;
|
||||||
type KE2State = Ke2State<<D as FixedOutput>::OutputSize>;
|
type KE2State = Ke2State<<D as FixedOutput>::OutputSize>;
|
||||||
@@ -213,207 +279,10 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The client state produced after the first key exchange message
|
/////////////////////////
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
|
// Convenience Structs //
|
||||||
pub struct Ke1State<G: Group> {
|
//==================== //
|
||||||
client_e_sk: PrivateKey<G>,
|
/////////////////////////
|
||||||
client_nonce: GenericArray<u8, NonceLen>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_clone_for!(
|
|
||||||
struct Ke1State<G: Group>,
|
|
||||||
[client_e_sk, client_nonce],
|
|
||||||
);
|
|
||||||
impl_debug_eq_hash_for!(
|
|
||||||
struct Ke1State<G: Group>,
|
|
||||||
[client_e_sk, client_nonce],
|
|
||||||
);
|
|
||||||
|
|
||||||
// This can't be derived because of the use of a generic parameter
|
|
||||||
impl<G: Group> Zeroize for Ke1State<G> {
|
|
||||||
fn zeroize(&mut self) {
|
|
||||||
self.client_e_sk.zeroize();
|
|
||||||
self.client_nonce.zeroize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<G: Group> Drop for Ke1State<G> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.zeroize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The first key exchange message
|
|
||||||
#[derive(PartialEq, Eq, Debug, Hash, Clone)]
|
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
|
|
||||||
pub struct Ke1Message<G: Group> {
|
|
||||||
pub(crate) client_nonce: GenericArray<u8, NonceLen>,
|
|
||||||
pub(crate) client_e_pk: PublicKey<G>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<G: Group> FromBytes for Ke1State<G> {
|
|
||||||
fn from_bytes<CS: CipherSuite>(bytes: &[u8]) -> Result<Self, ProtocolError> {
|
|
||||||
let key_len = <G as Group>::ElemLen::USIZE;
|
|
||||||
|
|
||||||
let nonce_len = NonceLen::USIZE;
|
|
||||||
let checked_bytes = check_slice_size_atleast(bytes, key_len + nonce_len, "ke1_state")?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
client_e_sk: PrivateKey::from_bytes(&checked_bytes[..key_len])?,
|
|
||||||
client_nonce: GenericArray::clone_from_slice(
|
|
||||||
&checked_bytes[key_len..key_len + nonce_len],
|
|
||||||
),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<G: Group> ToBytesWithPointers for Ke1State<G> {
|
|
||||||
fn to_bytes(&self) -> Vec<u8> {
|
|
||||||
let output: Vec<u8> = [&self.client_e_sk.to_arr(), &self.client_nonce[..]].concat();
|
|
||||||
output
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
|
|
||||||
vec![
|
|
||||||
(self.client_e_sk.as_ptr(), G::ScalarLen::USIZE),
|
|
||||||
(self.client_nonce.as_ptr(), NonceLen::USIZE),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<G: Group> ToBytes for Ke1Message<G> {
|
|
||||||
fn to_bytes(&self) -> Vec<u8> {
|
|
||||||
[&self.client_nonce[..], &self.client_e_pk.to_arr()].concat()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<G: Group> FromBytes for Ke1Message<G> {
|
|
||||||
fn from_bytes<CS: CipherSuite>(ke1_message_bytes: &[u8]) -> Result<Self, ProtocolError> {
|
|
||||||
let nonce_len = NonceLen::USIZE;
|
|
||||||
let checked_nonce = check_slice_size(
|
|
||||||
ke1_message_bytes,
|
|
||||||
nonce_len + <G as Group>::ElemLen::USIZE,
|
|
||||||
"ke1_message nonce",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
client_nonce: GenericArray::clone_from_slice(&checked_nonce[..nonce_len]),
|
|
||||||
client_e_pk: PublicKey::from_bytes(&checked_nonce[nonce_len..])?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// The server state produced after the second key exchange message
|
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
|
|
||||||
#[cfg_attr(feature = "serialize", serde(bound = ""))]
|
|
||||||
pub struct Ke2State<HashLen: ArrayLength<u8>> {
|
|
||||||
km3: GenericArray<u8, HashLen>,
|
|
||||||
hashed_transcript: GenericArray<u8, HashLen>,
|
|
||||||
session_key: GenericArray<u8, HashLen>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// This can't be derived because of the use of a phantom parameter
|
|
||||||
impl<HashLen: ArrayLength<u8>> Zeroize for Ke2State<HashLen> {
|
|
||||||
fn zeroize(&mut self) {
|
|
||||||
self.km3.zeroize();
|
|
||||||
self.hashed_transcript.zeroize();
|
|
||||||
self.session_key.zeroize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<HashLen: ArrayLength<u8>> Drop for Ke2State<HashLen> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.zeroize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<HashLen: ArrayLength<u8>> ToBytesWithPointers for Ke2State<HashLen> {
|
|
||||||
fn to_bytes(&self) -> Vec<u8> {
|
|
||||||
[
|
|
||||||
&self.km3[..],
|
|
||||||
&self.hashed_transcript[..],
|
|
||||||
&self.session_key[..],
|
|
||||||
]
|
|
||||||
.concat()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
|
|
||||||
vec![
|
|
||||||
(self.km3.as_ptr(), HashLen::USIZE),
|
|
||||||
(self.hashed_transcript.as_ptr(), HashLen::USIZE),
|
|
||||||
(self.session_key.as_ptr(), HashLen::USIZE),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The second key exchange message
|
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
|
|
||||||
#[cfg_attr(feature = "serialize", serde(bound = ""))]
|
|
||||||
pub struct Ke2Message<G: Group, HashLen: ArrayLength<u8>> {
|
|
||||||
server_nonce: GenericArray<u8, NonceLen>,
|
|
||||||
server_e_pk: PublicKey<G>,
|
|
||||||
mac: GenericArray<u8, HashLen>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<HashLen: ArrayLength<u8>> FromBytes for Ke2State<HashLen> {
|
|
||||||
fn from_bytes<CS: CipherSuite>(input: &[u8]) -> Result<Self, ProtocolError> {
|
|
||||||
let hash_len = HashLen::USIZE;
|
|
||||||
let checked_bytes = check_slice_size(input, 3 * hash_len, "ke2_state")?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
km3: GenericArray::clone_from_slice(&checked_bytes[..hash_len]),
|
|
||||||
hashed_transcript: GenericArray::clone_from_slice(
|
|
||||||
&checked_bytes[hash_len..2 * hash_len],
|
|
||||||
),
|
|
||||||
session_key: GenericArray::clone_from_slice(&checked_bytes[2 * hash_len..3 * hash_len]),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<G: Group, HashLen: ArrayLength<u8>> ToBytes for Ke2Message<G, HashLen> {
|
|
||||||
fn to_bytes(&self) -> Vec<u8> {
|
|
||||||
[&self.to_bytes_without_info_or_mac(), &self.mac[..]].concat()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<G: Group, HashLen: ArrayLength<u8>> Ke2Message<G, HashLen> {
|
|
||||||
fn to_bytes_without_info_or_mac(&self) -> Vec<u8> {
|
|
||||||
[&self.server_nonce[..], &self.server_e_pk.to_arr()].concat()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<G: Group, HashLen: ArrayLength<u8>> FromBytes for Ke2Message<G, HashLen> {
|
|
||||||
fn from_bytes<CS: CipherSuite>(input: &[u8]) -> Result<Self, ProtocolError> {
|
|
||||||
let key_len = <G as Group>::ElemLen::USIZE;
|
|
||||||
let nonce_len = NonceLen::USIZE;
|
|
||||||
let checked_nonce = check_slice_size_atleast(input, nonce_len, "ke2_message nonce")?;
|
|
||||||
|
|
||||||
let unchecked_server_e_pk = check_slice_size_atleast(
|
|
||||||
&checked_nonce[nonce_len..],
|
|
||||||
key_len,
|
|
||||||
"ke2_message server_e_pk",
|
|
||||||
)?;
|
|
||||||
let checked_mac = check_slice_size(
|
|
||||||
&unchecked_server_e_pk[key_len..],
|
|
||||||
HashLen::USIZE,
|
|
||||||
"ke1_message mac",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Check the public key bytes
|
|
||||||
let server_e_pk = KeyPair::<CS::OprfGroup>::check_public_key(PublicKey::from_bytes(
|
|
||||||
&unchecked_server_e_pk[..key_len],
|
|
||||||
)?)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
server_nonce: GenericArray::clone_from_slice(&checked_nonce[..nonce_len]),
|
|
||||||
server_e_pk: PublicKey::from_bytes(&server_e_pk)?,
|
|
||||||
mac: GenericArray::clone_from_slice(checked_mac),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
// The triple of public and private components used in the 3DH computation
|
// The triple of public and private components used in the 3DH computation
|
||||||
@@ -442,29 +311,10 @@ type TripleDHDerivationResult<D> = (
|
|||||||
Vec<u8>,
|
Vec<u8>,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// The third key exchange message
|
////////////////////////////////////////////////
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
// Helper functions and Trait Implementations //
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
|
// ========================================== //
|
||||||
#[cfg_attr(feature = "serialize", serde(bound = ""))]
|
////////////////////////////////////////////////
|
||||||
pub struct Ke3Message<HashLen: ArrayLength<u8>> {
|
|
||||||
mac: GenericArray<u8, HashLen>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<HashLen: ArrayLength<u8>> ToBytes for Ke3Message<HashLen> {
|
|
||||||
fn to_bytes(&self) -> Vec<u8> {
|
|
||||||
self.mac.to_vec()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<HashLen: ArrayLength<u8>> FromBytes for Ke3Message<HashLen> {
|
|
||||||
fn from_bytes<CS: CipherSuite>(bytes: &[u8]) -> Result<Self, ProtocolError> {
|
|
||||||
let checked_bytes = check_slice_size(bytes, HashLen::USIZE, "ke3_message")?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
mac: GenericArray::clone_from_slice(checked_bytes),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
|
|
||||||
@@ -577,3 +427,182 @@ fn generate_nonce<R: RngCore + CryptoRng>(rng: &mut R) -> GenericArray<u8, Nonce
|
|||||||
rng.fill_bytes(&mut nonce_bytes);
|
rng.fill_bytes(&mut nonce_bytes);
|
||||||
GenericArray::clone_from_slice(&nonce_bytes)
|
GenericArray::clone_from_slice(&nonce_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Serialization and deserialization implementations
|
||||||
|
|
||||||
|
impl<G: Group> FromBytes for Ke1State<G> {
|
||||||
|
fn from_bytes<CS: CipherSuite>(bytes: &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
let key_len = <G as Group>::ElemLen::USIZE;
|
||||||
|
|
||||||
|
let nonce_len = NonceLen::USIZE;
|
||||||
|
let checked_bytes = check_slice_size_atleast(bytes, key_len + nonce_len, "ke1_state")?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
client_e_sk: PrivateKey::from_bytes(&checked_bytes[..key_len])?,
|
||||||
|
client_nonce: GenericArray::clone_from_slice(
|
||||||
|
&checked_bytes[key_len..key_len + nonce_len],
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group> ToBytesWithPointers for Ke1State<G> {
|
||||||
|
fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
let output: Vec<u8> = [&self.client_e_sk.to_arr(), &self.client_nonce[..]].concat();
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
|
||||||
|
vec![
|
||||||
|
(self.client_e_sk.as_ptr(), G::ScalarLen::USIZE),
|
||||||
|
(self.client_nonce.as_ptr(), NonceLen::USIZE),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group> FromBytes for Ke1Message<G> {
|
||||||
|
fn from_bytes<CS: CipherSuite>(ke1_message_bytes: &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
let nonce_len = NonceLen::USIZE;
|
||||||
|
let checked_nonce = check_slice_size(
|
||||||
|
ke1_message_bytes,
|
||||||
|
nonce_len + <G as Group>::ElemLen::USIZE,
|
||||||
|
"ke1_message nonce",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
client_nonce: GenericArray::clone_from_slice(&checked_nonce[..nonce_len]),
|
||||||
|
client_e_pk: PublicKey::from_bytes(&checked_nonce[nonce_len..])?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group> ToBytes for Ke1Message<G> {
|
||||||
|
fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
[&self.client_nonce[..], &self.client_e_pk.to_arr()].concat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<HashLen: ArrayLength<u8>> FromBytes for Ke2State<HashLen> {
|
||||||
|
fn from_bytes<CS: CipherSuite>(input: &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
let hash_len = HashLen::USIZE;
|
||||||
|
let checked_bytes = check_slice_size(input, 3 * hash_len, "ke2_state")?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
km3: GenericArray::clone_from_slice(&checked_bytes[..hash_len]),
|
||||||
|
hashed_transcript: GenericArray::clone_from_slice(
|
||||||
|
&checked_bytes[hash_len..2 * hash_len],
|
||||||
|
),
|
||||||
|
session_key: GenericArray::clone_from_slice(&checked_bytes[2 * hash_len..3 * hash_len]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<HashLen: ArrayLength<u8>> ToBytesWithPointers for Ke2State<HashLen> {
|
||||||
|
fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
[
|
||||||
|
&self.km3[..],
|
||||||
|
&self.hashed_transcript[..],
|
||||||
|
&self.session_key[..],
|
||||||
|
]
|
||||||
|
.concat()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
|
||||||
|
vec![
|
||||||
|
(self.km3.as_ptr(), HashLen::USIZE),
|
||||||
|
(self.hashed_transcript.as_ptr(), HashLen::USIZE),
|
||||||
|
(self.session_key.as_ptr(), HashLen::USIZE),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group, HashLen: ArrayLength<u8>> FromBytes for Ke2Message<G, HashLen> {
|
||||||
|
fn from_bytes<CS: CipherSuite>(input: &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
let key_len = <G as Group>::ElemLen::USIZE;
|
||||||
|
let nonce_len = NonceLen::USIZE;
|
||||||
|
let checked_nonce = check_slice_size_atleast(input, nonce_len, "ke2_message nonce")?;
|
||||||
|
|
||||||
|
let unchecked_server_e_pk = check_slice_size_atleast(
|
||||||
|
&checked_nonce[nonce_len..],
|
||||||
|
key_len,
|
||||||
|
"ke2_message server_e_pk",
|
||||||
|
)?;
|
||||||
|
let checked_mac = check_slice_size(
|
||||||
|
&unchecked_server_e_pk[key_len..],
|
||||||
|
HashLen::USIZE,
|
||||||
|
"ke1_message mac",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Check the public key bytes
|
||||||
|
let server_e_pk = KeyPair::<CS::OprfGroup>::check_public_key(PublicKey::from_bytes(
|
||||||
|
&unchecked_server_e_pk[..key_len],
|
||||||
|
)?)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
server_nonce: GenericArray::clone_from_slice(&checked_nonce[..nonce_len]),
|
||||||
|
server_e_pk: PublicKey::from_bytes(&server_e_pk)?,
|
||||||
|
mac: GenericArray::clone_from_slice(checked_mac),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group, HashLen: ArrayLength<u8>> ToBytes for Ke2Message<G, HashLen> {
|
||||||
|
fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
[&self.to_bytes_without_info_or_mac(), &self.mac[..]].concat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group, HashLen: ArrayLength<u8>> Ke2Message<G, HashLen> {
|
||||||
|
fn to_bytes_without_info_or_mac(&self) -> Vec<u8> {
|
||||||
|
[&self.server_nonce[..], &self.server_e_pk.to_arr()].concat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<HashLen: ArrayLength<u8>> FromBytes for Ke3Message<HashLen> {
|
||||||
|
fn from_bytes<CS: CipherSuite>(bytes: &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
let checked_bytes = check_slice_size(bytes, HashLen::USIZE, "ke3_message")?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
mac: GenericArray::clone_from_slice(checked_bytes),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<HashLen: ArrayLength<u8>> ToBytes for Ke3Message<HashLen> {
|
||||||
|
fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
self.mac.to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zeroize on drop implementations
|
||||||
|
|
||||||
|
// This can't be derived because of the use of a generic parameter
|
||||||
|
impl<G: Group> Zeroize for Ke1State<G> {
|
||||||
|
fn zeroize(&mut self) {
|
||||||
|
self.client_e_sk.zeroize();
|
||||||
|
self.client_nonce.zeroize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group> Drop for Ke1State<G> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.zeroize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This can't be derived because of the use of a phantom parameter
|
||||||
|
impl<HashLen: ArrayLength<u8>> Zeroize for Ke2State<HashLen> {
|
||||||
|
fn zeroize(&mut self) {
|
||||||
|
self.km3.zeroize();
|
||||||
|
self.hashed_transcript.zeroize();
|
||||||
|
self.session_key.zeroize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<HashLen: ArrayLength<u8>> Drop for Ke2State<HashLen> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.zeroize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+4
-11
@@ -843,28 +843,21 @@ pub mod errors;
|
|||||||
mod impls;
|
mod impls;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod serialization;
|
mod serialization;
|
||||||
|
|
||||||
// High-level API
|
|
||||||
mod opaque;
|
|
||||||
|
|
||||||
mod messages;
|
|
||||||
|
|
||||||
pub mod ciphersuite;
|
pub mod ciphersuite;
|
||||||
mod envelope;
|
mod envelope;
|
||||||
pub mod hash;
|
|
||||||
|
|
||||||
pub mod group;
|
pub mod group;
|
||||||
|
pub mod hash;
|
||||||
pub mod key_exchange;
|
pub mod key_exchange;
|
||||||
pub mod keypair;
|
pub mod keypair;
|
||||||
|
mod messages;
|
||||||
|
mod opaque;
|
||||||
|
pub mod slow_hash;
|
||||||
|
|
||||||
#[cfg(feature = "bench")]
|
#[cfg(feature = "bench")]
|
||||||
pub mod oprf;
|
pub mod oprf;
|
||||||
#[cfg(not(feature = "bench"))]
|
#[cfg(not(feature = "bench"))]
|
||||||
mod oprf;
|
mod oprf;
|
||||||
|
|
||||||
pub mod slow_hash;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
|
|||||||
+120
-139
@@ -22,8 +22,10 @@ use digest::Digest;
|
|||||||
use generic_array::{typenum::Unsigned, GenericArray};
|
use generic_array::{typenum::Unsigned, GenericArray};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
|
|
||||||
// Messages
|
////////////////////////////
|
||||||
// =========
|
// High-level API Structs //
|
||||||
|
// ====================== //
|
||||||
|
////////////////////////////
|
||||||
|
|
||||||
/// The message sent by the client to the server, to initiate registration
|
/// The message sent by the client to the server, to initiate registration
|
||||||
pub struct RegistrationRequest<CS: CipherSuite> {
|
pub struct RegistrationRequest<CS: CipherSuite> {
|
||||||
@@ -31,24 +33,62 @@ pub struct RegistrationRequest<CS: CipherSuite> {
|
|||||||
pub(crate) alpha: CS::OprfGroup,
|
pub(crate) alpha: CS::OprfGroup,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The answer sent by the server to the user, upon reception of the
|
||||||
|
/// registration attempt
|
||||||
|
pub struct RegistrationResponse<CS: CipherSuite> {
|
||||||
|
/// The server's oprf output
|
||||||
|
pub(crate) beta: CS::OprfGroup,
|
||||||
|
/// Server's static public key
|
||||||
|
pub(crate) server_s_pk: PublicKey<CS::KeGroup>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The final message from the client, containing sealed cryptographic
|
||||||
|
/// identifiers
|
||||||
|
pub struct RegistrationUpload<CS: CipherSuite> {
|
||||||
|
/// The "envelope" generated by the user, containing sealed
|
||||||
|
/// cryptographic identifiers
|
||||||
|
pub(crate) envelope: Envelope<CS>,
|
||||||
|
/// 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<CS::KeGroup>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The message sent by the user to the server, to initiate registration
|
||||||
|
pub struct CredentialRequest<CS: CipherSuite> {
|
||||||
|
/// blinded password information
|
||||||
|
pub(crate) alpha: CS::OprfGroup,
|
||||||
|
pub(crate) ke1_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1Message,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The answer sent by the server to the user, upon reception of the
|
||||||
|
/// login attempt
|
||||||
|
pub struct CredentialResponse<CS: CipherSuite> {
|
||||||
|
/// the server's oprf output
|
||||||
|
pub(crate) beta: CS::OprfGroup,
|
||||||
|
pub(crate) masking_nonce: Vec<u8>,
|
||||||
|
pub(crate) masked_response: Vec<u8>,
|
||||||
|
pub(crate) ke2_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2Message,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The answer sent by the client to the server, upon reception of the
|
||||||
|
/// sealed envelope
|
||||||
|
pub struct CredentialFinalization<CS: CipherSuite> {
|
||||||
|
pub(crate) ke3_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE3Message,
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// High-level Implementations //
|
||||||
|
// ========================== //
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
impl<CS: CipherSuite> RegistrationRequest<CS> {
|
impl<CS: CipherSuite> RegistrationRequest<CS> {
|
||||||
/// Only used for testing purposes
|
/// Only used for testing purposes
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn get_alpha_for_testing(&self) -> CS::OprfGroup {
|
pub fn get_alpha_for_testing(&self) -> CS::OprfGroup {
|
||||||
self.alpha
|
self.alpha
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Cannot be derived because it would require for CS to be Clone.
|
|
||||||
impl<CS: CipherSuite> Clone for RegistrationRequest<CS> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self { alpha: self.alpha }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_debug_eq_hash_for!(struct RegistrationRequest<CS: CipherSuite>, [alpha], [CS::OprfGroup]);
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> RegistrationRequest<CS> {
|
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
self.alpha.to_arr().to_vec()
|
self.alpha.to_arr().to_vec()
|
||||||
@@ -71,33 +111,6 @@ impl<CS: CipherSuite> RegistrationRequest<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_serialize_and_deserialize_for!(RegistrationRequest);
|
|
||||||
|
|
||||||
/// The answer sent by the server to the user, upon reception of the
|
|
||||||
/// registration attempt
|
|
||||||
pub struct RegistrationResponse<CS: CipherSuite> {
|
|
||||||
/// The server's oprf output
|
|
||||||
pub(crate) beta: CS::OprfGroup,
|
|
||||||
/// Server's static public key
|
|
||||||
pub(crate) server_s_pk: PublicKey<CS::KeGroup>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cannot be derived because it would require for CS to be Clone.
|
|
||||||
impl<CS: CipherSuite> Clone for RegistrationResponse<CS> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
beta: self.beta,
|
|
||||||
server_s_pk: self.server_s_pk.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_debug_eq_hash_for!(
|
|
||||||
struct RegistrationResponse<CS: CipherSuite>,
|
|
||||||
[beta, server_s_pk],
|
|
||||||
[CS::OprfGroup],
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> RegistrationResponse<CS> {
|
impl<CS: CipherSuite> RegistrationResponse<CS> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
@@ -140,29 +153,6 @@ impl<CS: CipherSuite> RegistrationResponse<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_serialize_and_deserialize_for!(RegistrationResponse);
|
|
||||||
|
|
||||||
/// The final message from the client, containing sealed cryptographic
|
|
||||||
/// identifiers
|
|
||||||
pub struct RegistrationUpload<CS: CipherSuite> {
|
|
||||||
/// The "envelope" generated by the user, containing sealed
|
|
||||||
/// cryptographic identifiers
|
|
||||||
pub(crate) envelope: Envelope<CS>,
|
|
||||||
/// 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<CS::KeGroup>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_clone_for!(
|
|
||||||
struct RegistrationUpload<CS: CipherSuite>,
|
|
||||||
[envelope, masking_key, client_s_pk],
|
|
||||||
);
|
|
||||||
impl_debug_eq_hash_for!(
|
|
||||||
struct RegistrationUpload<CS: CipherSuite>,
|
|
||||||
[envelope, masking_key, client_s_pk],
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> RegistrationUpload<CS> {
|
impl<CS: CipherSuite> RegistrationUpload<CS> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
@@ -208,34 +198,6 @@ impl<CS: CipherSuite> RegistrationUpload<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_serialize_and_deserialize_for!(RegistrationUpload);
|
|
||||||
|
|
||||||
/// The message sent by the user to the server, to initiate registration
|
|
||||||
pub struct CredentialRequest<CS: CipherSuite> {
|
|
||||||
/// blinded password information
|
|
||||||
pub(crate) alpha: CS::OprfGroup,
|
|
||||||
pub(crate) ke1_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1Message,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cannot be derived because it would require for CS to be Clone.
|
|
||||||
impl<CS: CipherSuite> Clone for CredentialRequest<CS> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
alpha: self.alpha,
|
|
||||||
ke1_message: self.ke1_message.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_debug_eq_hash_for!(
|
|
||||||
struct CredentialRequest<CS: CipherSuite>,
|
|
||||||
[alpha, ke1_message],
|
|
||||||
[
|
|
||||||
CS::OprfGroup,
|
|
||||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1Message
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> CredentialRequest<CS> {
|
impl<CS: CipherSuite> CredentialRequest<CS> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
@@ -273,39 +235,6 @@ impl<CS: CipherSuite> CredentialRequest<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_serialize_and_deserialize_for!(CredentialRequest);
|
|
||||||
|
|
||||||
/// The answer sent by the server to the user, upon reception of the
|
|
||||||
/// login attempt
|
|
||||||
pub struct CredentialResponse<CS: CipherSuite> {
|
|
||||||
/// the server's oprf output
|
|
||||||
pub(crate) beta: CS::OprfGroup,
|
|
||||||
pub(crate) masking_nonce: Vec<u8>,
|
|
||||||
pub(crate) masked_response: Vec<u8>,
|
|
||||||
pub(crate) ke2_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2Message,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cannot be derived because it would require for CS to be Clone.
|
|
||||||
impl<CS: CipherSuite> Clone for CredentialResponse<CS> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
beta: self.beta,
|
|
||||||
masking_nonce: self.masking_nonce.clone(),
|
|
||||||
masked_response: self.masked_response.clone(),
|
|
||||||
ke2_message: self.ke2_message.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_debug_eq_hash_for!(
|
|
||||||
struct CredentialResponse<CS: CipherSuite>,
|
|
||||||
[beta, masking_nonce, masked_response, ke2_message],
|
|
||||||
[
|
|
||||||
CS::OprfGroup,
|
|
||||||
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2Message,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> CredentialResponse<CS> {
|
impl<CS: CipherSuite> CredentialResponse<CS> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
@@ -380,21 +309,6 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_serialize_and_deserialize_for!(CredentialResponse);
|
|
||||||
|
|
||||||
/// The answer sent by the client to the server, upon reception of the
|
|
||||||
/// sealed envelope
|
|
||||||
pub struct CredentialFinalization<CS: CipherSuite> {
|
|
||||||
pub(crate) ke3_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE3Message,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_clone_for!(struct CredentialFinalization<CS: CipherSuite>, [ke3_message]);
|
|
||||||
impl_debug_eq_hash_for!(
|
|
||||||
struct CredentialFinalization<CS: CipherSuite>,
|
|
||||||
[ke3_message],
|
|
||||||
[<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE3Message],
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> CredentialFinalization<CS> {
|
impl<CS: CipherSuite> CredentialFinalization<CS> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
@@ -411,4 +325,71 @@ impl<CS: CipherSuite> CredentialFinalization<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////
|
||||||
|
// Trait Implementations //
|
||||||
|
// ===================== //
|
||||||
|
///////////////////////////
|
||||||
|
|
||||||
|
impl_clone_for!(
|
||||||
|
struct RegistrationRequest<CS: CipherSuite>,
|
||||||
|
[alpha],
|
||||||
|
);
|
||||||
|
impl_debug_eq_hash_for!(struct RegistrationRequest<CS: CipherSuite>, [alpha], [CS::OprfGroup]);
|
||||||
|
impl_serialize_and_deserialize_for!(RegistrationRequest);
|
||||||
|
|
||||||
|
impl_clone_for!(
|
||||||
|
struct RegistrationResponse<CS: CipherSuite>,
|
||||||
|
[beta, server_s_pk],
|
||||||
|
);
|
||||||
|
impl_debug_eq_hash_for!(
|
||||||
|
struct RegistrationResponse<CS: CipherSuite>,
|
||||||
|
[beta, server_s_pk],
|
||||||
|
[CS::OprfGroup],
|
||||||
|
);
|
||||||
|
impl_serialize_and_deserialize_for!(RegistrationResponse);
|
||||||
|
|
||||||
|
impl_clone_for!(
|
||||||
|
struct RegistrationUpload<CS: CipherSuite>,
|
||||||
|
[envelope, masking_key, client_s_pk],
|
||||||
|
);
|
||||||
|
impl_debug_eq_hash_for!(
|
||||||
|
struct RegistrationUpload<CS: CipherSuite>,
|
||||||
|
[envelope, masking_key, client_s_pk],
|
||||||
|
);
|
||||||
|
impl_serialize_and_deserialize_for!(RegistrationUpload);
|
||||||
|
|
||||||
|
impl_clone_for!(
|
||||||
|
struct CredentialRequest<CS: CipherSuite>,
|
||||||
|
[alpha, ke1_message],
|
||||||
|
);
|
||||||
|
impl_debug_eq_hash_for!(
|
||||||
|
struct CredentialRequest<CS: CipherSuite>,
|
||||||
|
[alpha, ke1_message],
|
||||||
|
[
|
||||||
|
CS::OprfGroup,
|
||||||
|
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1Message
|
||||||
|
],
|
||||||
|
);
|
||||||
|
impl_serialize_and_deserialize_for!(CredentialRequest);
|
||||||
|
|
||||||
|
impl_clone_for!(
|
||||||
|
struct CredentialResponse<CS: CipherSuite>,
|
||||||
|
[beta, masking_nonce, masked_response, ke2_message],
|
||||||
|
);
|
||||||
|
impl_debug_eq_hash_for!(
|
||||||
|
struct CredentialResponse<CS: CipherSuite>,
|
||||||
|
[beta, masking_nonce, masked_response, ke2_message],
|
||||||
|
[
|
||||||
|
CS::OprfGroup,
|
||||||
|
<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2Message,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
impl_serialize_and_deserialize_for!(CredentialResponse);
|
||||||
|
|
||||||
|
impl_clone_for!(struct CredentialFinalization<CS: CipherSuite>, [ke3_message]);
|
||||||
|
impl_debug_eq_hash_for!(
|
||||||
|
struct CredentialFinalization<CS: CipherSuite>,
|
||||||
|
[ke3_message],
|
||||||
|
[<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE3Message],
|
||||||
|
);
|
||||||
impl_serialize_and_deserialize_for!(CredentialFinalization);
|
impl_serialize_and_deserialize_for!(CredentialFinalization);
|
||||||
|
|||||||
+431
-413
@@ -28,13 +28,20 @@ use hkdf::Hkdf;
|
|||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
///////////////
|
||||||
|
// Constants //
|
||||||
|
// ========= //
|
||||||
|
///////////////
|
||||||
|
|
||||||
const STR_CREDENTIAL_RESPONSE_PAD: &[u8] = b"CredentialResponsePad";
|
const STR_CREDENTIAL_RESPONSE_PAD: &[u8] = b"CredentialResponsePad";
|
||||||
const STR_MASKING_KEY: &[u8] = b"MaskingKey";
|
const STR_MASKING_KEY: &[u8] = b"MaskingKey";
|
||||||
const STR_OPRF_KEY: &[u8] = b"OprfKey";
|
const STR_OPRF_KEY: &[u8] = b"OprfKey";
|
||||||
const STR_OPAQUE_DERIVE_KEY_PAIR: &[u8] = b"OPAQUE-DeriveKeyPair";
|
const STR_OPAQUE_DERIVE_KEY_PAIR: &[u8] = b"OPAQUE-DeriveKeyPair";
|
||||||
|
|
||||||
// Server Setup
|
////////////////////////////
|
||||||
// ============
|
// High-level API Structs //
|
||||||
|
// ====================== //
|
||||||
|
////////////////////////////
|
||||||
|
|
||||||
/// The state elements the server holds upon setup
|
/// The state elements the server holds upon setup
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
@@ -54,6 +61,86 @@ pub struct ServerSetup<
|
|||||||
pub(crate) fake_keypair: KeyPair<CS::KeGroup>,
|
pub(crate) fake_keypair: KeyPair<CS::KeGroup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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> {
|
||||||
|
alpha: CS::OprfGroup,
|
||||||
|
/// token containing the client's password and the blinding factor
|
||||||
|
pub(crate) token: oprf::Token<CS::OprfGroup>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_clone_for!(struct ClientRegistration<CS: CipherSuite>, [token, alpha]);
|
||||||
|
impl_debug_eq_hash_for!(
|
||||||
|
struct ClientRegistration<CS: CipherSuite>,
|
||||||
|
[token],
|
||||||
|
[oprf::Token<CS::OprfGroup>],
|
||||||
|
);
|
||||||
|
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
|
||||||
|
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serialize",
|
||||||
|
serde(bound(
|
||||||
|
deserialize = "oprf::Token<CS::OprfGroup>: serde::Deserialize<'de>, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State: serde::Deserialize<'de>",
|
||||||
|
serialize = "oprf::Token<CS::OprfGroup>: serde::Serialize, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State: serde::Serialize"
|
||||||
|
))
|
||||||
|
)]
|
||||||
|
pub struct ClientLogin<CS: CipherSuite> {
|
||||||
|
/// token containing the client's password and the blinding factor
|
||||||
|
token: oprf::Token<CS::OprfGroup>,
|
||||||
|
ke1_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State,
|
||||||
|
serialized_credential_request: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_clone_for!(struct ClientLogin<CS: CipherSuite>, [token, ke1_state, serialized_credential_request]);
|
||||||
|
impl_debug_eq_hash_for!(
|
||||||
|
struct ClientLogin<CS: CipherSuite>,
|
||||||
|
[token, ke1_state, serialized_credential_request],
|
||||||
|
[oprf::Token<CS::OprfGroup>, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State],
|
||||||
|
);
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
// ============
|
||||||
|
|
||||||
impl<CS: CipherSuite> ServerSetup<CS, PrivateKey<CS::KeGroup>> {
|
impl<CS: CipherSuite> ServerSetup<CS, PrivateKey<CS::KeGroup>> {
|
||||||
/// Generate a new instance of server setup
|
/// Generate a new instance of server setup
|
||||||
pub fn new<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
|
pub fn new<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
|
||||||
@@ -108,33 +195,9 @@ impl<CS: CipherSuite, S: SecretKey<CS::KeGroup>> ServerSetup<CS, S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Registration
|
// Registration
|
||||||
// ============
|
// ============
|
||||||
|
|
||||||
/// The state elements the client holds to register itself
|
|
||||||
pub struct ClientRegistration<CS: CipherSuite> {
|
|
||||||
alpha: CS::OprfGroup,
|
|
||||||
/// token containing the client's password and the blinding factor
|
|
||||||
pub(crate) token: oprf::Token<CS::OprfGroup>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_clone_for!(struct ClientRegistration<CS: CipherSuite>, [token, alpha]);
|
|
||||||
impl_debug_eq_hash_for!(
|
|
||||||
struct ClientRegistration<CS: CipherSuite>,
|
|
||||||
[token],
|
|
||||||
[oprf::Token<CS::OprfGroup>],
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> ClientRegistration<CS> {
|
impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
@@ -188,85 +251,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
|||||||
/* cannot provide raw pointer to self.token.blind until this is exposed in curve25519_dalek::scalar::Scalar */
|
/* cannot provide raw pointer to self.token.blind until this is exposed in curve25519_dalek::scalar::Scalar */
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl_serialize_and_deserialize_for!(ClientRegistration);
|
|
||||||
|
|
||||||
/// 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>),
|
|
||||||
}
|
|
||||||
|
|
||||||
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)?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'h, CS: CipherSuite> Default for ClientRegistrationFinishParameters<'h, CS> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
identifiers: None,
|
|
||||||
slow_hash: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> ClientRegistration<CS> {
|
|
||||||
/// Returns an initial "blinded" request to send to the server, as well as a ClientRegistration
|
/// Returns an initial "blinded" request to send to the server, as well as a ClientRegistration
|
||||||
pub fn start<R: RngCore + CryptoRng>(
|
pub fn start<R: RngCore + CryptoRng>(
|
||||||
blinding_factor_rng: &mut R,
|
blinding_factor_rng: &mut R,
|
||||||
@@ -280,45 +265,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
|||||||
state: Self { alpha, token },
|
state: Self { alpha, token },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// 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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> ClientRegistration<CS> {
|
|
||||||
/// "Unblinds" the server's answer and returns a final message containing
|
/// "Unblinds" the server's answer and returns a final message containing
|
||||||
/// cryptographic identifiers, to be sent to the server on setup finalization
|
/// cryptographic identifiers, to be sent to the server on setup finalization
|
||||||
pub fn finish<R: CryptoRng + RngCore>(
|
pub fn finish<R: CryptoRng + RngCore>(
|
||||||
@@ -366,36 +313,6 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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<CS: CipherSuite> ServerRegistration<CS> {
|
impl<CS: CipherSuite> ServerRegistration<CS> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
@@ -456,34 +373,9 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_serialize_and_deserialize_for!(ServerRegistration);
|
|
||||||
|
|
||||||
// Login
|
// Login
|
||||||
// =====
|
// =====
|
||||||
|
|
||||||
/// The state elements the client holds to perform a login
|
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "serialize",
|
|
||||||
serde(bound(
|
|
||||||
deserialize = "oprf::Token<CS::OprfGroup>: serde::Deserialize<'de>, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State: serde::Deserialize<'de>",
|
|
||||||
serialize = "oprf::Token<CS::OprfGroup>: serde::Serialize, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State: serde::Serialize"
|
|
||||||
))
|
|
||||||
)]
|
|
||||||
pub struct ClientLogin<CS: CipherSuite> {
|
|
||||||
/// token containing the client's password and the blinding factor
|
|
||||||
token: oprf::Token<CS::OprfGroup>,
|
|
||||||
ke1_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State,
|
|
||||||
serialized_credential_request: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_clone_for!(struct ClientLogin<CS: CipherSuite>, [token, ke1_state, serialized_credential_request]);
|
|
||||||
impl_debug_eq_hash_for!(
|
|
||||||
struct ClientLogin<CS: CipherSuite>,
|
|
||||||
[token, ke1_state, serialized_credential_request],
|
|
||||||
[oprf::Token<CS::OprfGroup>, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State],
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> ClientLogin<CS> {
|
impl<CS: CipherSuite> ClientLogin<CS> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
|
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
|
||||||
@@ -543,99 +435,6 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> ClientLogin<CS> {
|
impl<CS: CipherSuite> ClientLogin<CS> {
|
||||||
/// Returns an initial "blinded" password request to send to the server, as well as a ClientLogin
|
/// Returns an initial "blinded" password request to send to the server, as well as a ClientLogin
|
||||||
pub fn start<R: RngCore + CryptoRng>(
|
pub fn start<R: RngCore + CryptoRng>(
|
||||||
@@ -743,93 +542,6 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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],
|
|
||||||
);
|
|
||||||
|
|
||||||
/// 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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> ServerLogin<CS> {
|
impl<CS: CipherSuite> ServerLogin<CS> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
@@ -964,68 +676,297 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_serialize_and_deserialize_for!(ServerLogin);
|
/////////////////////////
|
||||||
|
// Convenience Structs //
|
||||||
|
//==================== //
|
||||||
|
/////////////////////////
|
||||||
|
|
||||||
// Zeroize on drop implementations
|
/// 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>),
|
||||||
|
}
|
||||||
|
|
||||||
// This can't be derived because of the use of a phantom parameter
|
/// Optional parameters for client registration finish
|
||||||
impl<CS: CipherSuite> Zeroize for ClientRegistration<CS> {
|
#[derive(Clone)]
|
||||||
fn zeroize(&mut self) {
|
pub struct ClientRegistrationFinishParameters<'h, CS: CipherSuite> {
|
||||||
self.token.data.zeroize();
|
/// Specifying the identifiers idU and idS
|
||||||
self.token.blind.zeroize();
|
pub identifiers: Option<Identifiers>,
|
||||||
|
/// Specifying a configuration for the slow hash
|
||||||
|
pub slow_hash: Option<&'h CS::SlowHash>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'h, CS: CipherSuite> Default for ClientRegistrationFinishParameters<'h, CS> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
identifiers: None,
|
||||||
|
slow_hash: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CS: CipherSuite> Drop for ClientRegistration<CS> {
|
impl<'h, CS: CipherSuite> ClientRegistrationFinishParameters<'h, CS> {
|
||||||
fn drop(&mut self) {
|
/// Create a new [`ClientRegistrationFinishParameters`]
|
||||||
self.zeroize();
|
pub fn new(identifiers: Option<Identifiers>, slow_hash: Option<&'h CS::SlowHash>) -> Self {
|
||||||
|
Self {
|
||||||
|
identifiers,
|
||||||
|
slow_hash,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This can't be derived because of the use of a phantom parameter
|
/// Contains the fields that are returned by a client registration start
|
||||||
impl<CS: CipherSuite> Zeroize for ServerRegistration<CS> {
|
pub struct ClientRegistrationStartResult<CS: CipherSuite> {
|
||||||
fn zeroize(&mut self) {
|
/// The registration request message to be sent to the server
|
||||||
self.0.envelope.zeroize();
|
pub message: RegistrationRequest<CS>,
|
||||||
self.0.masking_key.zeroize();
|
/// The client state that must be persisted in order to complete registration
|
||||||
self.0.client_s_pk.zeroize();
|
pub state: ClientRegistration<CS>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CS: CipherSuite> Drop for ServerRegistration<CS> {
|
/// Contains the fields that are returned by a client registration finish
|
||||||
fn drop(&mut self) {
|
pub struct ClientRegistrationFinishResult<CS: CipherSuite> {
|
||||||
self.zeroize();
|
/// 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(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This can't be derived because of the use of a phantom parameter
|
/// Contains the fields that are returned by a server registration start.
|
||||||
impl<CS: CipherSuite> Zeroize for ClientLogin<CS> {
|
/// Note that there is no state output in this step
|
||||||
fn zeroize(&mut self) {
|
pub struct ServerRegistrationStartResult<CS: CipherSuite> {
|
||||||
self.token.data.zeroize();
|
/// The registration resposne message to send to the client
|
||||||
self.token.blind.zeroize();
|
pub message: RegistrationResponse<CS>,
|
||||||
self.ke1_state.zeroize();
|
/// OPRF key, only used in tests
|
||||||
self.serialized_credential_request.zeroize();
|
#[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(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CS: CipherSuite> Drop for ClientLogin<CS> {
|
/// Contains the fields that are returned by a client login start
|
||||||
fn drop(&mut self) {
|
pub struct ClientLoginStartResult<CS: CipherSuite> {
|
||||||
self.zeroize();
|
/// 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(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This can't be derived because of the use of a phantom parameter
|
/// Optional parameters for client login finish
|
||||||
impl<CS: CipherSuite> Zeroize for ServerLogin<CS> {
|
#[derive(Clone)]
|
||||||
fn zeroize(&mut self) {
|
pub struct ClientLoginFinishParameters<'h, CS: CipherSuite> {
|
||||||
self.ke2_state.zeroize();
|
/// 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<CS: CipherSuite> Drop for ServerLogin<CS> {
|
impl<'h, CS: CipherSuite> ClientLoginFinishParameters<'h, CS> {
|
||||||
fn drop(&mut self) {
|
/// Create a new [`ClientLoginFinishParameters`]
|
||||||
self.zeroize();
|
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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
// Helper functions and Trait Implementations //
|
||||||
|
// ========================================== //
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
|
|
||||||
fn get_password_derived_key<CS: CipherSuite>(
|
fn get_password_derived_key<CS: CipherSuite>(
|
||||||
@@ -1107,3 +1048,80 @@ fn unmask_response<CS: CipherSuite>(
|
|||||||
|
|
||||||
Ok((server_s_pk, envelope))
|
Ok((server_s_pk, envelope))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
self.token.data.zeroize();
|
||||||
|
self.token.blind.zeroize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
self.token.data.zeroize();
|
||||||
|
self.token.blind.zeroize();
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -76,7 +76,7 @@ fn finalize_after_unblind<G: Group, H: Hash>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
// Benchmarking shims //
|
// Benchmarking Shims //
|
||||||
////////////////////////
|
////////////////////////
|
||||||
|
|
||||||
#[cfg(feature = "bench")]
|
#[cfg(feature = "bench")]
|
||||||
|
|||||||
@@ -64,73 +64,6 @@ pub(crate) fn tokenize(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inner macro used for deriving `serde`'s `Serialize` and `Deserialize` traits.
|
|
||||||
macro_rules! impl_serialize_and_deserialize_for {
|
|
||||||
($t:ident) => {
|
|
||||||
#[cfg(feature = "serialize")]
|
|
||||||
impl<CS: CipherSuite> serde::Serialize for $t<CS> {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
if serializer.is_human_readable() {
|
|
||||||
serializer.serialize_str(&base64::encode(&self.serialize()))
|
|
||||||
} else {
|
|
||||||
serializer.serialize_bytes(&self.serialize())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
|
||||||
impl<'de, CS: CipherSuite> serde::Deserialize<'de> for $t<CS> {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
if deserializer.is_human_readable() {
|
|
||||||
let s = <&str>::deserialize(deserializer)?;
|
|
||||||
$t::<CS>::deserialize(&base64::decode(s).map_err(serde::de::Error::custom)?)
|
|
||||||
.map_err(serde::de::Error::custom)
|
|
||||||
} else {
|
|
||||||
struct ByteVisitor<CS: CipherSuite> {
|
|
||||||
marker: core::marker::PhantomData<CS>,
|
|
||||||
}
|
|
||||||
impl<'de, CS: CipherSuite> serde::de::Visitor<'de> for ByteVisitor<CS> {
|
|
||||||
type Value = $t<CS>;
|
|
||||||
fn expecting(
|
|
||||||
&self,
|
|
||||||
formatter: &mut core::fmt::Formatter,
|
|
||||||
) -> core::fmt::Result {
|
|
||||||
formatter.write_str(core::concat!(
|
|
||||||
"the byte representation of a ",
|
|
||||||
core::stringify!($t)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
$t::<CS>::deserialize(value).map_err(|_| {
|
|
||||||
serde::de::Error::invalid_value(
|
|
||||||
serde::de::Unexpected::Bytes(value),
|
|
||||||
&core::concat!(
|
|
||||||
"invalid byte sequence for ",
|
|
||||||
core::stringify!($t)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
deserializer.deserialize_bytes(ByteVisitor::<CS> {
|
|
||||||
marker: core::marker::PhantomData,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ fn credential_response_roundtrip() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn login_third_message_roundtrip() {
|
fn credential_finalization_roundtrip() {
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
let mut mac = [0u8; MAC_SIZE];
|
let mut mac = [0u8; MAC_SIZE];
|
||||||
rng.fill_bytes(&mut mac);
|
rng.fill_bytes(&mut mac);
|
||||||
|
|||||||
Reference in New Issue
Block a user