Adding support for "internal mode" and fake credential response + test vectors (#155)

* Adding support for internal and external mode
This commit is contained in:
Kevin Lewi
2021-06-21 01:29:39 -07:00
committed by Kevin Lewi
parent f0c13945d1
commit 1572ff0104
12 changed files with 907 additions and 927 deletions
+3 -7
View File
@@ -33,7 +33,7 @@ use std::process::exit;
use opaque_ke::{ use opaque_ke::{
ciphersuite::CipherSuite, ciphersuite::CipherSuite,
rand::{rngs::OsRng, RngCore}, rand::{rngs::OsRng, RngCore},
ClientLogin, ClientLoginFinishParameters, ClientLoginStartParameters, ClientRegistration, ClientLogin, ClientLoginFinishParameters, ClientRegistration,
ClientRegistrationFinishParameters, CredentialFinalization, CredentialRequest, ClientRegistrationFinishParameters, CredentialFinalization, CredentialRequest,
CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin, CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin,
ServerLoginStartParameters, ServerRegistration, ServerSetup, ServerLoginStartParameters, ServerRegistration, ServerSetup,
@@ -138,12 +138,8 @@ fn open_locker(
locker: &Locker, locker: &Locker,
) -> Result<String, String> { ) -> Result<String, String> {
let mut client_rng = OsRng; let mut client_rng = OsRng;
let client_login_start_result = ClientLogin::<Default>::start( let client_login_start_result =
&mut client_rng, ClientLogin::<Default>::start(&mut client_rng, password.as_bytes()).unwrap();
password.as_bytes(),
ClientLoginStartParameters::default(),
)
.unwrap();
let credential_request_bytes = client_login_start_result.message.serialize(); let credential_request_bytes = client_login_start_result.message.serialize();
// Client sends credential_request_bytes to server // Client sends credential_request_bytes to server
+5 -10
View File
@@ -27,10 +27,9 @@ use std::process::exit;
use opaque_ke::{ use opaque_ke::{
ciphersuite::CipherSuite, rand::rngs::OsRng, ClientLogin, ClientLoginFinishParameters, ciphersuite::CipherSuite, rand::rngs::OsRng, ClientLogin, ClientLoginFinishParameters,
ClientLoginStartParameters, ClientRegistration, ClientRegistrationFinishParameters, ClientRegistration, ClientRegistrationFinishParameters, CredentialFinalization,
CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest, CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse,
RegistrationResponse, RegistrationUpload, ServerLogin, ServerLoginStartParameters, RegistrationUpload, ServerLogin, ServerLoginStartParameters, ServerRegistration, ServerSetup,
ServerRegistration, ServerSetup,
}; };
// The ciphersuite trait allows to specify the underlying primitives // The ciphersuite trait allows to specify the underlying primitives
@@ -93,12 +92,8 @@ fn account_login(
password_file_bytes: &[u8], password_file_bytes: &[u8],
) -> bool { ) -> bool {
let mut client_rng = OsRng; let mut client_rng = OsRng;
let client_login_start_result = ClientLogin::<Default>::start( let client_login_start_result =
&mut client_rng, ClientLogin::<Default>::start(&mut client_rng, password.as_bytes()).unwrap();
password.as_bytes(),
ClientLoginStartParameters::default(),
)
.unwrap();
let credential_request_bytes = client_login_start_result.message.serialize(); let credential_request_bytes = client_login_start_result.message.serialize();
// Client sends credential_request_bytes to server // Client sends credential_request_bytes to server
+160 -208
View File
@@ -4,10 +4,13 @@
// LICENSE file in the root directory of this source tree. // LICENSE file in the root directory of this source tree.
use crate::{ use crate::{
ciphersuite::CipherSuite,
errors::{utils::check_slice_size, InternalPakeError, PakeError, ProtocolError}, errors::{utils::check_slice_size, InternalPakeError, PakeError, ProtocolError},
group::Group,
hash::Hash, hash::Hash,
keypair::PublicKey, keypair::{KeyPair, PrivateKey, PublicKey},
serialization::serialize, map_to_curve::GroupWithMapToCurve,
opaque::{bytestrings_from_identifiers, Identifiers},
}; };
use digest::Digest; use digest::Digest;
use generic_array::{typenum::Unsigned, GenericArray}; use generic_array::{typenum::Unsigned, GenericArray};
@@ -19,81 +22,62 @@ use std::convert::TryFrom;
use zeroize::Zeroize; use zeroize::Zeroize;
// Constant string used as salt for HKDF computation // Constant string used as salt for HKDF computation
const STR_PAD: &[u8] = b"Pad";
const STR_AUTH_KEY: &[u8] = b"AuthKey"; 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_OPAQUE_HASH_TO_SCALAR: &[u8] = b"OPAQUE-HashToScalar";
const NONCE_LEN: usize = 32; const NONCE_LEN: usize = 32;
fn build_inner_envelope_internal<CS: CipherSuite>(
random_pwd: &[u8],
nonce: &[u8],
) -> Result<PublicKey, InternalPakeError> {
let h = Hkdf::<CS::Hash>::new(None, random_pwd);
let mut keypair_seed = vec![0u8; <PrivateKey as SizedBytes>::Len::to_usize()];
h.expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed)
.map_err(|_| InternalPakeError::HkdfError)?;
let client_static_keypair =
KeyPair::<CS::Group>::from_private_key_slice(CS::Group::scalar_as_bytes(
&CS::Group::hash_to_scalar::<CS::Hash>(&keypair_seed[..], STR_OPAQUE_HASH_TO_SCALAR)?,
))?;
Ok(client_static_keypair.public().clone())
}
fn recover_keys_internal<CS: CipherSuite>(
random_pwd: &[u8],
nonce: &[u8],
) -> Result<KeyPair<CS::Group>, InternalPakeError> {
let h = Hkdf::<CS::Hash>::new(None, random_pwd);
let mut keypair_seed = vec![0u8; <PrivateKey as SizedBytes>::Len::to_usize()];
h.expand(&[nonce, STR_PRIVATE_KEY].concat(), &mut keypair_seed)
.map_err(|_| InternalPakeError::HkdfError)?;
let client_static_keypair =
KeyPair::<CS::Group>::from_private_key_slice(CS::Group::scalar_as_bytes(
&CS::Group::hash_to_scalar::<CS::Hash>(&keypair_seed[..], STR_OPAQUE_HASH_TO_SCALAR)?,
))?;
Ok(client_static_keypair)
}
#[derive(Clone, Copy, PartialEq, Zeroize)] #[derive(Clone, Copy, PartialEq, Zeroize)]
#[zeroize(drop)] #[zeroize(drop)]
pub(crate) enum InnerEnvelopeMode { pub(crate) enum InnerEnvelopeMode {
Unused = 0, Zero = 0,
Base = 1, Internal = 1,
CustomIdentifier = 2,
} }
impl TryFrom<u8> for InnerEnvelopeMode { impl TryFrom<u8> for InnerEnvelopeMode {
type Error = PakeError; type Error = PakeError;
fn try_from(x: u8) -> Result<Self, Self::Error> { fn try_from(x: u8) -> Result<Self, Self::Error> {
match x { match x {
1 => Ok(InnerEnvelopeMode::Base), 1 => Ok(InnerEnvelopeMode::Internal),
2 => Ok(InnerEnvelopeMode::CustomIdentifier),
_ => Err(PakeError::SerializationError), _ => Err(PakeError::SerializationError),
} }
} }
} }
#[derive(Clone, Zeroize)]
#[zeroize(drop)]
pub(crate) struct InnerEnvelope {
mode: InnerEnvelopeMode,
nonce: Vec<u8>,
ciphertext: Vec<u8>,
}
impl InnerEnvelope {
pub(crate) fn serialize(&self) -> Vec<u8> {
[&[self.mode as u8], &self.nonce[..], &self.ciphertext[..]].concat()
}
pub(crate) fn deserialize(input: &[u8]) -> Result<(Self, Vec<u8>), ProtocolError> {
if input.is_empty() {
return Err(ProtocolError::VerificationError(
PakeError::SerializationError,
));
}
let mode = InnerEnvelopeMode::try_from(input[0])?;
let key_len = <PublicKey as SizedBytes>::Len::to_usize();
let bytes = &input[1..];
if bytes.len() < NONCE_LEN + key_len {
return Err(ProtocolError::VerificationError(
PakeError::SerializationError,
));
}
Ok((
Self {
mode,
nonce: bytes[..NONCE_LEN].to_vec(),
ciphertext: bytes[NONCE_LEN..NONCE_LEN + key_len].to_vec(),
},
bytes[NONCE_LEN + key_len..].to_vec(),
))
}
#[cfg(test)]
pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
vec![
/* Cannot easily get raw pointer of enum value, otherwise would do self.mode.as_ptr() */
(self.nonce.as_ptr(), self.nonce.len()),
(self.ciphertext.as_ptr(), self.ciphertext.len()),
]
}
}
/// This struct is an instantiation of the envelope as described in /// This struct is an instantiation of the envelope as described in
/// https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-06#section-4 /// https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-06#section-4
/// ///
@@ -104,128 +88,151 @@ impl InnerEnvelope {
/// The specification update has simplified this assumption by taking /// The specification update has simplified this assumption by taking
/// an XOR-based approach without compromising on security, and to avoid /// an XOR-based approach without compromising on security, and to avoid
/// the confusion around the implementation of an RKR-secure encryption. /// the confusion around the implementation of an RKR-secure encryption.
#[derive(Clone)] pub(crate) struct Envelope<CS: CipherSuite> {
pub(crate) struct Envelope<D: Hash> { mode: InnerEnvelopeMode,
inner_envelope: InnerEnvelope, nonce: Vec<u8>,
hmac: GenericArray<u8, <D as Digest>::OutputSize>, hmac: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
}
// Cannot be derived because it would require for CS to be Clone.
impl<CS: CipherSuite> Clone for Envelope<CS> {
fn clone(&self) -> Self {
Self {
mode: self.mode,
nonce: self.nonce.clone(),
hmac: self.hmac.clone(),
}
}
} }
// Note that this struct represents an envelope that has been "opened" with the asssociated // Note that this struct represents an envelope that has been "opened" with the asssociated
// key. This key is also used to derive the export_key parameter, which is technically // key. This key is also used to derive the export_key parameter, which is technically
// unrelated to the envelope's encrypted and authenticated contents. // unrelated to the envelope's encrypted and authenticated contents.
pub(crate) struct OpenedEnvelope<D: Hash> { pub(crate) struct OpenedEnvelope<CS: CipherSuite> {
pub(crate) client_s_sk: Vec<u8>, pub(crate) client_static_keypair: KeyPair<CS::Group>,
pub(crate) export_key: GenericArray<u8, <D as Digest>::OutputSize>, pub(crate) export_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
pub(crate) id_u: Vec<u8>,
pub(crate) id_s: Vec<u8>,
} }
pub(crate) struct OpenedInnerEnvelope<D: Hash> { pub(crate) struct OpenedInnerEnvelope<D: Hash> {
pub(crate) plaintext: Vec<u8>,
pub(crate) export_key: GenericArray<u8, <D as Digest>::OutputSize>, pub(crate) export_key: GenericArray<u8, <D as Digest>::OutputSize>,
} }
impl<D: Hash> Envelope<D> { impl<CS: CipherSuite> Envelope<CS> {
fn hmac_key_size() -> usize { fn hmac_key_size() -> usize {
<D as Digest>::OutputSize::to_usize() <CS::Hash as Digest>::OutputSize::to_usize()
} }
fn export_key_size() -> usize { fn export_key_size() -> usize {
<D as Digest>::OutputSize::to_usize() <CS::Hash as Digest>::OutputSize::to_usize()
} }
pub(crate) fn len() -> usize { pub(crate) fn len() -> usize {
1 + <PublicKey as SizedBytes>::Len::to_usize() + <D as Digest>::OutputSize::to_usize() + NONCE_LEN <CS::Hash as Digest>::OutputSize::to_usize() + NONCE_LEN
}
pub(crate) fn get_mode(&self) -> InnerEnvelopeMode {
self.inner_envelope.mode
} }
pub(crate) fn serialize(&self) -> Vec<u8> { pub(crate) fn serialize(&self) -> Vec<u8> {
[&self.inner_envelope.serialize(), &self.hmac[..]].concat() [&self.nonce[..], &self.hmac[..]].concat()
} }
pub(crate) fn deserialize(bytes: &[u8]) -> Result<Self, ProtocolError> {
let mode = InnerEnvelopeMode::Internal; // Better way to hard-code this?
pub(crate) fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> { if bytes.len() < NONCE_LEN {
let (inner_envelope, remainder) = InnerEnvelope::deserialize(input) return Err(ProtocolError::VerificationError(
.map_err(|_| ProtocolError::InvalidInnerEnvelopeError)?; PakeError::SerializationError,
));
}
let nonce = bytes[..NONCE_LEN].to_vec();
let remainder = match mode {
InnerEnvelopeMode::Zero => {
return Err(InternalPakeError::IncompatibleEnvelopeModeError.into())
}
InnerEnvelopeMode::Internal => bytes[NONCE_LEN..].to_vec(),
};
let hmac_key_size = Self::hmac_key_size(); let hmac_key_size = Self::hmac_key_size();
let hmac = check_slice_size(&remainder, hmac_key_size, "hmac_key_size")?; let hmac = check_slice_size(&remainder, hmac_key_size, "hmac_key_size")?;
Ok(Self { Ok(Self {
inner_envelope, mode,
hmac: GenericArray::clone_from_slice(&hmac), nonce,
hmac: GenericArray::clone_from_slice(hmac),
}) })
} }
// Creates a dummy envelope object that serializes to the all-zeros byte string // Creates a dummy envelope object that serializes to the all-zeros byte string
pub(crate) fn dummy() -> Self { pub(crate) fn dummy() -> Self {
Self { Self {
inner_envelope: InnerEnvelope { mode: InnerEnvelopeMode::Zero,
mode: InnerEnvelopeMode::Unused, nonce: vec![0u8; NONCE_LEN],
nonce: vec![0u8; NONCE_LEN], hmac: GenericArray::clone_from_slice(&vec![
ciphertext: vec![0u8; <PublicKey as SizedBytes>::Len::to_usize()], 0u8;
}, <CS::Hash as Digest>::OutputSize::to_usize()
hmac: GenericArray::clone_from_slice(&vec![0u8; <D as Digest>::OutputSize::to_usize()]), ]),
} }
} }
#[allow(clippy::type_complexity)]
pub(crate) fn seal<R: RngCore + CryptoRng>( pub(crate) fn seal<R: RngCore + CryptoRng>(
rng: &mut R, rng: &mut R,
key: &[u8], key: &[u8],
client_s_sk: &[u8],
server_s_pk: &[u8], server_s_pk: &[u8],
optional_ids: Option<(Vec<u8>, Vec<u8>)>, optional_ids: Option<Identifiers>,
) -> Result<(Self, GenericArray<u8, <D as Digest>::OutputSize>), InternalPakeError> { ) -> Result<
let aad = construct_aad(server_s_pk, &optional_ids); (
Self::seal_raw(rng, key, client_s_sk, &aad, mode_from_ids(&optional_ids)) Self,
PublicKey,
GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
),
InternalPakeError,
> {
let mut nonce = vec![0u8; NONCE_LEN];
rng.fill_bytes(&mut nonce);
let (mode, client_s_pk) = (
InnerEnvelopeMode::Internal,
build_inner_envelope_internal::<CS>(key, &nonce)?,
);
let (id_u, id_s) =
bytestrings_from_identifiers(&optional_ids, &client_s_pk.to_arr(), server_s_pk);
let aad = construct_aad(&id_u, &id_s, server_s_pk);
let (envelope, export_key) = Self::seal_raw(key, &nonce, &aad, mode)?;
Ok((envelope, client_s_pk, export_key))
} }
/// Uses a key to convert the plaintext into an envelope, authenticated by the aad field. /// Uses a key to convert the plaintext into an envelope, authenticated by the aad field.
/// Note that a new nonce is sampled for each call to seal. /// Note that a new nonce is sampled for each call to seal.
pub(crate) fn seal_raw<R: RngCore + CryptoRng>( #[allow(clippy::type_complexity)]
rng: &mut R, pub(crate) fn seal_raw(
key: &[u8], key: &[u8],
plaintext: &[u8], nonce: &[u8],
aad: &[u8], aad: &[u8],
mode: InnerEnvelopeMode, mode: InnerEnvelopeMode,
) -> Result<(Self, GenericArray<u8, <D as Digest>::OutputSize>), InternalPakeError> { ) -> Result<(Self, GenericArray<u8, <CS::Hash as Digest>::OutputSize>), InternalPakeError> {
let mut nonce = vec![0u8; NONCE_LEN]; let h = Hkdf::<CS::Hash>::new(None, key);
rng.fill_bytes(&mut nonce);
let h = Hkdf::<D>::new(None, key);
let mut xor_key = vec![0u8; plaintext.len()];
let mut hmac_key = vec![0u8; Self::hmac_key_size()]; let mut hmac_key = vec![0u8; Self::hmac_key_size()];
let mut export_key = vec![0u8; Self::export_key_size()]; let mut export_key = vec![0u8; Self::export_key_size()];
h.expand(&[&nonce, STR_PAD].concat(), &mut xor_key) h.expand(&[nonce, STR_AUTH_KEY].concat(), &mut hmac_key)
.map_err(|_| InternalPakeError::HkdfError)?; .map_err(|_| InternalPakeError::HkdfError)?;
h.expand(&[&nonce, STR_AUTH_KEY].concat(), &mut hmac_key) h.expand(&[nonce, STR_EXPORT_KEY].concat(), &mut export_key)
.map_err(|_| InternalPakeError::HkdfError)?; .map_err(|_| InternalPakeError::HkdfError)?;
h.expand(&[&nonce, STR_EXPORT_KEY].concat(), &mut export_key)
.map_err(|_| InternalPakeError::HkdfError)?;
let ciphertext: Vec<u8> = xor_key
.iter()
.zip(plaintext.iter())
.map(|(&x1, &x2)| x1 ^ x2)
.collect();
let inner_envelope = InnerEnvelope {
mode,
nonce,
ciphertext,
};
let mut hmac = let mut hmac =
Hmac::<D>::new_varkey(&hmac_key).map_err(|_| InternalPakeError::HmacError)?; Hmac::<CS::Hash>::new_varkey(&hmac_key).map_err(|_| InternalPakeError::HmacError)?;
hmac.update(&inner_envelope.serialize()); hmac.update(nonce);
hmac.update(aad); hmac.update(aad);
let hmac_bytes = hmac.finalize().into_bytes(); let hmac_bytes = hmac.finalize().into_bytes();
Ok(( Ok((
Self { Self {
inner_envelope, mode,
nonce: nonce.to_vec(),
hmac: hmac_bytes, hmac: hmac_bytes,
}, },
GenericArray::clone_from_slice(&export_key), GenericArray::clone_from_slice(&export_key),
@@ -236,24 +243,29 @@ impl<D: Hash> Envelope<D> {
&self, &self,
key: &[u8], key: &[u8],
server_s_pk: &[u8], server_s_pk: &[u8],
optional_ids: &Option<(Vec<u8>, Vec<u8>)>, optional_ids: &Option<Identifiers>,
) -> Result<OpenedEnvelope<D>, InternalPakeError> { ) -> Result<OpenedEnvelope<CS>, InternalPakeError> {
// First, check that mode matches let client_static_keypair = match self.mode {
if self.inner_envelope.mode != mode_from_ids(optional_ids) { InnerEnvelopeMode::Zero => {
return Err(InternalPakeError::IncompatibleEnvelopeModeError); return Err(InternalPakeError::IncompatibleEnvelopeModeError)
} }
InnerEnvelopeMode::Internal => recover_keys_internal::<CS>(key, &self.nonce)?,
};
let (id_u, id_s) = bytestrings_from_identifiers(
optional_ids,
&client_static_keypair.public().to_arr(),
server_s_pk,
);
let aad = construct_aad(&id_u, &id_s, server_s_pk);
let aad = construct_aad(server_s_pk, optional_ids);
let opened = self.open_raw(key, &aad)?; let opened = self.open_raw(key, &aad)?;
if opened.plaintext.len() != <PublicKey as SizedBytes>::Len::to_usize() {
// Plaintext should consist of a single key
return Err(InternalPakeError::UnexpectedEnvelopeContentsError);
}
Ok(OpenedEnvelope { Ok(OpenedEnvelope {
client_s_sk: opened.plaintext, client_static_keypair,
export_key: opened.export_key, export_key: opened.export_key,
id_u,
id_s,
}) })
} }
@@ -263,44 +275,26 @@ impl<D: Hash> Envelope<D> {
&self, &self,
key: &[u8], key: &[u8],
aad: &[u8], aad: &[u8],
) -> Result<OpenedInnerEnvelope<D>, InternalPakeError> { ) -> Result<OpenedInnerEnvelope<CS::Hash>, InternalPakeError> {
let h = Hkdf::<D>::new(None, key); let h = Hkdf::<CS::Hash>::new(None, key);
let mut xor_key = vec![0u8; self.inner_envelope.ciphertext.len()];
let mut hmac_key = vec![0u8; Self::hmac_key_size()]; let mut hmac_key = vec![0u8; Self::hmac_key_size()];
let mut export_key = vec![0u8; Self::export_key_size()]; let mut export_key = vec![0u8; Self::export_key_size()];
h.expand( h.expand(&[&self.nonce, STR_AUTH_KEY].concat(), &mut hmac_key)
&[&self.inner_envelope.nonce, STR_PAD].concat(), .map_err(|_| InternalPakeError::HkdfError)?;
&mut xor_key, h.expand(&[&self.nonce, STR_EXPORT_KEY].concat(), &mut export_key)
) .map_err(|_| InternalPakeError::HkdfError)?;
.map_err(|_| InternalPakeError::HkdfError)?;
h.expand(
&[&self.inner_envelope.nonce, STR_AUTH_KEY].concat(),
&mut hmac_key,
)
.map_err(|_| InternalPakeError::HkdfError)?;
h.expand(
&[&self.inner_envelope.nonce, STR_EXPORT_KEY].concat(),
&mut export_key,
)
.map_err(|_| InternalPakeError::HkdfError)?;
let mut hmac = let mut hmac =
Hmac::<D>::new_varkey(&hmac_key).map_err(|_| InternalPakeError::HmacError)?; Hmac::<CS::Hash>::new_varkey(&hmac_key).map_err(|_| InternalPakeError::HmacError)?;
hmac.update(&self.inner_envelope.serialize()); hmac.update(&self.nonce);
hmac.update(aad); hmac.update(aad);
if hmac.verify(&self.hmac).is_err() { if hmac.verify(&self.hmac).is_err() {
return Err(InternalPakeError::SealOpenHmacError); return Err(InternalPakeError::SealOpenHmacError);
} }
let plaintext: Vec<u8> = xor_key
.iter()
.zip(self.inner_envelope.ciphertext.iter())
.map(|(&x1, &x2)| x1 ^ x2)
.collect();
Ok(OpenedInnerEnvelope { Ok(OpenedInnerEnvelope {
plaintext, export_key: GenericArray::<u8, <CS::Hash as Digest>::OutputSize>::clone_from_slice(
export_key: GenericArray::<u8, <D as Digest>::OutputSize>::clone_from_slice(
&export_key, &export_key,
), ),
}) })
@@ -308,23 +302,20 @@ impl<D: Hash> Envelope<D> {
#[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())]
self.inner_envelope.as_byte_ptrs(),
vec![(self.hmac.as_ptr(), self.hmac.len())],
]
.concat()
} }
} }
// This can't be derived because of the use of a phantom parameter // This can't be derived because of the use of a phantom parameter
impl<D: Hash> Zeroize for Envelope<D> { impl<CS: CipherSuite> Zeroize for Envelope<CS> {
fn zeroize(&mut self) { fn zeroize(&mut self) {
self.inner_envelope.zeroize(); self.mode.zeroize();
self.nonce.zeroize();
self.hmac.zeroize(); self.hmac.zeroize();
} }
} }
impl<D: Hash> Drop for Envelope<D> { impl<CS: CipherSuite> Drop for Envelope<CS> {
fn drop(&mut self) { fn drop(&mut self) {
self.zeroize(); self.zeroize();
} }
@@ -332,45 +323,6 @@ impl<D: Hash> Drop for Envelope<D> {
// Helper functions // Helper functions
fn construct_aad(server_s_pk: &[u8], optional_ids: &Option<(Vec<u8>, Vec<u8>)>) -> Vec<u8> { fn construct_aad(id_u: &[u8], id_s: &[u8], server_s_pk: &[u8]) -> Vec<u8> {
let ids = optional_ids [server_s_pk, id_s, id_u].concat()
.iter()
.flat_map(|(l, r)| [serialize(l, 2), serialize(r, 2)].concat())
.collect();
[server_s_pk.to_vec(), ids].concat()
}
pub(crate) fn mode_from_ids(optional_ids: &Option<(Vec<u8>, Vec<u8>)>) -> InnerEnvelopeMode {
match optional_ids {
Some(_) => InnerEnvelopeMode::CustomIdentifier,
None => InnerEnvelopeMode::Base,
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::rngs::OsRng;
#[test]
fn seal_and_open() {
let mut rng = OsRng;
let mut key = [0u8; 32];
rng.fill_bytes(&mut key);
let mut msg = [0u8; 100];
rng.fill_bytes(&mut msg);
let (envelope, export_key) = Envelope::<sha2::Sha256>::seal_raw(
&mut rng,
&key,
&msg,
b"aad",
InnerEnvelopeMode::Base,
)
.unwrap();
let opened_envelope = envelope.open_raw(&key, b"aad").unwrap();
assert_eq!(&msg.to_vec(), &opened_envelope.plaintext);
assert_eq!(&export_key.to_vec(), &opened_envelope.export_key.to_vec());
}
} }
+4 -4
View File
@@ -21,7 +21,6 @@ pub trait KeyExchange<D: Hash, G: Group> {
type KE3Message: FromBytes + ToBytes + Clone; type KE3Message: FromBytes + ToBytes + Clone;
fn generate_ke1<R: RngCore + CryptoRng>( fn generate_ke1<R: RngCore + CryptoRng>(
info: Vec<u8>,
rng: &mut R, rng: &mut R,
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError>; ) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError>;
@@ -35,8 +34,8 @@ pub trait KeyExchange<D: Hash, G: Group> {
server_s_sk: PrivateKey, server_s_sk: PrivateKey,
id_u: Vec<u8>, id_u: Vec<u8>,
id_s: Vec<u8>, id_s: Vec<u8>,
e_info: Vec<u8>, context: Vec<u8>,
) -> Result<(Vec<u8>, Self::KE2State, Self::KE2Message), ProtocolError>; ) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError>;
#[allow(clippy::too_many_arguments, clippy::type_complexity)] #[allow(clippy::too_many_arguments, clippy::type_complexity)]
fn generate_ke3( fn generate_ke3(
@@ -48,7 +47,8 @@ pub trait KeyExchange<D: Hash, G: Group> {
client_s_sk: PrivateKey, client_s_sk: PrivateKey,
id_u: Vec<u8>, id_u: Vec<u8>,
id_s: Vec<u8>, id_s: Vec<u8>,
) -> Result<(Vec<u8>, Vec<u8>, Self::KE3Message), ProtocolError>; context: Vec<u8>,
) -> Result<(Vec<u8>, Self::KE3Message), ProtocolError>;
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
fn finish_ke( fn finish_ke(
+33 -90
View File
@@ -14,7 +14,7 @@ use crate::{
hash::Hash, hash::Hash,
key_exchange::traits::{FromBytes, KeyExchange, ToBytes, ToBytesWithPointers}, key_exchange::traits::{FromBytes, KeyExchange, ToBytes, ToBytesWithPointers},
keypair::{KeyPair, PrivateKey, PublicKey, SizedBytesExt}, keypair::{KeyPair, PrivateKey, PublicKey, SizedBytesExt},
serialization::{serialize, tokenize}, serialization::serialize,
}; };
use digest::{Digest, FixedOutput}; use digest::{Digest, FixedOutput};
use generic_array::{ use generic_array::{
@@ -30,14 +30,12 @@ use zeroize::Zeroize;
const KEY_LEN: usize = 32; const KEY_LEN: usize = 32;
pub(crate) type NonceLen = U32; pub(crate) type NonceLen = U32;
static STR_3DH: &[u8] = b"3DH"; static STR_RFC: &[u8] = b"RFCXXXX";
static STR_CLIENT_MAC: &[u8] = b"client mac"; static STR_CLIENT_MAC: &[u8] = b"ClientMAC";
static STR_HANDSHAKE_SECRET: &[u8] = b"handshake secret"; static STR_HANDSHAKE_SECRET: &[u8] = b"HandshakeSecret";
static STR_SERVER_MAC: &[u8] = b"server mac"; static STR_SERVER_MAC: &[u8] = b"ServerMAC";
static STR_HANDSHAKE_ENC: &[u8] = b"handshake enc"; static STR_SESSION_KEY: &[u8] = b"SessionKey";
static STR_ENCRYPTION_PAD: &[u8] = b"encryption pad"; static STR_OPAQUE: &[u8] = b"OPAQUE-";
static STR_SESSION_SECRET: &[u8] = b"session secret";
static STR_OPAQUE: &[u8] = b"OPAQUE ";
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
/// The Triple Diffie-Hellman key exchange implementation /// The Triple Diffie-Hellman key exchange implementation
@@ -51,7 +49,6 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
type KE3Message = Ke3Message<<D as FixedOutput>::OutputSize>; type KE3Message = Ke3Message<<D as FixedOutput>::OutputSize>;
fn generate_ke1<R: RngCore + CryptoRng>( fn generate_ke1<R: RngCore + CryptoRng>(
info: Vec<u8>,
rng: &mut R, rng: &mut R,
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> { ) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> {
let client_e_kp = KeyPair::<G>::generate_random(rng); let client_e_kp = KeyPair::<G>::generate_random(rng);
@@ -59,7 +56,6 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
let ke1_message = Ke1Message { let ke1_message = Ke1Message {
client_nonce, client_nonce,
info,
client_e_pk: client_e_kp.public().clone(), client_e_pk: client_e_kp.public().clone(),
}; };
@@ -82,21 +78,22 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
server_s_sk: PrivateKey, server_s_sk: PrivateKey,
id_u: Vec<u8>, id_u: Vec<u8>,
id_s: Vec<u8>, id_s: Vec<u8>,
e_info: Vec<u8>, context: Vec<u8>,
) -> Result<(Vec<u8>, Self::KE2State, Self::KE2Message), ProtocolError> { ) -> Result<(Self::KE2State, Self::KE2Message), ProtocolError> {
let server_e_kp = KeyPair::<G>::generate_random(rng); let server_e_kp = KeyPair::<G>::generate_random(rng);
let server_nonce = generate_nonce::<R>(rng); let server_nonce = generate_nonce::<R>(rng);
let mut transcript_hasher = D::new() let mut transcript_hasher = D::new()
.chain(STR_3DH) .chain(STR_RFC)
.chain(&serialize(&id_u, 2)) .chain(&serialize(&context, 2))
.chain(&id_u)
.chain(&serialized_credential_request[..]) .chain(&serialized_credential_request[..])
.chain(&serialize(&id_s, 2)) .chain(&id_s)
.chain(&l2_bytes[..]) .chain(&l2_bytes[..])
.chain(&server_nonce[..]) .chain(&server_nonce[..])
.chain(&server_e_kp.public().to_arr()); .chain(&server_e_kp.public().to_arr());
let (session_key, km2, ke2, km3) = derive_3dh_keys::<D, G>( let (session_key, km2, km3) = derive_3dh_keys::<D, G>(
TripleDHComponents { TripleDHComponents {
pk1: ke1_message.client_e_pk.clone(), pk1: ke1_message.client_e_pk.clone(),
sk1: server_e_kp.private().clone(), sk1: server_e_kp.private().clone(),
@@ -108,19 +105,6 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
&transcript_hasher.clone().finalize(), &transcript_hasher.clone().finalize(),
)?; )?;
// Compute encryption of e_info
let h = Hkdf::<D>::from_prk(&ke2).map_err(|_| InternalPakeError::HkdfError)?;
let mut encryption_pad = vec![0u8; e_info.len()];
h.expand(STR_ENCRYPTION_PAD, &mut encryption_pad)
.map_err(|_| InternalPakeError::HkdfError)?;
let ciphertext: Vec<u8> = encryption_pad
.iter()
.zip(e_info.iter())
.map(|(&x1, &x2)| x1 ^ x2)
.collect();
transcript_hasher.update(&serialize(&ciphertext, 2));
let mut mac_hasher = let mut mac_hasher =
Hmac::<D>::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?; Hmac::<D>::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?;
mac_hasher.update(&transcript_hasher.clone().finalize()); mac_hasher.update(&transcript_hasher.clone().finalize());
@@ -129,7 +113,6 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
transcript_hasher.update(&mac); transcript_hasher.update(&mac);
Ok(( Ok((
ke1_message.info,
Ke2State { Ke2State {
km3, km3,
hashed_transcript: transcript_hasher.finalize(), hashed_transcript: transcript_hasher.finalize(),
@@ -138,7 +121,6 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
Ke2Message { Ke2Message {
server_nonce, server_nonce,
server_e_pk: server_e_kp.public().clone(), server_e_pk: server_e_kp.public().clone(),
e_info: ciphertext,
mac, mac,
}, },
)) ))
@@ -154,16 +136,18 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
client_s_sk: PrivateKey, client_s_sk: PrivateKey,
id_u: Vec<u8>, id_u: Vec<u8>,
id_s: Vec<u8>, id_s: Vec<u8>,
) -> Result<(Vec<u8>, Vec<u8>, Self::KE3Message), ProtocolError> { context: Vec<u8>,
) -> Result<(Vec<u8>, Self::KE3Message), ProtocolError> {
let mut transcript_hasher = D::new() let mut transcript_hasher = D::new()
.chain(STR_3DH) .chain(STR_RFC)
.chain(&serialize(&id_u, 2)) .chain(&serialize(&context, 2))
.chain(&id_u)
.chain(&serialized_credential_request) .chain(&serialized_credential_request)
.chain(&serialize(&id_s, 2)) .chain(&id_s)
.chain(&l2_component[..]) .chain(&l2_component[..])
.chain(&ke2_message.to_bytes_without_info_or_mac()); .chain(&ke2_message.to_bytes_without_info_or_mac());
let (session_key, km2, ke2, km3) = derive_3dh_keys::<D, G>( let (session_key, km2, km3) = derive_3dh_keys::<D, G>(
TripleDHComponents { TripleDHComponents {
pk1: ke2_message.server_e_pk.clone(), pk1: ke2_message.server_e_pk.clone(),
sk1: ke1_state.client_e_sk.clone(), sk1: ke1_state.client_e_sk.clone(),
@@ -175,8 +159,6 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
&transcript_hasher.clone().finalize(), &transcript_hasher.clone().finalize(),
)?; )?;
transcript_hasher.update(&serialize(&ke2_message.e_info[..], 2));
let mut server_mac = let mut server_mac =
Hmac::<D>::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?; Hmac::<D>::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?;
server_mac.update(&transcript_hasher.clone().finalize()); server_mac.update(&transcript_hasher.clone().finalize());
@@ -193,19 +175,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
Hmac::<D>::new_varkey(&km3).map_err(|_| InternalPakeError::HmacError)?; Hmac::<D>::new_varkey(&km3).map_err(|_| InternalPakeError::HmacError)?;
client_mac.update(&transcript_hasher.finalize()); client_mac.update(&transcript_hasher.finalize());
// Compute decryption of e_info
let h = Hkdf::<D>::from_prk(&ke2).map_err(|_| InternalPakeError::HkdfError)?;
let mut encryption_pad = vec![0u8; ke2_message.e_info.len()];
h.expand(STR_ENCRYPTION_PAD, &mut encryption_pad)
.map_err(|_| InternalPakeError::HkdfError)?;
let plaintext: Vec<u8> = encryption_pad
.iter()
.zip(ke2_message.e_info.iter())
.map(|(&x1, &x2)| x1 ^ x2)
.collect();
Ok(( Ok((
plaintext,
session_key.to_vec(), session_key.to_vec(),
Ke3Message { Ke3Message {
mac: client_mac.finalize().into_bytes(), mac: client_mac.finalize().into_bytes(),
@@ -248,7 +218,6 @@ pub struct Ke1State {
#[derive(PartialEq, Eq, Clone)] #[derive(PartialEq, Eq, Clone)]
pub struct Ke1Message { pub struct Ke1Message {
pub(crate) client_nonce: GenericArray<u8, NonceLen>, pub(crate) client_nonce: GenericArray<u8, NonceLen>,
pub(crate) info: Vec<u8>,
pub(crate) client_e_pk: PublicKey, pub(crate) client_e_pk: PublicKey,
} }
@@ -286,12 +255,7 @@ impl ToBytesWithPointers for Ke1State {
impl ToBytes for Ke1Message { impl ToBytes for Ke1Message {
fn to_bytes(&self) -> Vec<u8> { fn to_bytes(&self) -> Vec<u8> {
[ [&self.client_nonce[..], &self.client_e_pk.to_arr()].concat()
&self.client_nonce[..],
&serialize(&self.info, 2),
&self.client_e_pk.to_arr(),
]
.concat()
} }
} }
@@ -299,20 +263,11 @@ impl FromBytes for Ke1Message {
fn from_bytes<CS: CipherSuite>(ke1_message_bytes: &[u8]) -> Result<Self, PakeError> { fn from_bytes<CS: CipherSuite>(ke1_message_bytes: &[u8]) -> Result<Self, PakeError> {
let nonce_len = NonceLen::to_usize(); let nonce_len = NonceLen::to_usize();
let checked_nonce = let checked_nonce =
check_slice_size_atleast(ke1_message_bytes, nonce_len, "ke1_message nonce")?; check_slice_size(ke1_message_bytes, nonce_len + KEY_LEN, "ke1_message nonce")?;
let (info, remainder) = tokenize(&checked_nonce[nonce_len..], 2)?;
// Check the public key bytes
let unchecked_client_e_pk =
check_slice_size(&remainder, KEY_LEN, "ke1_message client_e_pk")?;
let client_e_pk =
KeyPair::<CS::Group>::check_public_key(PublicKey::from_bytes(unchecked_client_e_pk)?)?;
Ok(Self { Ok(Self {
client_nonce: GenericArray::clone_from_slice(&checked_nonce[..nonce_len]), client_nonce: GenericArray::clone_from_slice(&checked_nonce[..nonce_len]),
info, client_e_pk: PublicKey::from_bytes(&checked_nonce[nonce_len..])?,
client_e_pk,
}) })
} }
} }
@@ -364,7 +319,6 @@ impl<HashLen: ArrayLength<u8>> ToBytesWithPointers for Ke2State<HashLen> {
pub struct Ke2Message<HashLen: ArrayLength<u8>> { pub struct Ke2Message<HashLen: ArrayLength<u8>> {
server_nonce: GenericArray<u8, NonceLen>, server_nonce: GenericArray<u8, NonceLen>,
server_e_pk: PublicKey, server_e_pk: PublicKey,
e_info: Vec<u8>,
mac: GenericArray<u8, HashLen>, mac: GenericArray<u8, HashLen>,
} }
@@ -385,12 +339,7 @@ impl<HashLen: ArrayLength<u8>> FromBytes for Ke2State<HashLen> {
impl<HashLen: ArrayLength<u8>> ToBytes for Ke2Message<HashLen> { impl<HashLen: ArrayLength<u8>> ToBytes for Ke2Message<HashLen> {
fn to_bytes(&self) -> Vec<u8> { fn to_bytes(&self) -> Vec<u8> {
[ [&self.to_bytes_without_info_or_mac(), &self.mac[..]].concat()
&self.to_bytes_without_info_or_mac(),
&serialize(&self.e_info, 2),
&self.mac[..],
]
.concat()
} }
} }
@@ -410,8 +359,11 @@ impl<HashLen: ArrayLength<u8>> FromBytes for Ke2Message<HashLen> {
KEY_LEN, KEY_LEN,
"ke2_message server_e_pk", "ke2_message server_e_pk",
)?; )?;
let (e_info, remainder) = tokenize(&unchecked_server_e_pk[KEY_LEN..], 2)?; let checked_mac = check_slice_size(
let checked_mac = check_slice_size(&remainder, HashLen::to_usize(), "ke1_message mac")?; &unchecked_server_e_pk[KEY_LEN..],
HashLen::to_usize(),
"ke1_message mac",
)?;
// Check the public key bytes // Check the public key bytes
let server_e_pk = KeyPair::<CS::Group>::check_public_key(PublicKey::from_bytes( let server_e_pk = KeyPair::<CS::Group>::check_public_key(PublicKey::from_bytes(
@@ -420,8 +372,7 @@ impl<HashLen: ArrayLength<u8>> FromBytes for Ke2Message<HashLen> {
Ok(Self { Ok(Self {
server_nonce: GenericArray::clone_from_slice(&checked_nonce[..nonce_len]), server_nonce: GenericArray::clone_from_slice(&checked_nonce[..nonce_len]),
server_e_pk, server_e_pk: PublicKey::from_bytes(&server_e_pk)?,
e_info,
mac: GenericArray::clone_from_slice(checked_mac), mac: GenericArray::clone_from_slice(checked_mac),
}) })
} }
@@ -439,12 +390,11 @@ struct TripleDHComponents {
} }
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
// Consists of a session key, followed by two mac keys and an encryption key: (session_key, km2, ke2, km3) // Consists of a session key, followed by two mac keys: (session_key, km2, km3)
type TripleDHDerivationResult<D> = ( type TripleDHDerivationResult<D> = (
GenericArray<u8, <D as FixedOutput>::OutputSize>, GenericArray<u8, <D as FixedOutput>::OutputSize>,
GenericArray<u8, <D as FixedOutput>::OutputSize>, GenericArray<u8, <D as FixedOutput>::OutputSize>,
GenericArray<u8, <D as FixedOutput>::OutputSize>, GenericArray<u8, <D as FixedOutput>::OutputSize>,
GenericArray<u8, <D as FixedOutput>::OutputSize>,
); );
/// The third key exchange message /// The third key exchange message
@@ -492,7 +442,7 @@ fn derive_3dh_keys<D: Hash, G: Group>(
)?; )?;
let session_key = derive_secrets::<D>( let session_key = derive_secrets::<D>(
&extracted_ikm, &extracted_ikm,
STR_SESSION_SECRET, STR_SESSION_KEY,
hashed_derivation_transcript, hashed_derivation_transcript,
)?; )?;
@@ -502,12 +452,6 @@ fn derive_3dh_keys<D: Hash, G: Group>(
b"", b"",
<D as Digest>::OutputSize::to_usize(), <D as Digest>::OutputSize::to_usize(),
)?; )?;
let ke2 = hkdf_expand_label::<D>(
&handshake_secret,
STR_HANDSHAKE_ENC,
b"",
<D as Digest>::OutputSize::to_usize(),
)?;
let km3 = hkdf_expand_label::<D>( let km3 = hkdf_expand_label::<D>(
&handshake_secret, &handshake_secret,
STR_CLIENT_MAC, STR_CLIENT_MAC,
@@ -518,7 +462,6 @@ fn derive_3dh_keys<D: Hash, G: Group>(
Ok(( Ok((
GenericArray::clone_from_slice(&session_key), GenericArray::clone_from_slice(&session_key),
GenericArray::clone_from_slice(&km2), GenericArray::clone_from_slice(&km2),
GenericArray::clone_from_slice(&ke2),
GenericArray::clone_from_slice(&km3), GenericArray::clone_from_slice(&km3),
)) ))
} }
+64 -56
View File
@@ -19,7 +19,7 @@
//! //!
//! We will use the following choices in this example: //! We will use the following choices in this example:
//! ``` //! ```
//! use opaque_ke::ciphersuite::CipherSuite; //! use opaque_ke::CipherSuite;
//! struct Default; //! struct Default;
//! impl CipherSuite for Default { //! impl CipherSuite for Default {
//! type Group = curve25519_dalek::ristretto::RistrettoPoint; //! type Group = curve25519_dalek::ristretto::RistrettoPoint;
@@ -39,7 +39,7 @@
//! To set up the protocol, the server begins by creating a `ServerSetup` object: //! To set up the protocol, the server begins by creating a `ServerSetup` object:
//! ``` //! ```
//! # use opaque_ke::errors::ProtocolError; //! # use opaque_ke::errors::ProtocolError;
//! # use opaque_ke::ciphersuite::CipherSuite; //! # use opaque_ke::CipherSuite;
//! # use opaque_ke::ServerSetup; //! # use opaque_ke::ServerSetup;
//! # struct Default; //! # struct Default;
//! # impl CipherSuite for Default { //! # impl CipherSuite for Default {
@@ -72,7 +72,7 @@
//! # ServerRegistration, //! # ServerRegistration,
//! # slow_hash::NoOpHash, //! # slow_hash::NoOpHash,
//! # }; //! # };
//! # use opaque_ke::ciphersuite::CipherSuite; //! # use opaque_ke::CipherSuite;
//! # struct Default; //! # struct Default;
//! # impl CipherSuite for Default { //! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
@@ -102,7 +102,7 @@
//! # ServerSetup, //! # ServerSetup,
//! # slow_hash::NoOpHash, //! # slow_hash::NoOpHash,
//! # }; //! # };
//! # use opaque_ke::ciphersuite::CipherSuite; //! # use opaque_ke::CipherSuite;
//! # struct Default; //! # struct Default;
//! # impl CipherSuite for Default { //! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
@@ -139,7 +139,7 @@
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ServerSetup, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ServerSetup,
//! # slow_hash::NoOpHash, //! # slow_hash::NoOpHash,
//! # }; //! # };
//! # use opaque_ke::ciphersuite::CipherSuite; //! # use opaque_ke::CipherSuite;
//! # struct Default; //! # struct Default;
//! # impl CipherSuite for Default { //! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
@@ -177,7 +177,7 @@
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ServerSetup, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ServerSetup,
//! # slow_hash::NoOpHash, //! # slow_hash::NoOpHash,
//! # }; //! # };
//! # use opaque_ke::ciphersuite::CipherSuite; //! # use opaque_ke::CipherSuite;
//! # struct Default; //! # struct Default;
//! # impl CipherSuite for Default { //! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
@@ -204,7 +204,8 @@
//! ## Login //! ## Login
//! The login protocol between a client and server also consists of four steps along with three messages: //! The login protocol between a client and server also consists of four steps along with three messages:
//! [CredentialRequest], [CredentialResponse], [CredentialFinalization]. The server is expected to have access to the password file //! [CredentialRequest], [CredentialResponse], [CredentialFinalization]. The server is expected to have access to the password file
//! corresponding to an output of the registration phase. The login protocol will execute successfully only if the same password //! corresponding to an output of the registration phase (see [Dummy Server Login](#dummy-server-login) for handling the scenario where
//! no password file is available). The login protocol will execute successfully only if the same password
//! was used in the registration phase that produced the password file that the server is testing against. //! was used in the registration phase that produced the password file that the server is testing against.
//! //!
//! ### Client Login Start //! ### Client Login Start
@@ -218,7 +219,7 @@
//! # ClientRegistration, ServerRegistration, ServerLogin, CredentialFinalization, //! # ClientRegistration, ServerRegistration, ServerLogin, CredentialFinalization,
//! # slow_hash::NoOpHash, //! # slow_hash::NoOpHash,
//! # }; //! # };
//! # use opaque_ke::ciphersuite::CipherSuite; //! # use opaque_ke::CipherSuite;
//! # struct Default; //! # struct Default;
//! # impl CipherSuite for Default { //! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
@@ -227,12 +228,11 @@
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash; //! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
//! # } //! # }
//! # use rand::{rngs::OsRng, RngCore}; //! # use rand::{rngs::OsRng, RngCore};
//! use opaque_ke::{ClientLogin, ClientLoginStartParameters}; //! use opaque_ke::ClientLogin;
//! let mut client_rng = OsRng; //! let mut client_rng = OsRng;
//! let client_login_start_result = ClientLogin::<Default>::start( //! let client_login_start_result = ClientLogin::<Default>::start(
//! &mut client_rng, //! &mut client_rng,
//! b"password", //! b"password",
//! ClientLoginStartParameters::default(),
//! )?; //! )?;
//! # Ok::<(), ProtocolError>(()) //! # Ok::<(), ProtocolError>(())
//! ``` //! ```
@@ -249,10 +249,10 @@
//! ``` //! ```
//! # use opaque_ke::{ //! # use opaque_ke::{
//! # errors::ProtocolError, //! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, CredentialFinalization, ServerSetup, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, CredentialFinalization, ServerSetup,
//! # slow_hash::NoOpHash, //! # slow_hash::NoOpHash,
//! # }; //! # };
//! # use opaque_ke::ciphersuite::CipherSuite; //! # use opaque_ke::CipherSuite;
//! # struct Default; //! # struct Default;
//! # impl CipherSuite for Default { //! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
@@ -274,7 +274,6 @@
//! # let client_login_start_result = ClientLogin::<Default>::start( //! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng, //! # &mut client_rng,
//! # b"password", //! # b"password",
//! # ClientLoginStartParameters::default(),
//! # )?; //! # )?;
//! use opaque_ke::{ServerLogin, ServerLoginStartParameters}; //! use opaque_ke::{ServerLogin, ServerLoginStartParameters};
//! let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes[..])?; //! let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes[..])?;
@@ -303,10 +302,10 @@
//! ``` //! ```
//! # use opaque_ke::{ //! # use opaque_ke::{
//! # errors::ProtocolError, //! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup,
//! # slow_hash::NoOpHash, //! # slow_hash::NoOpHash,
//! # }; //! # };
//! # use opaque_ke::ciphersuite::CipherSuite; //! # use opaque_ke::CipherSuite;
//! # struct Default; //! # struct Default;
//! # impl CipherSuite for Default { //! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
@@ -328,7 +327,6 @@
//! # let client_login_start_result = ClientLogin::<Default>::start( //! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng, //! # &mut client_rng,
//! # b"password", //! # b"password",
//! # ClientLoginStartParameters::default(),
//! # )?; //! # )?;
//! # let password_file = //! # let password_file =
//! # ServerRegistration::<Default>::deserialize( //! # ServerRegistration::<Default>::deserialize(
@@ -349,10 +347,10 @@
//! ``` //! ```
//! # use opaque_ke::{ //! # use opaque_ke::{
//! # errors::ProtocolError, //! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup,
//! # slow_hash::NoOpHash, //! # slow_hash::NoOpHash,
//! # }; //! # };
//! # use opaque_ke::ciphersuite::CipherSuite; //! # use opaque_ke::CipherSuite;
//! # struct Default; //! # struct Default;
//! # impl CipherSuite for Default { //! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
@@ -374,7 +372,6 @@
//! # let client_login_start_result = ClientLogin::<Default>::start( //! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng, //! # &mut client_rng,
//! # b"password", //! # b"password",
//! # ClientLoginStartParameters::default(),
//! # )?; //! # )?;
//! # let password_file = //! # let password_file =
//! # ServerRegistration::<Default>::deserialize( //! # ServerRegistration::<Default>::deserialize(
@@ -424,10 +421,10 @@
//! ``` //! ```
//! # use opaque_ke::{ //! # use opaque_ke::{
//! # errors::ProtocolError, //! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup,
//! # slow_hash::NoOpHash, //! # slow_hash::NoOpHash,
//! # }; //! # };
//! # use opaque_ke::ciphersuite::CipherSuite; //! # use opaque_ke::CipherSuite;
//! # struct Default; //! # struct Default;
//! # impl CipherSuite for Default { //! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
@@ -453,7 +450,6 @@
//! # let client_login_start_result = ClientLogin::<Default>::start( //! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng, //! # &mut client_rng,
//! # b"password", //! # b"password",
//! # ClientLoginStartParameters::default(),
//! # )?; //! # )?;
//! # let password_file = //! # let password_file =
//! # ServerRegistration::<Default>::deserialize( //! # ServerRegistration::<Default>::deserialize(
@@ -500,10 +496,10 @@
//! ``` //! ```
//! # use opaque_ke::{ //! # use opaque_ke::{
//! # errors::ProtocolError, //! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup,
//! # slow_hash::NoOpHash, //! # slow_hash::NoOpHash,
//! # }; //! # };
//! # use opaque_ke::ciphersuite::CipherSuite; //! # use opaque_ke::CipherSuite;
//! # struct Default; //! # struct Default;
//! # impl CipherSuite for Default { //! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
@@ -530,7 +526,6 @@
//! # let client_login_start_result = ClientLogin::<Default>::start( //! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng, //! # &mut client_rng,
//! # b"password", //! # b"password",
//! # ClientLoginStartParameters::default(),
//! # )?; //! # )?;
//! # let password_file = //! # let password_file =
//! # ServerRegistration::<Default>::deserialize( //! # ServerRegistration::<Default>::deserialize(
@@ -566,10 +561,10 @@
//! ``` //! ```
//! # use opaque_ke::{ //! # use opaque_ke::{
//! # errors::ProtocolError, //! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ServerSetup, //! # ClientRegistration, ClientRegistrationFinishParameters, Identifiers, ServerRegistration, ServerSetup,
//! # slow_hash::NoOpHash, //! # slow_hash::NoOpHash,
//! # }; //! # };
//! # use opaque_ke::ciphersuite::CipherSuite; //! # use opaque_ke::CipherSuite;
//! # struct Default; //! # struct Default;
//! # impl CipherSuite for Default { //! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
@@ -590,8 +585,10 @@
//! &mut client_rng, //! &mut client_rng,
//! server_registration_start_result.message, //! server_registration_start_result.message,
//! ClientRegistrationFinishParameters::WithIdentifiers( //! ClientRegistrationFinishParameters::WithIdentifiers(
//! b"Alice_the_Cryptographer".to_vec(), //! Identifiers::ClientAndServerIdentifiers(
//! b"Facebook".to_vec(), //! b"Alice_the_Cryptographer".to_vec(),
//! b"Facebook".to_vec(),
//! ),
//! ), //! ),
//! )?; //! )?;
//! # Ok::<(), ProtocolError>(()) //! # Ok::<(), ProtocolError>(())
@@ -601,10 +598,10 @@
//! ``` //! ```
//! # use opaque_ke::{ //! # use opaque_ke::{
//! # errors::ProtocolError, //! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, CredentialFinalization, ServerSetup, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, CredentialFinalization, Identifiers, ServerSetup,
//! # slow_hash::NoOpHash, //! # slow_hash::NoOpHash,
//! # }; //! # };
//! # use opaque_ke::ciphersuite::CipherSuite; //! # use opaque_ke::CipherSuite;
//! # struct Default; //! # struct Default;
//! # impl CipherSuite for Default { //! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
@@ -621,12 +618,11 @@
//! # let mut server_rng = OsRng; //! # let mut server_rng = OsRng;
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng); //! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?; //! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::WithIdentifiers(b"Alice_the_Cryptographer".to_vec(), b"Facebook".to_vec()))?; //! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::WithIdentifiers(Identifiers::ClientAndServerIdentifiers(b"Alice_the_Cryptographer".to_vec(), b"Facebook".to_vec())))?;
//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize(); //! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
//! # let client_login_start_result = ClientLogin::<Default>::start( //! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng, //! # &mut client_rng,
//! # b"password", //! # b"password",
//! # ClientLoginStartParameters::default(),
//! # )?; //! # )?;
//! # use opaque_ke::{ServerLogin, ServerLoginStartParameters}; //! # use opaque_ke::{ServerLogin, ServerLoginStartParameters};
//! # let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes[..])?; //! # let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes[..])?;
@@ -638,8 +634,10 @@
//! client_login_start_result.message, //! client_login_start_result.message,
//! b"[email protected]", //! b"[email protected]",
//! ServerLoginStartParameters::WithIdentifiers( //! ServerLoginStartParameters::WithIdentifiers(
//! b"Alice_the_Cryptographer".to_vec(), //! Identifiers::ClientAndServerIdentifiers(
//! b"Facebook".to_vec(), //! b"Alice_the_Cryptographer".to_vec(),
//! b"Facebook".to_vec(),
//! ),
//! ), //! ),
//! )?; //! )?;
//! # Ok::<(), ProtocolError>(()) //! # Ok::<(), ProtocolError>(())
@@ -649,10 +647,10 @@
//! ``` //! ```
//! # use opaque_ke::{ //! # use opaque_ke::{
//! # errors::ProtocolError, //! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup, //! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, Identifiers, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup,
//! # slow_hash::NoOpHash, //! # slow_hash::NoOpHash,
//! # }; //! # };
//! # use opaque_ke::ciphersuite::CipherSuite; //! # use opaque_ke::CipherSuite;
//! # struct Default; //! # struct Default;
//! # impl CipherSuite for Default { //! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint; //! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
@@ -669,45 +667,54 @@
//! # let mut server_rng = OsRng; //! # let mut server_rng = OsRng;
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng); //! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?; //! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::WithIdentifiers(b"Alice_the_Cryptographer".to_vec(), b"Facebook".to_vec()))?; //! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::WithIdentifiers(Identifiers::ClientAndServerIdentifiers(b"Alice_the_Cryptographer".to_vec(), b"Facebook".to_vec())))?;
//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize(); //! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
//! # let client_login_start_result = ClientLogin::<Default>::start( //! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng, //! # &mut client_rng,
//! # b"password", //! # b"password",
//! # ClientLoginStartParameters::default(),
//! # )?; //! # )?;
//! # let password_file = //! # let password_file =
//! # ServerRegistration::<Default>::deserialize( //! # ServerRegistration::<Default>::deserialize(
//! # &password_file_bytes[..], //! # &password_file_bytes[..],
//! # )?; //! # )?;
//! # let server_login_start_result = //! # let server_login_start_result =
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginStartParameters::WithIdentifiers(b"Alice_the_Cryptographer".to_vec(), b"Facebook".to_vec()))?; //! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginStartParameters::WithIdentifiers(Identifiers::ClientAndServerIdentifiers(b"Alice_the_Cryptographer".to_vec(), b"Facebook".to_vec())))?;
//! let client_login_finish_result = client_login_start_result.state.finish( //! let client_login_finish_result = client_login_start_result.state.finish(
//! server_login_start_result.message, //! server_login_start_result.message,
//! ClientLoginFinishParameters::WithIdentifiers( //! ClientLoginFinishParameters::WithIdentifiers(
//! b"Alice_the_Cryptographer".to_vec(), //! Identifiers::ClientAndServerIdentifiers(
//! b"Facebook".to_vec(), //! b"Alice_the_Cryptographer".to_vec(),
//! b"Facebook".to_vec(),
//! ),
//! ), //! ),
//! )?; //! )?;
//!
//! # Ok::<(), ProtocolError>(()) //! # Ok::<(), ProtocolError>(())
//! ``` //! ```
//! Failing to supply the same pair of custom identifiers in any of the three steps above will result in an error in attempting to complete //! Failing to supply the same pair of custom identifiers in any of the three steps above will result in an error in attempting to complete
//! the protocol! //! the protocol!
//! //!
//! ## Key Exchange Additional Data //! Note that if only one of the client and server identifiers are present, then [Identifiers::ClientIdentifier] and [Identifiers::ServerIdentifier] can be
//! used to specify them individually.
//! //!
//! A key exchange protocol typically supports the passing of data between the two parties before the exchange is complete, so as to bind the integrity //! ## Key Exchange Context
//! and/or confidentiality of application-specific data to the security of the key exchange. During the login phase, the client and server can pass
//! additional data alongside the first two messages of the protocol, with confidential data being supported for the second message.
//! //!
//! The following three messages support passing of additional data: //! A key exchange protocol typically allows for the specifying of shared "context" information between the two parties before the exchange is complete,
//! - The first login message, where the client can populate [ClientLoginStartParameters::WithInfo] with plaintext additional data, and //! so as to bind the integrity of application-specific data or configuration parameters to the security of the key exchange.
//! the server can retrieve using the `plain_info` field of [ServerLoginStartResult]. //! During the login phase, the client and server can specify this context using:
//! - The second login message, where the server can populate [ServerLoginStartParameters::WithInfo] with confidential additional data, //! - The second login message, where the server can populate [ServerLoginStartParameters::WithContext], and
//! and the client can retrieve using the `confidential_info` field of [ClientLoginFinishResult]. //! - The third login message, where the client can populate [ClientLoginFinishParameters::WithContext].
//! //!
//! For the second login message, the `WithInfoAndIdentifiers` variant can be used to specify these fields in addition to //! For both of these messages, the `WithContextAndIdentifiers` variant can be used to specify these fields in addition to
//! [custom identifiers](#custom-identifiers), with the ordering of the fields as `WithInfoAndIdentifiers(confidential_info, username, server_name)`. //! [custom identifiers](#custom-identifiers), with the ordering of the fields as
//! `WithContextAndIdentifiers(context, Identifiers::ClientAndServerIdentifiers(username, server_name))`.
//!
//! ## Dummy Server Login
//!
//! For applications in which the server does not wish to reveal to the client whether an existing password file has been
//! registered, the server can return a "dummy" credential response message to the client for an unregistered client,
//! which is indistinguishable from the normal credential response message that the server would return for a registered client.
//! The dummy message is created by passing a `None` to the password_file parameter for [ServerLogin::start].
//! //!
//! # Features //! # Features
//! //!
@@ -773,6 +780,8 @@ mod tests;
pub use rand; pub use rand;
pub use ciphersuite::CipherSuite;
pub use crate::messages::{ pub use crate::messages::{
CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest, CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest,
RegistrationResponse, RegistrationUpload, RegistrationResponse, RegistrationUpload,
@@ -781,11 +790,10 @@ pub use crate::opaque::{
ClientLogin, ClientRegistration, ServerLogin, ServerRegistration, ServerSetup, ClientLogin, ClientRegistration, ServerLogin, ServerRegistration, ServerSetup,
}; };
pub use crate::opaque::{ pub use crate::opaque::{
ClientLoginFinishParameters, ClientLoginStartParameters, ClientRegistrationFinishParameters, ClientLoginFinishParameters, ClientRegistrationFinishParameters, ServerLoginStartParameters,
ServerLoginStartParameters,
}; };
pub use crate::opaque::{ pub use crate::opaque::{
ClientLoginFinishResult, ClientLoginStartResult, ClientRegistrationFinishResult, ClientLoginFinishResult, ClientLoginStartResult, ClientRegistrationFinishResult,
ClientRegistrationStartResult, ServerLoginFinishResult, ServerLoginStartResult, ClientRegistrationStartResult, Identifiers, ServerLoginFinishResult, ServerLoginStartResult,
ServerRegistrationStartResult, ServerRegistrationStartResult,
}; };
+7 -3
View File
@@ -25,7 +25,8 @@ pub trait GroupWithMapToCurve: Group {
fn map_to_curve<H: Hash>(msg: &[u8], dst: &[u8]) -> Result<Self, InternalPakeError>; fn map_to_curve<H: Hash>(msg: &[u8], dst: &[u8]) -> Result<Self, InternalPakeError>;
/// Hashes a slice of pseudo-random bytes to a scalar /// Hashes a slice of pseudo-random bytes to a scalar
fn hash_to_scalar<H: Hash>(input: &[u8]) -> Result<Self::Scalar, InternalPakeError>; fn hash_to_scalar<H: Hash>(input: &[u8], dst: &[u8])
-> Result<Self::Scalar, InternalPakeError>;
/// Generates the contextString parameter as defined in /// Generates the contextString parameter as defined in
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-05.txt> /// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-05.txt>
@@ -47,9 +48,12 @@ impl GroupWithMapToCurve for RistrettoPoint {
)) ))
} }
fn hash_to_scalar<H: Hash>(input: &[u8]) -> Result<Self::Scalar, InternalPakeError> { fn hash_to_scalar<H: Hash>(
input: &[u8],
dst: &[u8],
) -> Result<Self::Scalar, InternalPakeError> {
const LEN_IN_BYTES: usize = 64; const LEN_IN_BYTES: usize = 64;
let uniform_bytes = expand_message_xmd::<H>(input, b"", LEN_IN_BYTES)?; let uniform_bytes = expand_message_xmd::<H>(input, dst, LEN_IN_BYTES)?;
let mut bits = [0u8; LEN_IN_BYTES]; let mut bits = [0u8; LEN_IN_BYTES];
bits.copy_from_slice(&uniform_bytes[..]); bits.copy_from_slice(&uniform_bytes[..]);
+13 -14
View File
@@ -10,11 +10,12 @@ use crate::{
envelope::Envelope, envelope::Envelope,
errors::{ errors::{
utils::{check_slice_size, check_slice_size_atleast}, utils::{check_slice_size, check_slice_size_atleast},
ProtocolError, PakeError, PakeError, ProtocolError,
}, },
group::Group, group::Group,
key_exchange::traits::{FromBytes, KeyExchange, ToBytes}, key_exchange::traits::{FromBytes, KeyExchange, ToBytes},
keypair::{KeyPair, PublicKey, SizedBytesExt}, keypair::{KeyPair, PublicKey, SizedBytesExt},
opaque::ServerSetup,
}; };
use digest::Digest; use digest::Digest;
use generic_array::{typenum::Unsigned, GenericArray}; use generic_array::{typenum::Unsigned, GenericArray};
@@ -128,7 +129,7 @@ impl_serialize_and_deserialize_for!(RegistrationResponse);
pub struct RegistrationUpload<CS: CipherSuite> { pub struct RegistrationUpload<CS: CipherSuite> {
/// The "envelope" generated by the user, containing sealed /// The "envelope" generated by the user, containing sealed
/// cryptographic identifiers /// cryptographic identifiers
pub(crate) envelope: Envelope<CS::Hash>, pub(crate) envelope: Envelope<CS>,
/// The masking key used to mask the envelope /// The masking key used to mask the envelope
pub(crate) masking_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>, pub(crate) masking_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
/// The user's public key /// The user's public key
@@ -161,12 +162,9 @@ impl<CS: CipherSuite> RegistrationUpload<CS> {
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> { pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let key_len = <PublicKey as SizedBytes>::Len::to_usize(); let key_len = <PublicKey as SizedBytes>::Len::to_usize();
let hash_len = <CS::Hash as Digest>::OutputSize::to_usize(); let hash_len = <CS::Hash as Digest>::OutputSize::to_usize();
let checked_slice = check_slice_size( let checked_slice =
&input, check_slice_size_atleast(input, key_len + hash_len, "registration_upload_bytes")?;
key_len + hash_len + Envelope::<CS::Hash>::len(), let envelope = Envelope::<CS>::deserialize(&checked_slice[key_len + hash_len..])?;
"registration_upload_bytes",
)?;
let envelope = Envelope::<CS::Hash>::deserialize(&checked_slice[key_len + hash_len..])?;
Ok(Self { Ok(Self {
envelope, envelope,
masking_key: GenericArray::clone_from_slice( masking_key: GenericArray::clone_from_slice(
@@ -179,16 +177,17 @@ impl<CS: CipherSuite> RegistrationUpload<CS> {
} }
// Creates a dummy instance used for faking a [CredentialResponse] // Creates a dummy instance used for faking a [CredentialResponse]
pub(crate) fn dummy<R: RngCore + CryptoRng>(rng: &mut R) -> Self { pub(crate) fn dummy<R: RngCore + CryptoRng>(
rng: &mut R,
server_setup: &ServerSetup<CS>,
) -> Self {
let mut masking_key = vec![0u8; <CS::Hash as Digest>::OutputSize::to_usize()]; let mut masking_key = vec![0u8; <CS::Hash as Digest>::OutputSize::to_usize()];
rng.fill_bytes(&mut masking_key); rng.fill_bytes(&mut masking_key);
let keypair = KeyPair::<CS::Group>::generate_random(rng);
Self { Self {
envelope: Envelope::<CS::Hash>::dummy(), envelope: Envelope::<CS>::dummy(),
masking_key: GenericArray::clone_from_slice(&masking_key), masking_key: GenericArray::clone_from_slice(&masking_key),
client_s_pk: keypair.public().clone(), client_s_pk: server_setup.fake_keypair.public().clone(),
} }
} }
} }
@@ -290,7 +289,7 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
let elem_len = <CS::Group as Group>::ElemLen::to_usize(); let elem_len = <CS::Group as Group>::ElemLen::to_usize();
let key_len = <PublicKey as SizedBytes>::Len::to_usize(); let key_len = <PublicKey as SizedBytes>::Len::to_usize();
let nonce_len: usize = 32; let nonce_len: usize = 32;
let envelope_len = Envelope::<CS::Hash>::len(); let envelope_len = Envelope::<CS>::len();
let masked_response_len = key_len + envelope_len; let masked_response_len = key_len + envelope_len;
let ke2_message_len = CS::KeyExchange::ke2_message_size(); let ke2_message_len = CS::KeyExchange::ke2_message_size();
+114 -139
View File
@@ -7,12 +7,12 @@
use crate::{ use crate::{
ciphersuite::CipherSuite, ciphersuite::CipherSuite,
envelope::{mode_from_ids, Envelope}, envelope::Envelope,
errors::{utils::check_slice_size, InternalPakeError, PakeError, ProtocolError}, errors::{utils::check_slice_size, InternalPakeError, PakeError, ProtocolError},
group::Group, group::Group,
hash::Hash, hash::Hash,
key_exchange::traits::{FromBytes, KeyExchange, ToBytesWithPointers}, key_exchange::traits::{FromBytes, KeyExchange, ToBytesWithPointers},
keypair::{KeyPair, PrivateKey, PublicKey, SizedBytesExt}, keypair::{KeyPair, PrivateKey, PublicKey},
map_to_curve::GroupWithMapToCurve, map_to_curve::GroupWithMapToCurve,
oprf, oprf,
serialization::{serialize, tokenize}, serialization::{serialize, tokenize},
@@ -39,6 +39,7 @@ const STR_OPRF_KEY: &[u8] = b"OprfKey";
pub struct ServerSetup<CS: CipherSuite> { pub struct ServerSetup<CS: CipherSuite> {
oprf_seed: GenericArray<u8, <CS::Hash as Digest>::OutputSize>, oprf_seed: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
keypair: KeyPair<CS::Group>, keypair: KeyPair<CS::Group>,
pub(crate) fake_keypair: KeyPair<CS::Group>,
} }
impl<CS: CipherSuite> ServerSetup<CS> { impl<CS: CipherSuite> ServerSetup<CS> {
@@ -50,6 +51,7 @@ impl<CS: CipherSuite> ServerSetup<CS> {
Self { Self {
oprf_seed: GenericArray::clone_from_slice(&seed[..]), oprf_seed: GenericArray::clone_from_slice(&seed[..]),
keypair: KeyPair::<CS::Group>::generate_random(rng), keypair: KeyPair::<CS::Group>::generate_random(rng),
fake_keypair: KeyPair::<CS::Group>::generate_random(rng),
} }
} }
@@ -58,6 +60,7 @@ impl<CS: CipherSuite> ServerSetup<CS> {
[ [
self.oprf_seed.to_vec(), self.oprf_seed.to_vec(),
self.keypair.private().to_arr().to_vec(), self.keypair.private().to_arr().to_vec(),
self.fake_keypair.private().to_arr().to_vec(),
] ]
.concat() .concat()
} }
@@ -65,15 +68,13 @@ impl<CS: CipherSuite> ServerSetup<CS> {
/// Deserialization from bytes /// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> { pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let seed_len = <CS::Hash as Digest>::OutputSize::to_usize(); let seed_len = <CS::Hash as Digest>::OutputSize::to_usize();
let checked_slice = check_slice_size( let key_len = <PrivateKey as SizedBytes>::Len::to_usize();
input, let checked_slice = check_slice_size(input, seed_len + key_len + key_len, "server_setup")?;
seed_len + <PrivateKey as SizedBytes>::Len::to_usize(),
"server_setup",
)?;
Ok(Self { Ok(Self {
oprf_seed: GenericArray::clone_from_slice(&checked_slice[..seed_len]), oprf_seed: GenericArray::clone_from_slice(&checked_slice[..seed_len]),
keypair: KeyPair::from_private_key_slice(&checked_slice[seed_len..])?, keypair: KeyPair::from_private_key_slice(&checked_slice[seed_len..seed_len + key_len])?,
fake_keypair: KeyPair::from_private_key_slice(&checked_slice[seed_len + key_len..])?,
}) })
} }
@@ -149,12 +150,40 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
impl_serialize_and_deserialize_for!(ClientRegistration); 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],
) -> (Vec<u8>, Vec<u8>) {
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()),
};
(
serialize(&client_identity, 2),
serialize(&server_identity, 2),
)
}
/// Optional parameters for client registration finish /// Optional parameters for client registration finish
#[derive(Clone)] #[derive(Clone)]
pub enum ClientRegistrationFinishParameters { pub enum ClientRegistrationFinishParameters {
/// Specifying the identifiers idU and idS (corresponding to custom identifier mode) /// Specifying the identifiers idU and idS
WithIdentifiers(Vec<u8>, Vec<u8>), WithIdentifiers(Identifiers),
/// No identifiers specified (corresponding to base mode) /// No identifiers or private key specified
Default, Default,
} }
@@ -230,10 +259,9 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
params: ClientRegistrationFinishParameters, params: ClientRegistrationFinishParameters,
) -> Result<ClientRegistrationFinishResult<CS>, ProtocolError> { ) -> Result<ClientRegistrationFinishResult<CS>, ProtocolError> {
let optional_ids = match params { let optional_ids = match params {
ClientRegistrationFinishParameters::WithIdentifiers(id_u, id_s) => Some((id_u, id_s)), ClientRegistrationFinishParameters::WithIdentifiers(ids) => Some(ids),
ClientRegistrationFinishParameters::Default => None, ClientRegistrationFinishParameters::Default => None,
}; };
let client_static_keypair = KeyPair::<CS::Group>::generate_random(rng);
let password_derived_key = let password_derived_key =
get_password_derived_key::<CS::Group, CS::SlowHash, CS::Hash>(&self.token, r2.beta)?; get_password_derived_key::<CS::Group, CS::SlowHash, CS::Hash>(&self.token, r2.beta)?;
@@ -243,19 +271,14 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
h.expand(STR_MASKING_KEY, &mut masking_key) h.expand(STR_MASKING_KEY, &mut masking_key)
.map_err(|_| InternalPakeError::HkdfError)?; .map_err(|_| InternalPakeError::HkdfError)?;
let (envelope, export_key) = Envelope::<CS::Hash>::seal( let (envelope, client_s_pk, export_key) =
rng, Envelope::<CS>::seal(rng, &password_derived_key, &r2.server_s_pk, optional_ids)?;
&password_derived_key,
&client_static_keypair.private().to_arr().to_vec(),
&r2.server_s_pk,
optional_ids,
)?;
Ok(ClientRegistrationFinishResult { Ok(ClientRegistrationFinishResult {
message: RegistrationUpload { message: RegistrationUpload {
envelope, envelope,
masking_key: GenericArray::clone_from_slice(&masking_key[..]), masking_key: GenericArray::clone_from_slice(&masking_key[..]),
client_s_pk: client_static_keypair.public().clone(), client_s_pk,
}, },
export_key, export_key,
#[cfg(test)] #[cfg(test)]
@@ -340,8 +363,11 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
} }
// Creates a dummy instance used for faking a [CredentialResponse] // Creates a dummy instance used for faking a [CredentialResponse]
pub(crate) fn dummy<R: RngCore + CryptoRng>(rng: &mut R) -> Self { pub(crate) fn dummy<R: RngCore + CryptoRng>(
Self(RegistrationUpload::dummy(rng)) rng: &mut R,
server_setup: &ServerSetup<CS>,
) -> Self {
Self(RegistrationUpload::dummy(rng, server_setup))
} }
} }
@@ -428,21 +454,6 @@ impl<CS: CipherSuite> ClientLogin<CS> {
} }
} }
impl_serialize_and_deserialize_for!(ClientLogin);
/// Optional parameters for client login start
#[derive(Clone)]
pub enum ClientLoginStartParameters {
/// Specifying a plaintext info field that will be sent to the server
WithInfo(Vec<u8>),
}
impl Default for ClientLoginStartParameters {
fn default() -> Self {
Self::WithInfo(Vec::new())
}
}
/// Contains the fields that are returned by a client login start /// Contains the fields that are returned by a client login start
pub struct ClientLoginStartResult<CS: CipherSuite> { pub struct ClientLoginStartResult<CS: CipherSuite> {
/// The message to send to the server to begin the login protocol /// The message to send to the server to begin the login protocol
@@ -464,9 +475,14 @@ impl<CS: CipherSuite> Clone for ClientLoginStartResult<CS> {
/// Optional parameters for client login finish /// Optional parameters for client login finish
#[derive(Clone)] #[derive(Clone)]
pub enum ClientLoginFinishParameters { pub enum ClientLoginFinishParameters {
/// Specifying a user identifier and server identifier that will be matched against the client /// Specifying a context field that the server must agree on
WithIdentifiers(Vec<u8>, Vec<u8>), WithContext(Vec<u8>),
/// No info and no custom identifiers /// Specifying a user identifier and server identifier that will be matched against the server
WithIdentifiers(Identifiers),
/// Specifying a context field that the server must agree on,
/// along with a user identifier and server identifier and context that will be matched against the server
WithContextAndIdentifiers(Vec<u8>, Identifiers),
/// No custom identifiers and no context
Default, Default,
} }
@@ -486,8 +502,6 @@ pub struct ClientLoginFinishResult<CS: CipherSuite> {
pub export_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>, pub export_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
/// The server's static public key /// The server's static public key
pub server_s_pk: PublicKey, pub server_s_pk: PublicKey,
/// The confidential info sent by the client
pub confidential_info: Vec<u8>,
/// Instance of the ClientLogin, only used in tests for checking zeroize /// Instance of the ClientLogin, only used in tests for checking zeroize
#[cfg(test)] #[cfg(test)]
pub state: ClientLogin<CS>, pub state: ClientLogin<CS>,
@@ -501,7 +515,6 @@ impl<CS: CipherSuite> Clone for ClientLoginFinishResult<CS> {
session_key: self.session_key.clone(), session_key: self.session_key.clone(),
export_key: self.export_key.clone(), export_key: self.export_key.clone(),
server_s_pk: self.server_s_pk.clone(), server_s_pk: self.server_s_pk.clone(),
confidential_info: self.confidential_info.clone(),
#[cfg(test)] #[cfg(test)]
state: self.state.clone(), state: self.state.clone(),
} }
@@ -510,38 +523,13 @@ impl<CS: CipherSuite> Clone for ClientLoginFinishResult<CS> {
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
///
/// # Arguments
/// * `password` - A user password
///
/// # Example
///
/// ```
/// use opaque_ke::{ClientLogin, ClientLoginStartParameters};
/// # use opaque_ke::errors::ProtocolError;
/// use rand::{rngs::OsRng, RngCore};
/// use opaque_ke::ciphersuite::CipherSuite;
/// struct Default;
/// impl CipherSuite for Default {
/// type Group = curve25519_dalek::ristretto::RistrettoPoint;
/// type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH;
/// type Hash = sha2::Sha512;
/// type SlowHash = opaque_ke::slow_hash::NoOpHash;
/// }
/// let mut client_rng = OsRng;
/// let client_login_start_result = ClientLogin::<Default>::start(&mut client_rng, b"hunter2", ClientLoginStartParameters::default())?;
/// # Ok::<(), ProtocolError>(())
/// ```
pub fn start<R: RngCore + CryptoRng>( pub fn start<R: RngCore + CryptoRng>(
rng: &mut R, rng: &mut R,
password: &[u8], password: &[u8],
params: ClientLoginStartParameters,
) -> Result<ClientLoginStartResult<CS>, ProtocolError> { ) -> Result<ClientLoginStartResult<CS>, ProtocolError> {
let ClientLoginStartParameters::WithInfo(info) = params;
let (token, alpha) = oprf::blind::<R, CS::Group, CS::Hash>(password, rng)?; let (token, alpha) = oprf::blind::<R, CS::Group, CS::Hash>(password, rng)?;
let (ke1_state, ke1_message) = CS::KeyExchange::generate_ke1(info, rng)?; let (ke1_state, ke1_message) = CS::KeyExchange::generate_ke1(rng)?;
let credential_request = CredentialRequest { alpha, ke1_message }; let credential_request = CredentialRequest { alpha, ke1_message };
let serialized_credential_request = credential_request.serialize(); let serialized_credential_request = credential_request.serialize();
@@ -563,9 +551,14 @@ impl<CS: CipherSuite> ClientLogin<CS> {
credential_response: CredentialResponse<CS>, credential_response: CredentialResponse<CS>,
params: ClientLoginFinishParameters, params: ClientLoginFinishParameters,
) -> Result<ClientLoginFinishResult<CS>, ProtocolError> { ) -> Result<ClientLoginFinishResult<CS>, ProtocolError> {
let optional_ids = match params { let (context, optional_ids) = match params {
ClientLoginFinishParameters::Default => None, ClientLoginFinishParameters::Default => (vec![], None),
ClientLoginFinishParameters::WithIdentifiers(id_u, id_s) => Some((id_u, id_s)), ClientLoginFinishParameters::WithContext(context) => (context, None),
ClientLoginFinishParameters::WithIdentifiers(ids) => (vec![], Some(ids)),
// add context
ClientLoginFinishParameters::WithContextAndIdentifiers(context, ids) => {
(context, Some(ids))
}
}; };
let password_derived_key = get_password_derived_key::<CS::Group, CS::SlowHash, CS::Hash>( let password_derived_key = get_password_derived_key::<CS::Group, CS::SlowHash, CS::Hash>(
@@ -578,13 +571,16 @@ impl<CS: CipherSuite> ClientLogin<CS> {
h.expand(STR_MASKING_KEY, &mut masking_key) h.expand(STR_MASKING_KEY, &mut masking_key)
.map_err(|_| InternalPakeError::HkdfError)?; .map_err(|_| InternalPakeError::HkdfError)?;
let (server_s_pk, envelope) = unmask_response::<CS::Hash>( let (server_s_pk, envelope) = unmask_response::<CS>(
&masking_key, &masking_key,
&credential_response.masking_nonce, &credential_response.masking_nonce,
&credential_response.masked_response, &credential_response.masked_response,
) )
.map_err(|e| match e { .map_err(|e| match e {
ProtocolError::InvalidInnerEnvelopeError => PakeError::InvalidLoginError.into(), ProtocolError::InvalidInnerEnvelopeError => PakeError::InvalidLoginError.into(),
ProtocolError::VerificationError(PakeError::SerializationError) => {
PakeError::InvalidLoginError.into()
}
err => err, err => err,
})?; })?;
let server_s_pk_bytes = server_s_pk.to_arr().to_vec(); let server_s_pk_bytes = server_s_pk.to_arr().to_vec();
@@ -596,37 +592,25 @@ impl<CS: CipherSuite> ClientLogin<CS> {
err => PakeError::from(err), err => PakeError::from(err),
})?; })?;
let client_s_sk = PrivateKey::from_bytes(&opened_envelope.client_s_sk)?;
let (id_u, id_s) = match optional_ids {
None => (
KeyPair::<CS::Group>::public_from_private(&client_s_sk)
.to_arr()
.to_vec(),
server_s_pk_bytes,
),
Some((id_u, id_s)) => (id_u, id_s),
};
let credential_response_component = CredentialResponse::<CS>::serialize_without_ke( let credential_response_component = CredentialResponse::<CS>::serialize_without_ke(
&credential_response.beta, &credential_response.beta,
&credential_response.masking_nonce, &credential_response.masking_nonce,
&credential_response.masked_response, &credential_response.masked_response,
); );
let (confidential_info, session_key, ke3_message) = CS::KeyExchange::generate_ke3( let (session_key, ke3_message) = CS::KeyExchange::generate_ke3(
credential_response_component, credential_response_component,
credential_response.ke2_message, credential_response.ke2_message,
&self.ke1_state, &self.ke1_state,
&self.serialized_credential_request, &self.serialized_credential_request,
server_s_pk.clone(), server_s_pk.clone(),
client_s_sk, opened_envelope.client_static_keypair.private().clone(),
id_u, opened_envelope.id_u.clone(),
id_s, opened_envelope.id_s.clone(),
context,
)?; )?;
Ok(ClientLoginFinishResult { Ok(ClientLoginFinishResult {
confidential_info,
message: CredentialFinalization { ke3_message }, message: CredentialFinalization { ke3_message },
session_key, session_key,
export_key: opened_envelope.export_key.clone(), export_key: opened_envelope.export_key.clone(),
@@ -656,19 +640,19 @@ impl<CS: CipherSuite> Clone for ServerLogin<CS> {
/// Optional parameters for server login start /// Optional parameters for server login start
#[derive(Clone)] #[derive(Clone)]
pub enum ServerLoginStartParameters { pub enum ServerLoginStartParameters {
/// Specifying a confidential info field that will be sent to the client /// Specifying a context field that the client must agree on
WithInfo(Vec<u8>), WithContext(Vec<u8>),
/// Specifying a user identifier and server identifier that will be matched against the client /// Specifying a user identifier and server identifier that will be matched against the client
WithIdentifiers(Vec<u8>, Vec<u8>), WithIdentifiers(Identifiers),
/// Specifying a confidential info field that will be sent to the client, /// 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 /// along with a user identifier and and server identifier that will be matched against the client
/// (in that order) /// (in that order)
WithInfoAndIdentifiers(Vec<u8>, Vec<u8>, Vec<u8>), WithContextAndIdentifiers(Vec<u8>, Identifiers),
} }
impl Default for ServerLoginStartParameters { impl Default for ServerLoginStartParameters {
fn default() -> Self { fn default() -> Self {
Self::WithInfo(Vec::new()) Self::WithContext(Vec::new())
} }
} }
@@ -678,8 +662,6 @@ pub struct ServerLoginStartResult<CS: CipherSuite> {
pub message: CredentialResponse<CS>, pub message: CredentialResponse<CS>,
/// The state that the server must keep in order to finish the protocl /// The state that the server must keep in order to finish the protocl
pub state: ServerLogin<CS>, pub state: ServerLogin<CS>,
/// The plaintext info sent by the client
pub plain_info: Vec<u8>,
} }
// Cannot be derived because it would require for CS to be Clone. // Cannot be derived because it would require for CS to be Clone.
@@ -688,7 +670,6 @@ impl<CS: CipherSuite> Clone for ServerLoginStartResult<CS> {
Self { Self {
message: self.message.clone(), message: self.message.clone(),
state: self.state.clone(), state: self.state.clone(),
plain_info: self.plain_info.clone(),
} }
} }
} }
@@ -741,32 +722,23 @@ impl<CS: CipherSuite> ServerLogin<CS> {
credential_identifier: &[u8], credential_identifier: &[u8],
params: ServerLoginStartParameters, params: ServerLoginStartParameters,
) -> Result<ServerLoginStartResult<CS>, ProtocolError> { ) -> Result<ServerLoginStartResult<CS>, ProtocolError> {
// FIXME: handle optional password_file case, ensure that there is no timing attack by generating a random pubkey anyway
let record = match password_file { let record = match password_file {
Some(x) => x, Some(x) => x,
None => ServerRegistration::dummy(rng), None => ServerRegistration::dummy(rng, server_setup),
}; };
let client_s_pk = record.0.client_s_pk.clone(); let client_s_pk = record.0.client_s_pk.clone();
let (e_info, optional_ids) = match params { let (context, optional_ids) = match params {
ServerLoginStartParameters::WithInfo(e_info) => (e_info, None), ServerLoginStartParameters::WithContext(context) => (context, None),
ServerLoginStartParameters::WithIdentifiers(id_u, id_s) => { ServerLoginStartParameters::WithIdentifiers(ids) => (Vec::new(), Some(ids)),
(Vec::new(), Some((id_u, id_s))) ServerLoginStartParameters::WithContextAndIdentifiers(context, ids) => {
} (context, Some(ids))
ServerLoginStartParameters::WithInfoAndIdentifiers(e_info, id_u, id_s) => {
(e_info, Some((id_u, id_s)))
} }
}; };
let envelope = record.0.envelope.clone();
if envelope.get_mode() != mode_from_ids(&optional_ids) {
return Err(InternalPakeError::IncompatibleEnvelopeModeError.into());
}
let server_s_sk = server_setup.keypair.private(); let server_s_sk = server_setup.keypair.private();
let server_s_pk = KeyPair::<CS::Group>::public_from_private(&server_s_sk); let server_s_pk = KeyPair::<CS::Group>::public_from_private(server_s_sk);
let mut masking_nonce = vec![0u8; 32]; let mut masking_nonce = vec![0u8; 32];
rng.fill_bytes(&mut masking_nonce); rng.fill_bytes(&mut masking_nonce);
@@ -775,13 +747,14 @@ impl<CS: CipherSuite> ServerLogin<CS> {
&record.0.masking_key, &record.0.masking_key,
&masking_nonce, &masking_nonce,
&server_s_pk, &server_s_pk,
&envelope, &record.0.envelope,
)?; )?;
let (id_u, id_s) = match optional_ids { let (id_u, id_s) = bytestrings_from_identifiers(
None => (client_s_pk.to_arr().to_vec(), server_s_pk.to_arr().to_vec()), &optional_ids,
Some((id_u, id_s)) => (id_u, id_s), &client_s_pk.to_arr(),
}; &server_s_pk.to_arr(),
);
let l1_bytes = &l1.serialize(); let l1_bytes = &l1.serialize();
@@ -794,7 +767,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
let credential_response_component = let credential_response_component =
CredentialResponse::<CS>::serialize_without_ke(&beta, &masking_nonce, &masked_response); CredentialResponse::<CS>::serialize_without_ke(&beta, &masking_nonce, &masked_response);
let (plain_info, ke2_state, ke2_message) = CS::KeyExchange::generate_ke2( let (ke2_state, ke2_message) = CS::KeyExchange::generate_ke2(
rng, rng,
l1_bytes.to_vec(), l1_bytes.to_vec(),
credential_response_component, credential_response_component,
@@ -803,7 +776,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
server_s_sk.clone(), server_s_sk.clone(),
id_u, id_u,
id_s, id_s,
e_info, context,
)?; )?;
let credential_response = CredentialResponse { let credential_response = CredentialResponse {
@@ -814,7 +787,6 @@ impl<CS: CipherSuite> ServerLogin<CS> {
}; };
Ok(ServerLoginStartResult { Ok(ServerLoginStartResult {
plain_info,
message: credential_response, message: credential_response,
state: Self { state: Self {
_cs: PhantomData, _cs: PhantomData,
@@ -934,24 +906,24 @@ fn oprf_key_from_seed<G: GroupWithMapToCurve, D: Hash>(
Hkdf::<D>::from_prk(oprf_seed) Hkdf::<D>::from_prk(oprf_seed)
.map_err(|_| InternalPakeError::HkdfError)? .map_err(|_| InternalPakeError::HkdfError)?
.expand( .expand(
&[credential_identifier, &STR_OPRF_KEY].concat(), &[credential_identifier, STR_OPRF_KEY].concat(),
&mut oprf_key_bytes, &mut oprf_key_bytes,
) )
.map_err(|_| InternalPakeError::HkdfError)?; .map_err(|_| InternalPakeError::HkdfError)?;
G::hash_to_scalar::<D>(&oprf_key_bytes[..]) G::hash_to_scalar::<D>(&oprf_key_bytes[..], b"")
} }
fn mask_response<D: Hash>( fn mask_response<CS: CipherSuite>(
masking_key: &[u8], masking_key: &[u8],
masking_nonce: &[u8], masking_nonce: &[u8],
server_s_pk: &PublicKey, server_s_pk: &PublicKey,
envelope: &Envelope<D>, envelope: &Envelope<CS>,
) -> Result<Vec<u8>, ProtocolError> { ) -> Result<Vec<u8>, ProtocolError> {
let mut xor_pad = vec![0u8; <PublicKey as SizedBytes>::Len::to_usize() + Envelope::<D>::len()]; let mut xor_pad = vec![0u8; <PublicKey as SizedBytes>::Len::to_usize() + Envelope::<CS>::len()];
Hkdf::<D>::from_prk(&masking_key) Hkdf::<CS::Hash>::from_prk(masking_key)
.map_err(|_| InternalPakeError::HkdfError)? .map_err(|_| InternalPakeError::HkdfError)?
.expand( .expand(
&[masking_nonce, &STR_CREDENTIAL_RESPONSE_PAD].concat(), &[masking_nonce, STR_CREDENTIAL_RESPONSE_PAD].concat(),
&mut xor_pad, &mut xor_pad,
) )
.map_err(|_| InternalPakeError::HkdfError)?; .map_err(|_| InternalPakeError::HkdfError)?;
@@ -965,16 +937,16 @@ fn mask_response<D: Hash>(
.collect()) .collect())
} }
fn unmask_response<D: Hash>( fn unmask_response<CS: CipherSuite>(
masking_key: &[u8], masking_key: &[u8],
masking_nonce: &[u8], masking_nonce: &[u8],
masked_response: &[u8], masked_response: &[u8],
) -> Result<(PublicKey, Envelope<D>), ProtocolError> { ) -> Result<(PublicKey, Envelope<CS>), ProtocolError> {
let mut xor_pad = vec![0u8; <PublicKey as SizedBytes>::Len::to_usize() + Envelope::<D>::len()]; let mut xor_pad = vec![0u8; <PublicKey as SizedBytes>::Len::to_usize() + Envelope::<CS>::len()];
Hkdf::<D>::from_prk(&masking_key) Hkdf::<CS::Hash>::from_prk(masking_key)
.map_err(|_| InternalPakeError::HkdfError)? .map_err(|_| InternalPakeError::HkdfError)?
.expand( .expand(
&[masking_nonce, &STR_CREDENTIAL_RESPONSE_PAD].concat(), &[masking_nonce, STR_CREDENTIAL_RESPONSE_PAD].concat(),
&mut xor_pad, &mut xor_pad,
) )
.map_err(|_| InternalPakeError::HkdfError)?; .map_err(|_| InternalPakeError::HkdfError)?;
@@ -985,9 +957,12 @@ fn unmask_response<D: Hash>(
.collect(); .collect();
let key_len = <PublicKey as SizedBytes>::Len::to_usize(); let key_len = <PublicKey as SizedBytes>::Len::to_usize();
let unchecked_server_s_pk = let unchecked_server_s_pk =
PublicKey::from_arr(&GenericArray::clone_from_slice(&plaintext[..key_len]))?; PublicKey::from_arr(&GenericArray::clone_from_slice(&plaintext[..key_len]))?;
let envelope = Envelope::deserialize(&plaintext[key_len..])?; let envelope = Envelope::deserialize(&plaintext[key_len..])?;
// FIXME check server_s_pk
Ok((unchecked_server_s_pk, envelope)) // Ensure that public key is valid
let server_s_pk = KeyPair::<CS::Group>::check_public_key(unchecked_server_s_pk)
.map_err(|_| ProtocolError::VerificationError(PakeError::SerializationError))?;
Ok((server_s_pk, envelope))
} }
+13 -60
View File
@@ -12,7 +12,7 @@ use crate::{
traits::{FromBytes, KeyExchange, ToBytes}, traits::{FromBytes, KeyExchange, ToBytes},
tripledh::{NonceLen, TripleDH}, tripledh::{NonceLen, TripleDH},
}, },
keypair::{PublicKey, KeyPair}, keypair::{KeyPair, PublicKey},
serialization::{i2osp, os2ip, serialize}, serialization::{i2osp, os2ip, serialize},
*, *,
}; };
@@ -33,7 +33,6 @@ impl CipherSuite for Default {
type SlowHash = crate::slow_hash::NoOpHash; type SlowHash = crate::slow_hash::NoOpHash;
} }
const MAX_INFO_LENGTH: usize = 10;
const HASH_SIZE: usize = 64; // Because of SHA512 const HASH_SIZE: usize = 64; // Because of SHA512
const MAC_SIZE: usize = 64; // Because of SHA512 const MAC_SIZE: usize = 64; // Because of SHA512
@@ -72,14 +71,10 @@ fn server_registration_roundtrip() {
let mut masking_key = [0u8; HASH_SIZE]; let mut masking_key = [0u8; HASH_SIZE];
rng.fill_bytes(&mut masking_key); rng.fill_bytes(&mut masking_key);
let mut ciphertext = [0u8; 32];
rng.fill_bytes(&mut ciphertext);
// Construct a mock envelope // Construct a mock envelope
let mut mock_envelope_bytes = Vec::new(); let mut mock_envelope_bytes = Vec::new();
mock_envelope_bytes.extend_from_slice(&[1; 1]); // mode = 1
mock_envelope_bytes.extend_from_slice(&vec![0; NonceLen::to_usize()]); // empty nonce mock_envelope_bytes.extend_from_slice(&vec![0; NonceLen::to_usize()]); // empty nonce
mock_envelope_bytes.extend_from_slice(&ciphertext); // ciphertext which is an encrypted private key // mock_envelope_bytes.extend_from_slice(&ciphertext); // ciphertext which is an encrypted private key
mock_envelope_bytes.extend_from_slice(&[0; MAC_SIZE]); // length-MAC_SIZE hmac mock_envelope_bytes.extend_from_slice(&[0; MAC_SIZE]); // length-MAC_SIZE hmac
let mock_client_kp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng); let mock_client_kp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng);
@@ -153,21 +148,15 @@ fn registration_upload_roundtrip() {
let mut key = [0u8; 32]; let mut key = [0u8; 32];
rng.fill_bytes(&mut key); rng.fill_bytes(&mut key);
let mut nonce = [0u8; 32];
rng.fill_bytes(&mut nonce);
let mut masking_key = vec![0u8; <sha2::Sha512 as Digest>::OutputSize::to_usize()]; let mut masking_key = vec![0u8; <sha2::Sha512 as Digest>::OutputSize::to_usize()];
rng.fill_bytes(&mut masking_key); rng.fill_bytes(&mut masking_key);
let mut msg = [0u8; 32]; let (envelope, _) =
rng.fill_bytes(&mut msg); Envelope::<Default>::seal_raw(&key, &nonce, &pubkey_bytes, InnerEnvelopeMode::Internal)
.unwrap();
let (envelope, _) = Envelope::<sha2::Sha512>::seal_raw(
&mut rng,
&key,
&msg,
&pubkey_bytes,
InnerEnvelopeMode::Base,
)
.unwrap();
let envelope_bytes = envelope.serialize(); let envelope_bytes = envelope.serialize();
let mut input = Vec::new(); let mut input = Vec::new();
@@ -190,15 +179,7 @@ fn credential_request_roundtrip() {
let mut client_nonce = vec![0u8; NonceLen::to_usize()]; let mut client_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut client_nonce); rng.fill_bytes(&mut client_nonce);
let mut info = [0u8; MAX_INFO_LENGTH]; let ke1m: Vec<u8> = [&client_nonce[..], &client_e_kp.public()].concat();
rng.fill_bytes(&mut info);
let ke1m: Vec<u8> = [
&client_nonce[..],
&serialize(&info.to_vec(), 2),
&client_e_kp.public(),
]
.concat();
let mut input = Vec::new(); let mut input = Vec::new();
input.extend_from_slice(&alpha_bytes); input.extend_from_slice(&alpha_bytes);
@@ -230,11 +211,8 @@ fn credential_response_roundtrip() {
let mut masking_nonce = vec![0u8; 32]; let mut masking_nonce = vec![0u8; 32];
rng.fill_bytes(&mut masking_nonce); rng.fill_bytes(&mut masking_nonce);
let mut masked_response = vec![ let mut masked_response =
0u8; vec![0u8; <PublicKey as SizedBytes>::Len::to_usize() + Envelope::<Default>::len()];
<PublicKey as SizedBytes>::Len::to_usize()
+ Envelope::<<Default as CipherSuite>::Hash>::len()
];
rng.fill_bytes(&mut masked_response); rng.fill_bytes(&mut masked_response);
let server_e_kp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng); let server_e_kp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng);
@@ -243,16 +221,7 @@ fn credential_response_roundtrip() {
let mut server_nonce = vec![0u8; NonceLen::to_usize()]; let mut server_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut server_nonce); rng.fill_bytes(&mut server_nonce);
let mut e_info = [0u8; MAX_INFO_LENGTH]; let ke2m: Vec<u8> = [&server_nonce[..], &server_e_kp.public(), &mac[..]].concat();
rng.fill_bytes(&mut e_info);
let ke2m: Vec<u8> = [
&server_nonce[..],
&server_e_kp.public(),
&serialize(&e_info.to_vec(), 2),
&mac[..],
]
.concat();
let mut input = Vec::new(); let mut input = Vec::new();
input.extend_from_slice(pt_bytes.as_slice()); input.extend_from_slice(pt_bytes.as_slice());
@@ -329,15 +298,7 @@ fn ke1_message_roundtrip() {
let mut client_nonce = vec![0u8; NonceLen::to_usize()]; let mut client_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut client_nonce); rng.fill_bytes(&mut client_nonce);
let mut info = [0u8; MAX_INFO_LENGTH]; let ke1m: Vec<u8> = [&client_nonce[..], &client_e_kp.public()].concat();
rng.fill_bytes(&mut info);
let ke1m: Vec<u8> = [
&client_nonce[..],
&serialize(&info.to_vec(), 2),
&client_e_kp.public(),
]
.concat();
let reg = <TripleDH as KeyExchange<sha2::Sha512, RistrettoPoint>>::KE1Message::from_bytes::< let reg = <TripleDH as KeyExchange<sha2::Sha512, RistrettoPoint>>::KE1Message::from_bytes::<
Default, Default,
>(&ke1m[..]) >(&ke1m[..])
@@ -355,16 +316,8 @@ fn ke2_message_roundtrip() {
rng.fill_bytes(&mut mac); rng.fill_bytes(&mut mac);
let mut server_nonce = vec![0u8; NonceLen::to_usize()]; let mut server_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut server_nonce); rng.fill_bytes(&mut server_nonce);
let mut e_info = [0u8; MAX_INFO_LENGTH];
rng.fill_bytes(&mut e_info);
let ke2m: Vec<u8> = [ let ke2m: Vec<u8> = [&server_nonce[..], &server_e_kp.public(), &mac[..]].concat();
&server_nonce[..],
&server_e_kp.public(),
&serialize(&e_info.to_vec(), 2),
&mac[..],
]
.concat();
let reg = <TripleDH as KeyExchange<sha2::Sha512, RistrettoPoint>>::KE2Message::from_bytes::< let reg = <TripleDH as KeyExchange<sha2::Sha512, RistrettoPoint>>::KE2Message::from_bytes::<
Default, Default,
+79 -81
View File
@@ -44,6 +44,7 @@ pub struct TestVectorParameters {
pub server_s_sk: Vec<u8>, pub server_s_sk: Vec<u8>,
pub server_e_pk: Vec<u8>, pub server_e_pk: Vec<u8>,
pub server_e_sk: Vec<u8>, pub server_e_sk: Vec<u8>,
pub fake_sk: Vec<u8>,
pub credential_identifier: Vec<u8>, pub credential_identifier: Vec<u8>,
pub id_u: Vec<u8>, pub id_u: Vec<u8>,
pub id_s: Vec<u8>, pub id_s: Vec<u8>,
@@ -54,8 +55,7 @@ pub struct TestVectorParameters {
pub envelope_nonce: Vec<u8>, pub envelope_nonce: Vec<u8>,
pub client_nonce: Vec<u8>, pub client_nonce: Vec<u8>,
pub server_nonce: Vec<u8>, pub server_nonce: Vec<u8>,
pub info1: Vec<u8>, pub context: Vec<u8>,
pub einfo2: Vec<u8>,
pub registration_request: Vec<u8>, pub registration_request: Vec<u8>,
pub registration_response: Vec<u8>, pub registration_response: Vec<u8>,
pub registration_upload: Vec<u8>, pub registration_upload: Vec<u8>,
@@ -75,38 +75,38 @@ static STR_CREDENTIAL_IDENTIFIER: &str = "credential_identifier";
static TEST_VECTOR: &str = r#" static TEST_VECTOR: &str = r#"
{ {
"client_s_pk": "e6201a178ea486df66929dc15e6b7cf522a02c4ff05afdfe221bbda08f9c5142", "client_s_pk": "b47c69b4ea5e87139649349516c2842145993a2a00cc6e63d27c57f170475260",
"client_s_sk": "901f0363ae5b8606740ad0f4849c3f993743d3b1750244c27aa8a53035b42306", "client_s_sk": "60a33dd8e1970aa3d2ed09c03ad0380e0cf628a669d3b7d030d3fea0dd7f5c06",
"client_e_pk": "823dc5bf4e3479cf1be9cf2f9fe08f31a5171eb4ef012747b16364f4485e0022", "client_e_pk": "5a513aecfa17dab422221a980819c680aea9a49947c7c0caca94fc61dcb4632c",
"client_e_sk": "288cece1cfbca7441b35fa4f6a1f4291855835e623c4874edefd735e507c3f0f", "client_e_sk": "9f42ca864614d4175e1540e4c56fe18362cb56b778dccf6b0a9446a23735dc03",
"server_s_pk": "c44776fd33a5fb022bf6e26c4a5cb2c1ddbb785bd5822324df8eca6d80c1d279", "server_s_pk": "8ed3fd51aa5e6931559fa6ae9be9829e609e441efbabb0846933fd5e30a3a268",
"server_s_sk": "5f1c3382a1cc430beeb9bdd044d4453d930fac8fe0d6bb65740b6baca208aa01", "server_s_sk": "a514a8842cd760449887fb2f943440b17073b5073691ceeaa0552210e693ea01",
"server_e_pk": "987fcf872beaf9547adbc66a63481e0f315960836d46956af2b1eb2608142f2e", "server_e_pk": "e8249649f7614f6268df01e54eb992043d49df04c98f8c8cea27c263d95dec4b",
"server_e_sk": "a5d8174ade9f423a18438af605ee5b11047cf9973c21fffefcd336f690dd6103", "server_e_sk": "a4b66443250a0cc39ad9baae6ada72c243ddee53b712eb48933993230c13500f",
"fake_sk": "60a33dd8e1970aa3d2ed09c03ad0380e0cf628a669d3b7d030d3fea0dd7f5c06",
"credential_identifier": "637265644964656e746966696572", "credential_identifier": "637265644964656e746966696572",
"id_u": "696455", "id_u": "696455",
"id_s": "696453", "id_s": "696453",
"password": "70617373776f7264", "password": "70617373776f7264",
"blinding_factor": "ac66fda6a29b52b1c719453adea5370852f906448609601e7da7892c29017b0b", "blinding_factor": "08f845725404c823f477eb1e8f79dab63fdbbb2110a6c360fc98a4d2720e9d0a",
"oprf_seed": "c682275f90adc465ff551a82e54b6e16bd2e49cda05f7c6e6988a269b43a10f824ae31e90c8f8bbbb8cc85f552c0809acab37169332f68351dc7edc575d524f3", "oprf_seed": "1e7fddf167679cb1e83a179d4275034c09d2d745a1fec311a5e59ed30d0b80e2100ee8e6bbc996dc298f7f9e7dcc03c052853a02e4273d33c2973c7a6128affd",
"masking_nonce": "596b51d2a8a4485ae8d506684942cc2be327b72bac3ccf9864376264b2f5c5dbc5283e39cde28751430c6ab072c7c769c481ef61999488d740203a0b3dbce92d", "masking_nonce": "2b49d01802a69aacdad4979c503b96d08f44e4c67eaf82bbf6e71c6bb5473aa819359428f408bda29976beb0243c8a91fbeb2ee57840b90c62f4d87f88344db0",
"envelope_nonce": "9b33bc17a2daa8346449bffe8c479202237a0e074ba82b88b6c4d122b22edac7", "envelope_nonce": "f4351a2d4f1efb09877fbef82d44bff3a963b08cc727874aa75c5d57d604aa2e",
"client_nonce": "a854353a0d88014af9eda6cb7ee3aff7d12fd4174c05221f3300d6ae12561ac0", "client_nonce": "e56a024c1d89f05ff245b98ba097cdfdcc5c181b3d5e9d52100d421d3160f80a",
"server_nonce": "949f7773f95493a238f20a2212924108c6492d9eff5e1151b082c869496d1a7c", "server_nonce": "9b78591d87600abf26789c0691dd5f760d5620aa58e34181cb24503bf04c936a",
"info1": "696e666f31", "context": "636f6e74657874",
"einfo2": "65696e666f32", "registration_request": "0cdc7df1cca989b56917c95127e59ec8f05bda7c606cb45e714bfa582b429832",
"registration_request": "3a5d9fb60e6e91e97da6ca425a7ae8c5defce2ac0c6d89a8189f42f928fa3f04", "registration_response": "e88418f5a9145287062e50b060e6f6790583ec8646430af1bff0a2729bf20d1b8ed3fd51aa5e6931559fa6ae9be9829e609e441efbabb0846933fd5e30a3a268",
"registration_response": "145476844f947d9b639540c66045fc0e9c755d7a03c10ed618135fcd53cbdf30c44776fd33a5fb022bf6e26c4a5cb2c1ddbb785bd5822324df8eca6d80c1d279", "registration_upload": "d6f1486284e595707ae341a4d083d454477933b1bcf770bfc4087127c0a8e844e833f76e997aef5b46d2108811667183d08f0cc0a8465dac277287591cac1e42933ed23a2c9476cfa939854a40fc746c21606535b19f0a48cf8cc565f7c3e6df60a33dd8e1970aa3d2ed09c03ad0380e0cf628a669d3b7d030d3fea0dd7f5c0654e4188e55b7fe2eed8a7aee79ae6cfefabab86e7b7822f05bc422ac7e7a9acb968001b3dc5ead255a2d7599a7be60aa97ebed89808db20faa445e912f7df2da",
"registration_upload": "e6201a178ea486df66929dc15e6b7cf522a02c4ff05afdfe221bbda08f9c514276430085ed2a1f6f9ec49e0ee31f886c806ef1a15b07b0e243a736f38cfed069f97788f2843fc74b8dd6906186af8b77503a5e7932b22c0b772741a475352581029b33bc17a2daa8346449bffe8c479202237a0e074ba82b88b6c4d122b22edac7f32699e8be005286b985ec51d2b37212b04ccfc566cdd38a941177c31cae8158949c584667bf6bec49701e2ee9ac080d51919607993e31e9abf6a31368072e293011a8fce6781db258f1ccdff6b603eed9f405891d72c6e00145d8939ed226ad", "credential_request": "0cdc7df1cca989b56917c95127e59ec8f05bda7c606cb45e714bfa582b429832e56a024c1d89f05ff245b98ba097cdfdcc5c181b3d5e9d52100d421d3160f80a5a513aecfa17dab422221a980819c680aea9a49947c7c0caca94fc61dcb4632c",
"credential_request": "3a5d9fb60e6e91e97da6ca425a7ae8c5defce2ac0c6d89a8189f42f928fa3f04a854353a0d88014af9eda6cb7ee3aff7d12fd4174c05221f3300d6ae12561ac00005696e666f31823dc5bf4e3479cf1be9cf2f9fe08f31a5171eb4ef012747b16364f4485e0022", "credential_response": "e88418f5a9145287062e50b060e6f6790583ec8646430af1bff0a2729bf20d1b2b49d01802a69aacdad4979c503b96d08f44e4c67eaf82bbf6e71c6bb5473aa8718337df372fbb0de1beb29e2f4e6a2419858326ffe3f2a24172cca25e6344edd7db031cac3e206218eda4555d816f341c428317a4d37ed63441a278f78185b202b675b620e6f35056964d400c311cad23a1e6b0d9a91837d9d0021280bf0facf422961c96cffea530a24eb2486d4fa91adadaf7ac9a17d35b329b2add32e368a4b66443250a0cc39ad9baae6ada72c243ddee53b712eb48933993230c13500f2896e6f69e8610ced17584f34c09d872300bac6c99b8157392517ab9e9ed1f4aa163f8040d899cc77cf1f0ca2c4be6aef1616288cd3a6ac21989bdfc07bc4e94a284cf4c588583b2361195feab1ddcd390defde6282db2edc3eb535ede66404b",
"credential_response": "145476844f947d9b639540c66045fc0e9c755d7a03c10ed618135fcd53cbdf30596b51d2a8a4485ae8d506684942cc2be327b72bac3ccf9864376264b2f5c5dba3f796185711239a9e6ef70e66331a52aaaa93629d44987f10a4374ac6a91ab27779da2bf6b2fe37bb9defb73c1a25fdfe8eb06d55d1e40588173b52d075853c57a919f0e088fbdbefa58659a011384145667a12c0ee7474d8f72e86ea52ed1de3617be02b90a90da1b7dd5d5f606da651ea97ceee7a00eb0d56ef76e587921d0a79ed45e37258a7933a31e0d378be00cbcfa1096b62feadbdf9612a33183909aea5d8174ade9f423a18438af605ee5b11047cf9973c21fffefcd336f690dd6103fca1950aac58db4be36a8902ef986dccf93ac6d8ac3203fb5ab2bed06996f27d0006d61f243b55af0cc2a44b7c0f3e060a9091b166cdf4dc3a1017aefd9c419462246f8b4998a853495fb13ebada79f98807d804189e03055047cbf7cf8374924aa137840342e566", "credential_finalization": "2f8c71675d7db1b32ed3daaa7f15fc353f6af536ab1199e41e43ece9871d8b69336b8c84c4906810bb87c1a0407bd5f5d780c7d10a1c94016103639e507cf6d0",
"credential_finalization": "716d4d7cb3090d6897cd01a693df9f8254b910c75a872f62dc5e08f5c23af724c3d499d0d0e8a888ab4ea3fc1eefb870ab64f0513a978a97dc0e99cd47d05142", "client_registration_state": "08f845725404c823f477eb1e8f79dab63fdbbb2110a6c360fc98a4d2720e9d0a70617373776f7264",
"client_registration_state": "ac66fda6a29b52b1c719453adea5370852f906448609601e7da7892c29017b0b70617373776f7264", "client_login_state": "08f845725404c823f477eb1e8f79dab63fdbbb2110a6c360fc98a4d2720e9d0a00600cdc7df1cca989b56917c95127e59ec8f05bda7c606cb45e714bfa582b429832e56a024c1d89f05ff245b98ba097cdfdcc5c181b3d5e9d52100d421d3160f80a5a513aecfa17dab422221a980819c680aea9a49947c7c0caca94fc61dcb4632c00409f42ca864614d4175e1540e4c56fe18362cb56b778dccf6b0a9446a23735dc03e56a024c1d89f05ff245b98ba097cdfdcc5c181b3d5e9d52100d421d3160f80a70617373776f7264",
"client_login_state": "ac66fda6a29b52b1c719453adea5370852f906448609601e7da7892c29017b0b00673a5d9fb60e6e91e97da6ca425a7ae8c5defce2ac0c6d89a8189f42f928fa3f04a854353a0d88014af9eda6cb7ee3aff7d12fd4174c05221f3300d6ae12561ac00005696e666f31823dc5bf4e3479cf1be9cf2f9fe08f31a5171eb4ef012747b16364f4485e00220040288cece1cfbca7441b35fa4f6a1f4291855835e623c4874edefd735e507c3f0fa854353a0d88014af9eda6cb7ee3aff7d12fd4174c05221f3300d6ae12561ac070617373776f7264", "server_login_state": "a62f305635e341c151f5e51b89307940031337a0ad8f1369ddec9b672dc31f35d59be00eb66d77bda0079d6eda94809c863da359fef3a636704ae3fa1c9b9b2d18eb9b193528fbb392a5eab5da8068b7c276c8fe00814213ddd70d02157902bebfce850b403aaa4c99f8dbd5ff50d4ad3e703fb564a3fc474861e3f69d7c9a90037d3dbf36f215082644d5c5bc91e138f9665e7bc538f4bc70f97c91dfcd029b1c027b03dc99137478b3570d9da27922b88a8784f1c2f07cd04a0db04246531a",
"server_login_state": "20508608406858c2038cd6ceef7ef4363648b341576b89f0fbf1c5dd262efade90e16a1cc74f73971fc7c37a0fca8209bf13b0015fefe3e32637d2e951b8500282a29681b6264fa2dd4c3b88cd7d654bdc8e8fb50ed6d78869b403978a1dd05a2c0c602037c1eba611ea2780c5c9ea68a620440adc9ec828e203590420207ad5396a1ffedeb4048f38e8197866c256dd034d3481615fa7f269a3512c99e461e21e8e1be737f7064ed8881205d9271c2c01453eef262140e0561fb64499abe863", "password_file": "d6f1486284e595707ae341a4d083d454477933b1bcf770bfc4087127c0a8e844e833f76e997aef5b46d2108811667183d08f0cc0a8465dac277287591cac1e42933ed23a2c9476cfa939854a40fc746c21606535b19f0a48cf8cc565f7c3e6df60a33dd8e1970aa3d2ed09c03ad0380e0cf628a669d3b7d030d3fea0dd7f5c0654e4188e55b7fe2eed8a7aee79ae6cfefabab86e7b7822f05bc422ac7e7a9acb968001b3dc5ead255a2d7599a7be60aa97ebed89808db20faa445e912f7df2da",
"password_file": "e6201a178ea486df66929dc15e6b7cf522a02c4ff05afdfe221bbda08f9c514276430085ed2a1f6f9ec49e0ee31f886c806ef1a15b07b0e243a736f38cfed069f97788f2843fc74b8dd6906186af8b77503a5e7932b22c0b772741a475352581029b33bc17a2daa8346449bffe8c479202237a0e074ba82b88b6c4d122b22edac7f32699e8be005286b985ec51d2b37212b04ccfc566cdd38a941177c31cae8158949c584667bf6bec49701e2ee9ac080d51919607993e31e9abf6a31368072e293011a8fce6781db258f1ccdff6b603eed9f405891d72c6e00145d8939ed226ad", "export_key": "f1abeb7ab0a43ff1924b59d744053b271d999f341eedc740f1f62d785d19bec939479e5e39f2ec25f5ef712ecd10a085653ad1ed9049092cb2a3d44d6cc205ba",
"export_key": "b01eea8b84b9fa10a179d8002cc782d69cc82c9c02c19200322457126624b24cf981c99257ec09b2d738b7a7a474b50149c8c855e63f2ba7aa0cfe4493334742", "session_key": "037d3dbf36f215082644d5c5bc91e138f9665e7bc538f4bc70f97c91dfcd029b1c027b03dc99137478b3570d9da27922b88a8784f1c2f07cd04a0db04246531a"
"session_key": "396a1ffedeb4048f38e8197866c256dd034d3481615fa7f269a3512c99e461e21e8e1be737f7064ed8881205d9271c2c01453eef262140e0561fb64499abe863"
} }
"#; "#;
@@ -126,6 +126,7 @@ fn populate_test_vectors(values: &Value) -> TestVectorParameters {
server_s_sk: decode(&values, "server_s_sk").unwrap(), server_s_sk: decode(&values, "server_s_sk").unwrap(),
server_e_pk: decode(&values, "server_e_pk").unwrap(), server_e_pk: decode(&values, "server_e_pk").unwrap(),
server_e_sk: decode(&values, "server_e_sk").unwrap(), server_e_sk: decode(&values, "server_e_sk").unwrap(),
fake_sk: decode(&values, "fake_sk").unwrap(),
credential_identifier: decode(&values, "credential_identifier").unwrap(), credential_identifier: decode(&values, "credential_identifier").unwrap(),
id_u: decode(&values, "id_u").unwrap(), id_u: decode(&values, "id_u").unwrap(),
id_s: decode(&values, "id_s").unwrap(), id_s: decode(&values, "id_s").unwrap(),
@@ -136,8 +137,7 @@ fn populate_test_vectors(values: &Value) -> TestVectorParameters {
envelope_nonce: decode(&values, "envelope_nonce").unwrap(), envelope_nonce: decode(&values, "envelope_nonce").unwrap(),
client_nonce: decode(&values, "client_nonce").unwrap(), client_nonce: decode(&values, "client_nonce").unwrap(),
server_nonce: decode(&values, "server_nonce").unwrap(), server_nonce: decode(&values, "server_nonce").unwrap(),
info1: decode(&values, "info1").unwrap(), context: decode(&values, "context").unwrap(),
einfo2: decode(&values, "einfo2").unwrap(),
registration_request: decode(&values, "registration_request").unwrap(), registration_request: decode(&values, "registration_request").unwrap(),
registration_response: decode(&values, "registration_response").unwrap(), registration_response: decode(&values, "registration_response").unwrap(),
registration_upload: decode(&values, "registration_upload").unwrap(), registration_upload: decode(&values, "registration_upload").unwrap(),
@@ -198,8 +198,7 @@ fn stringify_test_vectors(p: &TestVectorParameters) -> String {
); );
s.push_str(format!("\"client_nonce\": \"{}\",\n", hex::encode(&p.client_nonce)).as_str()); s.push_str(format!("\"client_nonce\": \"{}\",\n", hex::encode(&p.client_nonce)).as_str());
s.push_str(format!("\"server_nonce\": \"{}\",\n", hex::encode(&p.server_nonce)).as_str()); s.push_str(format!("\"server_nonce\": \"{}\",\n", hex::encode(&p.server_nonce)).as_str());
s.push_str(format!("\"info1\": \"{}\",\n", hex::encode(&p.info1)).as_str()); s.push_str(format!("\"context\": \"{}\",\n", hex::encode(&p.context)).as_str());
s.push_str(format!("\"einfo2\": \"{}\",\n", hex::encode(&p.einfo2)).as_str());
s.push_str( s.push_str(
format!( format!(
"\"registration_request\": \"{}\",\n", "\"registration_request\": \"{}\",\n",
@@ -284,10 +283,12 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
let server_e_kp = KeyPair::<CS::Group>::generate_random(&mut rng); let server_e_kp = KeyPair::<CS::Group>::generate_random(&mut rng);
let client_s_kp = KeyPair::<CS::Group>::generate_random(&mut rng); let client_s_kp = KeyPair::<CS::Group>::generate_random(&mut rng);
let client_e_kp = KeyPair::<CS::Group>::generate_random(&mut rng); let client_e_kp = KeyPair::<CS::Group>::generate_random(&mut rng);
let fake_kp = KeyPair::<CS::Group>::generate_random(&mut rng);
let credential_identifier = b"credIdentifier"; let credential_identifier = b"credIdentifier";
let id_u = b"idU"; let id_u = b"idU";
let id_s = b"idS"; let id_s = b"idS";
let password = b"password"; let password = b"password";
let context = b"context";
let mut oprf_seed = [0u8; 64]; let mut oprf_seed = [0u8; 64];
rng.fill_bytes(&mut oprf_seed); rng.fill_bytes(&mut oprf_seed);
let mut masking_nonce = [0u8; 64]; let mut masking_nonce = [0u8; 64];
@@ -299,16 +300,15 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
let mut server_nonce = vec![0u8; NonceLen::to_usize()]; let mut server_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut server_nonce); rng.fill_bytes(&mut server_nonce);
let server_setup = let fake_sk: Vec<u8> = fake_kp.private().to_vec();
ServerSetup::<CS>::deserialize(&[&oprf_seed, &server_s_kp.private().to_arr()[..]].concat()) let server_setup = ServerSetup::<CS>::deserialize(
.unwrap(); &[&oprf_seed, &server_s_kp.private().to_arr()[..], &fake_sk].concat(),
)
.unwrap();
let blinding_factor = CS::Group::random_nonzero_scalar(&mut rng); let blinding_factor = CS::Group::random_nonzero_scalar(&mut rng);
let blinding_factor_bytes = CS::Group::scalar_as_bytes(&blinding_factor).clone(); let blinding_factor_bytes = CS::Group::scalar_as_bytes(&blinding_factor).clone();
let info1 = b"info1";
let einfo2 = b"einfo2";
let mut blinding_factor_registration_rng = CycleRng::new(blinding_factor_bytes.to_vec()); let mut blinding_factor_registration_rng = CycleRng::new(blinding_factor_bytes.to_vec());
let client_registration_start_result = let client_registration_start_result =
ClientRegistration::<CS>::start(&mut blinding_factor_registration_rng, password).unwrap(); ClientRegistration::<CS>::start(&mut blinding_factor_registration_rng, password).unwrap();
@@ -346,7 +346,9 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
.finish( .finish(
&mut finish_registration_rng, &mut finish_registration_rng,
server_registration_start_result.message, server_registration_start_result.message,
ClientRegistrationFinishParameters::WithIdentifiers(id_u.to_vec(), id_s.to_vec()), ClientRegistrationFinishParameters::WithIdentifiers(
Identifiers::ClientAndServerIdentifiers(id_u.to_vec(), id_s.to_vec()),
),
) )
.unwrap(); .unwrap();
let registration_upload_bytes = client_registration_finish_result let registration_upload_bytes = client_registration_finish_result
@@ -363,12 +365,8 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
client_login_start.extend_from_slice(&client_nonce); client_login_start.extend_from_slice(&client_nonce);
let mut client_login_start_rng = CycleRng::new(client_login_start); let mut client_login_start_rng = CycleRng::new(client_login_start);
let client_login_start_result = ClientLogin::<CS>::start( let client_login_start_result =
&mut client_login_start_rng, ClientLogin::<CS>::start(&mut client_login_start_rng, password).unwrap();
password,
ClientLoginStartParameters::WithInfo(info1.to_vec()),
)
.unwrap();
let credential_request_bytes = client_login_start_result.message.serialize().to_vec(); let credential_request_bytes = client_login_start_result.message.serialize().to_vec();
let client_login_state = client_login_start_result.state.serialize().to_vec(); let client_login_state = client_login_start_result.state.serialize().to_vec();
@@ -386,10 +384,9 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
Some(password_file), Some(password_file),
client_login_start_result.message, client_login_start_result.message,
credential_identifier, credential_identifier,
ServerLoginStartParameters::WithInfoAndIdentifiers( ServerLoginStartParameters::WithContextAndIdentifiers(
einfo2.to_vec(), context.to_vec(),
id_u.to_vec(), Identifiers::ClientAndServerIdentifiers(id_u.to_vec(), id_s.to_vec()),
id_s.to_vec(),
), ),
) )
.unwrap(); .unwrap();
@@ -400,7 +397,10 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
.state .state
.finish( .finish(
server_login_start_result.message, server_login_start_result.message,
ClientLoginFinishParameters::WithIdentifiers(id_u.to_vec(), id_s.to_vec()), ClientLoginFinishParameters::WithContextAndIdentifiers(
context.to_vec(),
Identifiers::ClientAndServerIdentifiers(id_u.to_vec(), id_s.to_vec()),
),
) )
.unwrap(); .unwrap();
let credential_finalization_bytes = client_login_finish_result.message.serialize(); let credential_finalization_bytes = client_login_finish_result.message.serialize();
@@ -414,6 +414,7 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
server_s_sk: server_s_kp.private().to_arr().to_vec(), server_s_sk: server_s_kp.private().to_arr().to_vec(),
server_e_pk: server_e_kp.public().to_arr().to_vec(), server_e_pk: server_e_kp.public().to_arr().to_vec(),
server_e_sk: server_e_kp.private().to_arr().to_vec(), server_e_sk: server_e_kp.private().to_arr().to_vec(),
fake_sk,
credential_identifier: credential_identifier.to_vec(), credential_identifier: credential_identifier.to_vec(),
id_u: id_u.to_vec(), id_u: id_u.to_vec(),
id_s: id_s.to_vec(), id_s: id_s.to_vec(),
@@ -424,8 +425,7 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
envelope_nonce: envelope_nonce.to_vec(), envelope_nonce: envelope_nonce.to_vec(),
client_nonce: client_nonce.to_vec(), client_nonce: client_nonce.to_vec(),
server_nonce: server_nonce.to_vec(), server_nonce: server_nonce.to_vec(),
info1: info1.to_vec(), context: context.to_vec(),
einfo2: einfo2.to_vec(),
registration_request: registration_request_bytes, registration_request: registration_request_bytes,
registration_response: registration_response_bytes, registration_response: registration_response_bytes,
registration_upload: registration_upload_bytes, registration_upload: registration_upload_bytes,
@@ -477,7 +477,7 @@ fn test_serialization() -> Result<(), ProtocolError> {
serde_json::to_string(&client_registration_start_result.message).unwrap(); serde_json::to_string(&client_registration_start_result.message).unwrap();
assert_eq!( assert_eq!(
registration_request_json, registration_request_json,
r#""Ol2ftg5ukel9pspCWnroxd784qwMbYmoGJ9C+Sj6PwQ=""# r#""DNx98cypibVpF8lRJ+WeyPBb2nxgbLRecUv6WCtCmDI=""#
); );
let registration_request: RegistrationRequest<RistrettoSha5123dhNoSlowHash> = let registration_request: RegistrationRequest<RistrettoSha5123dhNoSlowHash> =
serde_json::from_str(&registration_request_json).unwrap(); serde_json::from_str(&registration_request_json).unwrap();
@@ -505,7 +505,12 @@ fn test_registration_response() -> Result<(), ProtocolError> {
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::deserialize( let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::deserialize(
&[&parameters.oprf_seed[..], &parameters.server_s_sk[..]].concat(), &[
&parameters.oprf_seed[..],
&parameters.server_s_sk[..],
&parameters.fake_sk[..],
]
.concat(),
)?; )?;
let server_registration_start_result = let server_registration_start_result =
@@ -534,7 +539,9 @@ fn test_registration_upload() -> Result<(), ProtocolError> {
.finish( .finish(
&mut finish_registration_rng, &mut finish_registration_rng,
RegistrationResponse::deserialize(&parameters.registration_response[..])?, RegistrationResponse::deserialize(&parameters.registration_response[..])?,
ClientRegistrationFinishParameters::WithIdentifiers(parameters.id_u, parameters.id_s), ClientRegistrationFinishParameters::WithIdentifiers(
Identifiers::ClientAndServerIdentifiers(parameters.id_u, parameters.id_s),
),
)?; )?;
assert_eq!( assert_eq!(
@@ -580,7 +587,6 @@ fn test_credential_request() -> Result<(), ProtocolError> {
let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start( let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_login_start_rng, &mut client_login_start_rng,
&parameters.password, &parameters.password,
ClientLoginStartParameters::WithInfo(parameters.info1),
)?; )?;
assert_eq!( assert_eq!(
hex::encode(&parameters.credential_request), hex::encode(&parameters.credential_request),
@@ -598,7 +604,12 @@ fn test_credential_response() -> Result<(), ProtocolError> {
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap()); let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::deserialize( let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::deserialize(
&[&parameters.oprf_seed[..], &parameters.server_s_sk[..]].concat(), &[
&parameters.oprf_seed[..],
&parameters.server_s_sk[..],
&parameters.fake_sk[..],
]
.concat(),
)?; )?;
let mut server_e_sk_and_nonce_rng = CycleRng::new( let mut server_e_sk_and_nonce_rng = CycleRng::new(
@@ -619,16 +630,11 @@ fn test_credential_response() -> Result<(), ProtocolError> {
&parameters.credential_request[..], &parameters.credential_request[..],
)?, )?,
&parameters.credential_identifier, &parameters.credential_identifier,
ServerLoginStartParameters::WithInfoAndIdentifiers( ServerLoginStartParameters::WithContextAndIdentifiers(
parameters.einfo2.to_vec(), parameters.context,
parameters.id_u, Identifiers::ClientAndServerIdentifiers(parameters.id_u, parameters.id_s),
parameters.id_s,
), ),
)?; )?;
assert_eq!(
hex::encode(&parameters.info1),
hex::encode(server_login_start_result.plain_info),
);
assert_eq!( assert_eq!(
hex::encode(&parameters.credential_response), hex::encode(&parameters.credential_response),
hex::encode(server_login_start_result.message.serialize()) hex::encode(server_login_start_result.message.serialize())
@@ -651,13 +657,12 @@ fn test_credential_finalization() -> Result<(), ProtocolError> {
CredentialResponse::<RistrettoSha5123dhNoSlowHash>::deserialize( CredentialResponse::<RistrettoSha5123dhNoSlowHash>::deserialize(
&parameters.credential_response[..], &parameters.credential_response[..],
)?, )?,
ClientLoginFinishParameters::WithIdentifiers(parameters.id_u, parameters.id_s), ClientLoginFinishParameters::WithContextAndIdentifiers(
parameters.context,
Identifiers::ClientAndServerIdentifiers(parameters.id_u, parameters.id_s),
),
)?; )?;
assert_eq!(
hex::encode(&parameters.einfo2),
hex::encode(&client_login_finish_result.confidential_info)
);
assert_eq!( assert_eq!(
hex::encode(&parameters.server_s_pk), hex::encode(&parameters.server_s_pk),
hex::encode(&client_login_finish_result.server_s_pk.to_arr().to_vec()) hex::encode(&client_login_finish_result.server_s_pk.to_arr().to_vec())
@@ -722,11 +727,8 @@ fn test_complete_flow(
ClientRegistrationFinishParameters::default(), ClientRegistrationFinishParameters::default(),
)?; )?;
let p_file = ServerRegistration::finish(client_registration_finish_result.message); let p_file = ServerRegistration::finish(client_registration_finish_result.message);
let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start( let client_login_start_result =
&mut client_rng, ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(&mut client_rng, login_password)?;
login_password,
ClientLoginStartParameters::default(),
)?;
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start( let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng, &mut server_rng,
&server_setup, &server_setup,
@@ -873,7 +875,6 @@ fn test_zeroize_client_login_start() -> Result<(), ProtocolError> {
let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start( let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng, &mut client_rng,
STR_PASSWORD.as_bytes(), STR_PASSWORD.as_bytes(),
ClientLoginStartParameters::default(),
)?; )?;
let mut state = client_login_start_result.state; let mut state = client_login_start_result.state;
@@ -913,7 +914,6 @@ fn test_zeroize_server_login_start() -> Result<(), ProtocolError> {
let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start( let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng, &mut client_rng,
STR_PASSWORD.as_bytes(), STR_PASSWORD.as_bytes(),
ClientLoginStartParameters::default(),
)?; )?;
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start( let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng, &mut server_rng,
@@ -961,7 +961,6 @@ fn test_zeroize_client_login_finish() -> Result<(), ProtocolError> {
let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start( let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng, &mut client_rng,
STR_PASSWORD.as_bytes(), STR_PASSWORD.as_bytes(),
ClientLoginStartParameters::default(),
)?; )?;
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start( let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng, &mut server_rng,
@@ -1013,7 +1012,6 @@ fn test_zeroize_server_login_finish() -> Result<(), ProtocolError> {
let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start( let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng, &mut client_rng,
STR_PASSWORD.as_bytes(), STR_PASSWORD.as_bytes(),
ClientLoginStartParameters::default(),
)?; )?;
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start( let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng, &mut server_rng,
+412 -255
View File
@@ -4,15 +4,12 @@
// LICENSE file in the root directory of this source tree. // LICENSE file in the root directory of this source tree.
use crate::{ use crate::{
ciphersuite::CipherSuite, ciphersuite::CipherSuite, errors::*, key_exchange::tripledh::TripleDH, keypair::PrivateKey,
errors::*, opaque::*, slow_hash::NoOpHash, tests::mock_rng::CycleRng, *,
key_exchange::tripledh::TripleDH,
opaque::*,
slow_hash::NoOpHash,
tests::mock_rng::CycleRng,
*,
}; };
use curve25519_dalek::ristretto::RistrettoPoint; use curve25519_dalek::ristretto::RistrettoPoint;
use generic_array::typenum::Unsigned;
use generic_bytes::SizedBytes;
use serde_json::Value; use serde_json::Value;
// Tests // Tests
@@ -34,17 +31,19 @@ pub enum EnvelopeMode {
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub struct TestVectorParameters { pub struct TestVectorParameters {
pub dummy_private_key: Vec<u8>,
pub dummy_masking_key: Vec<u8>,
pub context: Vec<u8>,
pub envelope_mode: EnvelopeMode, pub envelope_mode: EnvelopeMode,
pub client_public_key: Vec<u8>, pub client_private_key: Option<Vec<u8>>,
pub client_private_key: Vec<u8>,
pub client_keyshare: Vec<u8>, pub client_keyshare: Vec<u8>,
pub client_private_keyshare: Vec<u8>, pub client_private_keyshare: Vec<u8>,
pub server_public_key: Vec<u8>, pub server_public_key: Vec<u8>,
pub server_private_key: Vec<u8>, pub server_private_key: Vec<u8>,
pub server_keyshare: Vec<u8>, pub server_keyshare: Vec<u8>,
pub server_private_keyshare: Vec<u8>, pub server_private_keyshare: Vec<u8>,
pub client_identity: Vec<u8>, pub client_identity: Option<Vec<u8>>,
pub server_identity: Vec<u8>, pub server_identity: Option<Vec<u8>>,
pub credential_identifier: Vec<u8>, pub credential_identifier: Vec<u8>,
pub password: Vec<u8>, pub password: Vec<u8>,
pub blind_registration: Vec<u8>, pub blind_registration: Vec<u8>,
@@ -70,224 +69,329 @@ pub struct TestVectorParameters {
// of https://datatracker.ietf.org/doc/draft-irtf-cfrg-opaque/ // of https://datatracker.ietf.org/doc/draft-irtf-cfrg-opaque/
static TEST_VECTORS: &[&str] = &[ static TEST_VECTORS: &[&str] = &[
r#" r#"
## OPAQUE-3DH Test Vector 1
### Configuration
~~~
OPRF: 0001 OPRF: 0001
Hash: SHA512 Hash: SHA512
MHF: Identity MHF: Identity
KDF: HKDF-SHA512 KDF: HKDF-SHA512
MAC: HMAC-SHA512 MAC: HMAC-SHA512
Name: 3DH
EnvelopeMode: 01 EnvelopeMode: 01
Group: ristretto255 Group: ristretto255
oprf_seed: 7bc32c4249689ebdf218d04a2cbfb8d06850d4f1d1acb2b0413b9b3e40 Context: 4f50415155452d504f43
b45b3f9f4647df5bbf6dd32e7d41f7dbc2ddf053f047cbf26a684f2b341ad7459373f Nh: 64
1 Npk: 32
Nsk: 32
Nm: 64
Nx: 64
Nok: 32
~~~
### Input Values
~~~
oprf_seed: 5c4f99877d253be5817b4b03f37b6da680b0d5671d1ec5351fa61c5d82
eab28b9de4c4e170f27e433ba377c71c49aa62ad26391ee1cac17011d8a7e9406657c
8
credential_identifier: 31323334 credential_identifier: 31323334
password: 436f7272656374486f72736542617474657279537461706c65 password: 436f7272656374486f72736542617474657279537461706c65
envelope_nonce: 69f191aeb1af61c1feea7688a7c433a645d0f81c3168b4558b3f1 envelope_nonce: 71b8f14b7a1059cdadc414c409064a22cf9e970b0ffc6f1fc6fdd
d83d08b7a25 539c4676775
masking_nonce: 154b361eb3a95bcb64700b8c26898a1e2f78eeb6232b2361b84721 masking_nonce: 54f9341ca183700f6b6acf28dbfe4a86afad788805de49f2d680ab
778ca93686 86ff39ed7f
client_private_key: 533c2e6d91c934f919ac218973be55ba0d7b234160a0d4cf3 server_private_key: 16eb9dc74a3df2033cd738bf2cfb7a3670c569d7749f284b2
bddafbda99e2e0c b241cb237e7d10f
client_public_key: a07d9609083613e2d7521b8f77f1cd7a07d89ea03aa0045080 server_public_key: 18d5035fd0a9c1d6412226df037125901a43f4dff660c0549d
775edc37949341 402f672bcc0933
server_private_key: 3af5aec325791592eee4a8860522f8444c8e71ac33af5186a server_nonce: f9c5ec75a8cd571370add249e99cb8a8c43f6ef05610ac6e354642b
9706137886dce08 f4fedbf69
server_public_key: 4c6dff3083c068b8ca6fec4dbaabc16b5fdac5d98832f25a5b client_nonce: 804133133e7ee6836c8515752e24bb44d323fef4ead34cde967798f
78624cbd10b371 2e9784f69
client_info: 68656c6c6f20626f62 server_keyshare: 6e77d4749eb304c4d74be9457c597546bc22aed699225499910f
server_info: 6772656574696e677320616c696365 c913b3e90712
server_nonce: a71b6f2ff4c8baae05637f574deec70050dffda1f68d10e8648c838 client_keyshare: f67926bd036c5dc4971816b9376e9f64737f361ef8269c18f69f
b696e1918 1ab555e96d4a
client_nonce: acd3d4ffff4667b6a6b2d82b95bb8a171caacaa063e102a3a10077a server_private_keyshare: f8e3e31543dd6fc86833296726773d51158291ab9afd
a6c7ac211 666bb55dce83474c1101
server_keyshare: ca372e52516d51c19763ad5eb1a5b60dafb68c264dcf6bcc692f client_private_keyshare: 4230d62ea740b13e178185fc517cf2c313e6908c4cd9
667a71c5a617 fb42154870ff3490c608
client_keyshare: 4c415eebd7a9bb5f921cbcfc5863e48c9e79fd2ecc1788e2b616 blind_registration: c62937d17dc9aa213c9038f84fe8c5bf3d953356db01c4d48
bea0853f627a acb7cae48e6a504
server_private_keyshare: 080d0a4d352de92672ab709b1ae1888cb48dfabc2d6c blind_login: b5f458822ea11c900ad776e38e29d7be361f75b4d79b55ad74923299
a5b914b335512fe70508 bf8d6503
client_private_keyshare: 7e5bcbf82a46109ee0d24e9bcab41fc830a6ce8b82fc oprf_key: 23d431bab39aea4d2737ac391a50076300210730971788e3a6a8c29ad3c
1e9213a043b743b95800 5930e
blind_registration: 8bcb0b70dac18de24eef12e737d6b28724d3e37774e0b092f ~~~
9f70b255defaf04
blind_login: f3a0829898a89239dce29ccc98ec8b449a34b255ba1e6f944829d18e ### Intermediate Values
0d589b0f
oprf_key: f993af4bc97f6e752da6d97eb0a489a68ec1dc2f3327e3f480880f26a2b ~~~
73b0a client_public_key: f692d6b738b4e240d5f59d534371363b47817c00c7058d4a33
auth_key: 8ec5878a7d305c1252f11e4c1a5b8219919a4b5b71c85ab3130d0b8cbd9 439911e66c3c27
e5302669a3b1b4275b5ffb67b33db3955eac60b37bea96d3dfad169be702720a00b3b auth_key: 27972f9b1cf2ce524d50a7afa40a2ee6957904e2bef29976bdbda452a84
prk: 2e400d7a64d6abbe9d36a83f9b5a89bbc1cef2fef1094292c4b76b6a3c1d987e fcf01023f3ddd8182e64ea5287f99765dd39b83fa89fe189db227212a144134684783
de2228b6d36ae174bc304302af542b6ccb1e7f2fb6bd302b30536112cc11110e randomized_pwd: 750ef06299c2fb102242fd84e59613616338f83e69c09c1dc3f91
pseudorandom_pad: b21d130015d7f6d33bbf27f8e1ea3d9a7c7c05f94a6f197bc85 c57ac0642876ccbe785e94aa094262efdc6aed08b3faff7c1bddfa14c434c5a908ad6
1313f4a4f2d2f c5f9d5
envelope: 0169f191aeb1af61c1feea7688a7c433a645d0f81c3168b4558b3f1d83d envelope: 71b8f14b7a1059cdadc414c409064a22cf9e970b0ffc6f1fc6fdd539c46
08b7a25e1213d6d841ec22a2213067192546820710726b82acfcdb4f38c9e82e3d103 76775455739db882585a7c8b3e9ae7955da7135900d85ab832aa83a34b3ce481efc9e
234e3f37b5afd99144411550b9dc3f4f0c920d923cdfb952f5a9050a6c97440230262 43d4c2276220c8bcb9d27b5a827a5a2d655700321f3b32d21f578c21316195d8
4bc5da9563a768f73b195f992741e399cfa9d95eb48b220998b59360b01a3 handshake_secret: 02fb23a668b7138b029c95d21f1e0eec9e10377be933bdbf3e5
handshake_secret: 7b416efcce7df41f7b9698c68a6b278286b89d378f5b3fac7f4 33ea39073d3ce9d1ef16b55a8a8464f3bf6a991cc645d14c1fa3d9d6cfe36c6c0dcc2
d96a0d45f5457e2c4ca2f3bf693325a8710d223a988aedcab7a4deb4892ddb7d13dec 691d7109
1989d298 server_mac_key: e75ce46beeebd26f22540d7988de9809a69cf34fec6c050750708
handshake_encrypt_key: 122f419c0457e01dda8382e65e92b614015b37cba09666 e91232297fdbb51e875cd37167d5ce661ebccf0004dbbf96311daf64ddec7faae04c4
92c6363f28fad97d00d2a0e6ab112e39a81a11aae074696e2e10b433918c5ffca6114 8bbd89
43a7bea8ad353 client_mac_key: 4bce132daa031fff2a6e5ac29287c4641e3b9dc2560394b8c73f3
server_mac_key: 9aeaacea81de96d2115bc9f86c1bedfe04357afc69a16c4e3c0bc b748f1e51e577b932a960b236981217b33bee220b0bce2696638cfb7791f427ade292
eb07409d5a9e4f76df2844f5f493feb55c9384af39bb9bd3e928cb796b2c8f1dc03a9 d60f55
ecf93d ~~~
client_mac_key: ee1ccd9c1d66bd6137aec52e323de67c914a35980fc384adeb15e
d10dfea0c389ffaebd889ee36ec4e03253f814e07d93b13e1d8c9a9fe683ea39c6b9d ### Output Values
58e53f
registration_request: 24bbcabb15452642f709cb8567eff38f4cda6044aca3356 ~~~
87a62b8453d849c18 registration_request: 80576bce33c6ce89f9e1a06d8595cd9d09d9aef46b20dad
registration_response: fe8865a1916ad735de5e131930f14922582e3205665cbf d57a845dc50e7c074
2e9e2f3fbcfb776e2b4c6dff3083c068b8ca6fec4dbaabc16b5fdac5d98832f25a5b7 registration_response: 1a80fdb4f4eb1985587b5b95661d2cff1ef2493cdcdd88
8624cbd10b371 b5699f39048f0d6c2618d5035fd0a9c1d6412226df037125901a43f4dff660c0549d4
registration_upload: a07d9609083613e2d7521b8f77f1cd7a07d89ea03aa00450 02f672bcc0933
80775edc379493418183c015b1b27290fc3cf14c963da55f59ed34f70ef871bcc9888 registration_upload: f692d6b738b4e240d5f59d534371363b47817c00c7058d4a
9d53039e5c589b3474f1e3439bfefaf55100334a578ca3d5ec8437d7b06ba02689536 33439911e66c3c2795014d8fc0c710bd763c981c5b9329c95e149c6717af91bad2cec
e8b6ae0169f191aeb1af61c1feea7688a7c433a645d0f81c3168b4558b3f1d83d08b7 daf87f2c3c9c11914cb6d44aaee5679e3e61e1b65241fda74902cca908a065495c0b2
a25e1213d6d841ec22a2213067192546820710726b82acfcdb4f38c9e82e3d103234e 8b799e71b8f14b7a1059cdadc414c409064a22cf9e970b0ffc6f1fc6fdd539c467677
3f37b5afd99144411550b9dc3f4f0c920d923cdfb952f5a9050a6c974402302624bc5 5455739db882585a7c8b3e9ae7955da7135900d85ab832aa83a34b3ce481efc9e43d4
da9563a768f73b195f992741e399cfa9d95eb48b220998b59360b01a3 c2276220c8bcb9d27b5a827a5a2d655700321f3b32d21f578c21316195d8
KE1: 0e8eeeb2ca0dbf5f690cfe0b76783d7667245f399b874a989f168fdd3e572663 KE1: 60d71c9f5d2a14568807b869e2c251a8e5f7ad8951cd8386c7e32c0634b26b16
acd3d4ffff4667b6a6b2d82b95bb8a171caacaa063e102a3a10077aa6c7ac21100096 804133133e7ee6836c8515752e24bb44d323fef4ead34cde967798f2e9784f69f6792
8656c6c6f20626f624c415eebd7a9bb5f921cbcfc5863e48c9e79fd2ecc1788e2b616 6bd036c5dc4971816b9376e9f64737f361ef8269c18f69f1ab555e96d4a
bea0853f627a KE2: 78a428204f552d3532bad040c961324edb22c738d98f1dd770d65caba0bd8966
KE2: 085ab15bca16570dfea48efee2e12d1b13b95c1530a5e9352acb6fc10485353f 54f9341ca183700f6b6acf28dbfe4a86afad788805de49f2d680ab86ff39ed7fbcbbb
154b361eb3a95bcb64700b8c26898a1e2f78eeb6232b2361b84721778ca9368696b92 84a18810b8eb1dc898d9af686f5901a21d0768720b325279fde4931ee52f0d4a0d0d9
2224f1082627dcb1912944d5df179fe27633a6deff35649bd8192f6c2012241823265 cd1cd7c424d4622b1588ba554cd9241352a59ef52bbe85e0f865021404b115ba954f5
51d7b485a071b2af1fb86dea63a8e9d3b40cd43bf42e97bb9d1763508478412c1a495 540cf2d811a6566a93876cac1239b1f75f39b070250af5a84a819e08b13e9e437a80f
1add4c82e622e2a14b612376364d7fd3a3cdfc1bb36bb789afe1567cbf1c10a006eb8 c25cc130f8475dde43efe6d900c664e9bac300298bb0f9c5ec75a8cd571370add249e
85ef412395e6d4a1823197254fb5d1a1345228712fd27ed49355735d30dbf548fea57 99cb8a8c43f6ef05610ac6e354642bf4fedbf696e77d4749eb304c4d74be9457c5975
48cfdc3b58e8062d0060e43e12c6a4b3788682c62a71b6f2ff4c8baae05637f574dee 46bc22aed699225499910fc913b3e907120485942e3e077f71c1dd2d87053b39f0d31
c70050dffda1f68d10e8648c838b696e1918ca372e52516d51c19763ad5eb1a5b60da bfe5d5f90df0e85ad9ce771e4f4d1ab697a10a02002cd73916051b887da9554465d58
fb68c264dcf6bcc692f667a71c5a617000f723180269119d5c2c2348cabb9ed372e24 68811fd8b22b8f457ed5a4b0
70210d9de3ff1970b449d7bac51d0a3b10d58d3f8c58d5698230d6dc102794f658538 KE3: b4f8aece9fb4f6b7b5ffe1c98747a91f4ec7bf5481fe5719ba4baad668e3fd4e
7b6b412a9ce973109ebeb62ea444c46180448a3a0af89055786d7ce 8aba4fa227bd4c688ed9e17f6c6d28ab5e5617a883207d80979dc4797ca89304
KE3: 737427092049117f0a75e42515b81572c9e971687509bf7919950fe078bc6cfd export_key: 045f61f4baa0a945c2e85dfb7a85fe4df8a49e6c31344920e863c286b
0cd8e7f32232f6eb296a000c2df29f6b65a4a858229766526380d687eda004d4 c8a17fe25fc16c84836335b4b5ecc9743c5d3a221101ab004aa99ce65026b6953ad6c
export_key: b3816d8a69b2a3f4d15113ae5b474353e99fbad6be7e331376688cc94 c0
6e19e063a604ee3532981e756fe60f0a3822b5881729752e2309ee21072b215e82bd4 session_key: 91187690e5ea0da3110a1dd7d5ffd7c4c3111950c587d9fcf3b9f34b
25 f73b86dbeafed42a05024fa875a32415c6143d20c39cd732eb0e31db5e60ea3fb2551
session_key: 3cf3ce41bd59b6e299aab4f26575f8bb8d4d8b12d2406c0d7a3eed92 cf7
11387c363949199665317c5500aef2fb8aa081aa7d74191b92b928382d57b03f13140 ~~~
9cf
"#, "#,
r#" r#"
## OPAQUE-3DH Test Vector 2
### Configuration
~~~
OPRF: 0001 OPRF: 0001
Hash: SHA512 Hash: SHA512
MHF: Identity MHF: Identity
KDF: HKDF-SHA512 KDF: HKDF-SHA512
MAC: HMAC-SHA512 MAC: HMAC-SHA512
Name: 3DH EnvelopeMode: 01
EnvelopeMode: 02
Group: ristretto255 Group: ristretto255
client_identity: 36840cc4f3fd4f57bad888ff6e75a120a6ae132b128df738fbc3 Context: 4f50415155452d504f43
16e5bd57356a Nh: 64
server_identity: 3e2651ff8442883bb83ec1b46a76f99a556ab182fd4828da3fe9 Npk: 32
65b145a0dc7f Nsk: 32
oprf_seed: 258b268d6ec9a468c30e7f009e5d631a31dede64596c8dde12e377d319 Nm: 64
3efe2c90e609e135c4bf7d2c7326306ba1f45c510ca9d2dfe4816c680fadfef82bfbe Nx: 64
c Nok: 32
~~~
### Input Values
~~~
client_identity: 616c696365
server_identity: 626f62
oprf_seed: db5c1c16e264b8933d5da56439e7cfed23ab7287b474fe3cdcd58df089
a365a426ea849258d9f4bc13573601f2e727c90ecc19d448cf3145a662e0065f157ba
5
credential_identifier: 31323334 credential_identifier: 31323334
password: 436f7272656374486f72736542617474657279537461706c65 password: 436f7272656374486f72736542617474657279537461706c65
envelope_nonce: e8cd944521ab8459398b39ef6c8b2bc12473281ec34a3220db1c7 envelope_nonce: d0c7b0f0047682bd87a87e0c3553b9bcdce7e1ae3348570df20bf
88fad2a001b 2747829b2d2
masking_nonce: 24907c8e1151cae5dfb583a99ec1d74d93166e6ee5089137e7a924 masking_nonce: 30635396b708ddb7fc10fb73c4e3a9258cd9c3f6f761b2c227853b
a11d60137b 5def228c85
client_private_key: ed811b4cca7c0e51a886c4343d83c4e5228b87399f1dbf033 server_private_key: eeb2fcc794f98501b16139771720a0713a2750b9e528adfd3
ee131fe4ad75c05 662ad56a7e19b04
client_public_key: 36840cc4f3fd4f57bad888ff6e75a120a6ae132b128df738fb server_public_key: 8aa90cb321a38759fc253c444f317782962ca18d33101eab2c
c316e5bd57356a 8cda04405a181f
server_private_key: 0db27eb7aef2af92c3b297c662a87631531aade91c0558d87 server_nonce: 3fa57f7ef652185f89114109f5a61cc8c9216fdd7398246bb7a0c20
224d922a8573f08 e2fbca2d8
server_public_key: 3e2651ff8442883bb83ec1b46a76f99a556ab182fd4828da3f client_nonce: a6bcd29b5aecc3507fc1f8f7631af3d2f5105155222e48099e5e608
e965b145a0dc7f 5d8c1187a
client_info: 68656c6c6f20626f62 server_keyshare: ae070cdffe5bb4b1c373e71be8e7d8f356ee5de37881533f1039
server_info: 6772656574696e677320616c696365 7bcd84d35445
server_nonce: ee9675eb495049b7d24c33691d9b150406e646d1f6f2911bf5c665b client_keyshare: 642e7eecf19b804a62817486663d6c6c239396f709b663a4350c
cb71e3649 da67d025687a
client_nonce: 10603d5ae57cdd698410d49769c00bc248ebba0f5709ba9d6179ab4 server_private_keyshare: 0974010a8528b813f5b33ae0d791df88516c8839c152
42d8a883c b030697637878b2d8b0a
server_keyshare: 264af8bc6a2c78acb503cd838ae3e5e3715df02d19dd4ddfbca9 client_private_keyshare: 03b52f066898929f4aca48014b2b97365205ce691ee3
e4f46b0a0e2d 444b0a7cecec3c7efb01
client_keyshare: 223228d3df70ac6e0b179a48609517304386692952f49cff086e blind_registration: a66ffb41ccf1194a8d7dda900f8b6b0652e4c7fac4610066f
0bea06f5363f e0489a804d3bb05
server_private_keyshare: 94b049e1b0d73ab5b8d914b08dff3e52e62ea8898d35 blind_login: e6f161ac189e6873a19a54efca4baa0719e801e336d929d35ca28b5b
b2862d28ff4c2bb1a607 4f60560e
client_private_keyshare: 362f233a8a73971925abc79daa9fcc06f6d3acf12df8 oprf_key: 1e0550d2dbb9ce5dd9bdbb5f808afbb724c573dc03306dcfc7217796465
2de919be4937fe716a03 ce607
blind_registration: 9b53af4cbdb352b0a2016e5e5f6c0bee4a642526ef9910289 ~~~
315b71feff26f0f
blind_login: 275e46a6aea42c40b78bd2f1281617519f3f790c8d0f42eacce68456 ### Intermediate Values
380b8405
oprf_key: 09da9c8c1ff925937a04c07b613a8bcdf1335db32e11db0a4ee45bfc297 ~~~
2380f client_public_key: ba6cb41f1870e9db7e858440a664e6559d01fdbfb638bbf7e1
auth_key: 20e401aeb76dac20c8f5054685d2531aa3ebe53569b5a50d9d34d19b93b c9004f20d5db71
e7488d7e7b6f0ae0f5c36a876464ac0b69aa2199438d7ac1be69059dea6394dcbe067 auth_key: 5142ae6f6bd80686039656fd7a03cdd7e39cc6e869aa637220d4b5fb64f
prk: 91a4c98e88faa5f30fd0bdc2fb2f0cc808d6808822d079e251bef46a399e4a25 afee2f284a1581fff95ad3a5261b413c5e5b91115f78a3c35486fa56023c300d1726b
6bd3c6207bd01b22bdd713ca3c2c3e1945bfddbbce193e5073c9f47b29928279 randomized_pwd: cea240b632b9c1d704034920cc3dc3c664ed8cd82cf5c0339af76
pseudorandom_pad: c08e7bb57b5b4e6c866119a8d29c51f4f97378e938342f596ff 4d6350d2ee9ba1f675ce8df7b6cf8692d1efb158bafa3c2695ac03a2d92346c19810c
56075bb06dcf0 1a698b
envelope: 02e8cd944521ab8459398b39ef6c8b2bc12473281ec34a3220db1c788fa envelope: d0c7b0f0047682bd87a87e0c3553b9bcdce7e1ae3348570df20bf274782
d2a001b2d0f60f9b127403d2ee7dd9cef1f9511dbf8ffd0a729905a5114518bf1d180 9b2d26e18240c0cbad3b4cdbd7d9d86512f87e43fac39e3785a17504aaa8508f81e3c
f5023e24c1dba1c8940220cf5475fb7d73419a4896096b37403ccdec1da295365cad4 1517b150259be478720935e175b1e34bbe625d0828a62ca9983f9a27aed27f5e
a423674850602e44a524ca3ecf7a3364ab106350e29cfae3937489b30601c handshake_secret: 7925c12d7bf3050e62fe5c8caaece3c85737754c5df79bc59a6
handshake_secret: 54a0b775fda0a405c3e2b585ffd427f8002ff6ed14fb13dbb2f 0fa87929ab1f4a4730f903b87be8b7d89ded8ec97aaec97bc8e7d53a555fd4ad74c4f
c2597984e14b40830c131a6c72035fd40a0c0ac7a24ace5682d73ac1e3a2dff0d36b3 33b9bc83
75cd3146 server_mac_key: 27d6036335c5654132fb08cc81d95b3067ef7fe795f017531231a
handshake_encrypt_key: 2ecbf7901784137571505028bcd2c119c77038ed024d87 e3fa03cd3ab72f1f5e81473318f9c01f990263d885dfce4b6ac8630fdc8ee8abc6a36
20810c50e076fbf01be82326df2d3c52a5fa2da623603c65caa078002abb9c9d0d5f7 7c2339
07c5fcade01bf client_mac_key: ebb3693bac6310075a89922c7a40599d14d03d9104b7a331106e8
server_mac_key: fc7bafe7c51426b18bee3fa7cd895c145ba36481946f4fa7b4355 a578a32a4944751f9d3c230a6690a5747137388a86159cf587969d13dadc0a3830218
3746331ec201891eb3b3cf28590569e3f7d0863da5542ff728c31282bba2ebcc642be dfbca5
533fa2 ~~~
client_mac_key: da7cb58102085bb05de9e175a004e862c653aac24a529f416b33e
3308cf9a9cd9d15e655c02f44d24f8e8e257a27e4db82049ba1eaa456a4f0ba73cfc4 ### Output Values
ef1ef4
registration_request: 1e026d981ad38a4c03e5785f151fc42cf932ec153a1134a ~~~
3e6f7f3cb9b2c632d registration_request: f841cbb85844967568c7405f3831a58c4f5f37ccddb0baa
registration_response: d4d75537ab05e41746c6ed5de6d985e8d08e47a433f04e 4972ea912c960ae66
9b97f0be760dc870093e2651ff8442883bb83ec1b46a76f99a556ab182fd4828da3fe registration_response: 0256257cc6e2b04444edc076b9ad44d8b31593e050bea8
965b145a0dc7f 06485707a818f8a93f8aa90cb321a38759fc253c444f317782962ca18d33101eab2c8
registration_upload: 36840cc4f3fd4f57bad888ff6e75a120a6ae132b128df738 cda04405a181f
fbc316e5bd57356a1008b12b8ab4c890f937b961554635298ea2696433c7a285c7df2 registration_upload: ba6cb41f1870e9db7e858440a664e6559d01fdbfb638bbf7
46da4bef777cc3b4b1004222263792be00050b20385c6f22abd40dc61bcfa823e3c83 e1c9004f20d5db71146e42585d25fa19913876edce4b5ee99b638eb37b1d8a8a76607
4f4fa702e8cd944521ab8459398b39ef6c8b2bc12473281ec34a3220db1c788fad2a0 efaa12299e828641ba4fbf1c46fc2c3776e0a0c9791f88a15b9ddfb5495d63ce92d8f
01b2d0f60f9b127403d2ee7dd9cef1f9511dbf8ffd0a729905a5114518bf1d180f502 58823bd0c7b0f0047682bd87a87e0c3553b9bcdce7e1ae3348570df20bf2747829b2d
3e24c1dba1c8940220cf5475fb7d73419a4896096b37403ccdec1da295365cad4a423 26e18240c0cbad3b4cdbd7d9d86512f87e43fac39e3785a17504aaa8508f81e3c1517
674850602e44a524ca3ecf7a3364ab106350e29cfae3937489b30601c b150259be478720935e175b1e34bbe625d0828a62ca9983f9a27aed27f5e
KE1: be5993d16412c8452d6b320ea8025a8f0b405a0d62dce14bee5dda17c3ef2645 KE1: 14cc586d982b6db9846c78e0b3c543591e95fbf2fc877fa0e5eff89897dd3050
10603d5ae57cdd698410d49769c00bc248ebba0f5709ba9d6179ab442d8a883c00096 a6bcd29b5aecc3507fc1f8f7631af3d2f5105155222e48099e5e6085d8c1187a642e7
8656c6c6f20626f62223228d3df70ac6e0b179a48609517304386692952f49cff086e eecf19b804a62817486663d6c6c239396f709b663a4350cda67d025687a
0bea06f5363f KE2: 8ab71c17547f376ae787741c367142790087090cdde6327dabb2581197bffa59
KE2: a2668282db653a9ed70e46556a78381bba08a8163a71463afc499289a2bf9632 30635396b708ddb7fc10fb73c4e3a9258cd9c3f6f761b2c227853b5def228c85dd973
24907c8e1151cae5dfb583a99ec1d74d93166e6ee5089137e7a924a11d60137b6e10b a1ac59244f674da4a1c057961886661bd29e0c1346f0fcf75bf1c78d4781815c2f9f6
ed0e73a37ea9617af60b3d295d6bd0761c2acdaae6a88614bd10f2ce5cd5dcec97b3e f2f9fe0e370b256f6e82fb2e14c7ffc374d42caf26abf13dca169a6faafd5cff8baa9
bdd1508e8aebdb48f86bec27dc21aaf6e0aaf67cd44b62a28b4377b4dee4634922bfb 717090bc1fc5e1ba56acb93492d1a8b789f33ff29b6004c4be9a755ff590d7d00d6e8
7b30550fafd287d00c3a2776f4c31f5a568b2d564cc9281b9cb2fea2ec45feba2d7f0 893e7e54e639aebf69d18f2182a9bb0f2e1c27c81ba73fa57f7ef652185f89114109f
0fe1f595647a515b8f7178139f668720de10c8e5d41169a79f7809d120ebe8b156855 5a61cc8c9216fdd7398246bb7a0c20e2fbca2d8ae070cdffe5bb4b1c373e71be8e7d8
4ebc9cf4b85454bdfca44cb1d4552871e0ffb1373ee9675eb495049b7d24c33691d9b f356ee5de37881533f10397bcd84d35445401c619d464ab3a134c71da4d9874f2f736
150406e646d1f6f2911bf5c665bcb71e3649264af8bc6a2c78acb503cd838ae3e5e37 189b8bbb659c28f8db25a58b9f089272132e3091efa87d6b07d10321ba464047be011
15df02d19dd4ddfbca9e4f46b0a0e2d000f5e02b66c4e83b9933b4ff5dc10a8e5e064 3e91514aba299fd1553bcebb
3cbf26f738bca0d25b0db9740dcbabbeb23e54c59deec56dab96bc96daa727aa0b730 KE3: c4a0d5b8148f3ac0f8611b38de38bda085d4eb00d561397ae59676f36dc705be
bf81e15d82641f49bc5d4c998c2bd0416f27de631f445c93e43522f 1c939e7bfdd7301103af5eb164bdfb70298aab889bd2ac797e419a82bfb442e6
KE3: 01be33d72a3d430b0bf3d5e3ef3450652c23354e818460b315a644635882899f export_key: 6b50ae4dba956930c0465b4a26c3cee58e05afcab623c1c254ae34acc
4f6746efabb3da6a5a8a645bbeba96d32d133e8d6e0ec13e96af61c74fe814f5 38babf954530a53475672ff46a1cf7fd53ef9e808f85b08793d021bb5c6d2a1bb9204
export_key: 3d4d4d6c586ef538d08756f5a1a04cc190aee59e8dea997d0aaff89a8 f6
da58ac712fb5002666d8fb09b18a4f46a54a858592dc3c0b683a37a06d5a29728349b session_key: c9bc2b7e2237f6fbeccd92dc6ec6d51faeb886492f8d23f21743a967
91 597025215df02a4afb75349acbafeef9dfd4f19e6d38da8bea4912f7b691b70849b0d
session_key: eaa0057dde12425c05b185fc490579987638a9a2d56dece67db5acc3 78e
b526241174635a7ffff2c2ad6359a82eff279cc23c8f6768dcbffc801420fbfd6f147 ~~~
ae2
"#, "#,
]; ];
static FAKE_TEST_VECTORS: &[&str] = &[r#"
### OPAQUE-3DH Fake Test Vector 1
#### Configuration
~~~
OPRF: 0001
Hash: SHA512
MHF: Identity
KDF: HKDF-SHA512
MAC: HMAC-SHA512
EnvelopeMode: 01
Group: ristretto255
Context: 4f50415155452d504f43
Nh: 64
Npk: 32
Nsk: 32
Nm: 64
Nx: 64
Nok: 32
~~~
#### Input Values
~~~
client_identity: 616c696365
server_identity: 626f62
oprf_seed: d3cb00535339fe4063c7ba5506a990c243a2b5c77b06848a0be9a0568c
252fb0d7425382babd267deeed669e56d1d5654c036211f49b42f4489f96f37100779
f
credential_identifier: 31323334
masking_nonce: 3058799f42516228746821dc8c8530d0e8273ebde81941591d69ca
5aea773090
client_private_key: 83c9bcc31a9da0ffa4489900d3d1f85bb65c27f26e9ae4e3b
66f6e02e098c503
client_public_key: 56717b74a5e1770edb14c65f22cee0487046bd96e122ba97da
ffed06c4bf4052
server_private_key: 8d3a9355f9757e7071b3f836e3fb1461a6436e92971625b17
cd7e580dd27c009
server_public_key: 7a464761cb19c8b6e832fdfcfd18779b0edc246fe808f5de6c
e7bdb54df41b67
server_nonce: 4e2a8098173efa2968036f1762f2e5df41ab976fb1bfb91dae29950
f8526de4c
server_keyshare: 0e247410004d83d7cbe3af89c62ff03f942127aec4b0084c9eb5
88e74ce6dd06
server_private_keyshare: 326345820acc8aacf4948fce775a1fd265e4e93fd579
cec8177d6389ee379b0a
masking_key: e968bfe56ad934c3e1088115bcbf1af8b405fd0de94cdf301f9192cc
2781de00617e568b14b7235cc1189265811ea354031ea39b62e31a104f181c01d3dae
4b8
KE1: 480b6c0066c9320c50dce20f8b6b63e4ded7681defd9da3f70ecdc15770f9e68
05603c1acb64ea417c0dabaab858a5f9da046d4a0cdbf092034c00451ccdc6e1ee835
5c91d5ed7aa5ea75b8a730ba8dc45f6b41ae9713e6aa7126211346e8754
~~~
#### Output Values
~~~
KE2: 04013bca360b4b9ba95b2f494927375e0f234dac23053822e466a9738f781522
3058799f42516228746821dc8c8530d0e8273ebde81941591d69ca5aea77309078577
13efdc95f69166737cd7a80ead60e1a1f805c1da9cccbc0d29120f34be291518798c7
00793f232374e66182495b76b388d9e11f479580cc2297da02fecee88a99cea6bc411
b9467e8bfa9a4006aba7f21b74b4ce3bccd686785878b0ec9b3fc4200228014d5d073
69d42d1d1b1669ecd2ad8905734ca0a641d8f16667ca4e2a8098173efa2968036f176
2f2e5df41ab976fb1bfb91dae29950f8526de4c0e247410004d83d7cbe3af89c62ff0
3f942127aec4b0084c9eb588e74ce6dd06fb1a0fd81da51bc1d87c740c186d881ed79
71fdba5ad1d5cfc94ffe6a731241c78ea7ea5dae503e987edc37355b7348883dc65cd
b57aec04e64593007f98a405
~~~
"#];
macro_rules! parse { macro_rules! parse {
( $v:ident, $s:expr ) => { ( $v:ident, $s:expr ) => {
parse_default!($v, $s, vec![])
};
}
macro_rules! parse_default {
( $v:ident, $s:expr, $d:expr ) => {
match decode(&$v, $s) { match decode(&$v, $s) {
Some(x) => x, Some(x) => x,
None => vec![], None => $d,
} }
}; };
} }
@@ -317,7 +421,11 @@ fn rfc_to_json(input: &str) -> String {
json.push(format!(" \"{}\": \"{}", key, val)); json.push(format!(" \"{}\": \"{}", key, val));
} else { } else {
let s = line.trim().to_string(); let s = line.trim().to_string();
if !s.is_empty() { if s.contains("~") || s.contains("#") {
// Ignore comment lines
continue;
}
if s.len() > 0 {
json.push(s); json.push(s);
} }
} }
@@ -334,21 +442,27 @@ fn decode(values: &Value, key: &str) -> Option<Vec<u8>> {
fn populate_test_vectors(values: &Value) -> TestVectorParameters { fn populate_test_vectors(values: &Value) -> TestVectorParameters {
TestVectorParameters { TestVectorParameters {
dummy_private_key: parse_default!(
values,
"client_private_key",
vec![0u8; <PrivateKey as SizedBytes>::Len::to_usize()]
),
dummy_masking_key: parse_default!(values, "masking_key", vec![0u8; 64]),
context: parse!(values, "Context"),
envelope_mode: match values["EnvelopeMode"].as_str() { envelope_mode: match values["EnvelopeMode"].as_str() {
Some("01") => EnvelopeMode::Base, Some("01") => EnvelopeMode::Base,
Some("02") => EnvelopeMode::CustomIdentifier, Some("02") => EnvelopeMode::CustomIdentifier,
_ => panic!("Could not match envelope mode"), _ => panic!("Could not match envelope mode"),
}, },
client_public_key: parse!(values, "client_public_key"), client_private_key: decode(values, "client_private_key"),
client_private_key: parse!(values, "client_private_key"),
client_keyshare: parse!(values, "client_keyshare"), client_keyshare: parse!(values, "client_keyshare"),
client_private_keyshare: parse!(values, "client_private_keyshare"), client_private_keyshare: parse!(values, "client_private_keyshare"),
server_public_key: parse!(values, "server_public_key"), server_public_key: parse!(values, "server_public_key"),
server_private_key: parse!(values, "server_private_key"), server_private_key: parse!(values, "server_private_key"),
server_keyshare: parse!(values, "server_keyshare"), server_keyshare: parse!(values, "server_keyshare"),
server_private_keyshare: parse!(values, "server_private_keyshare"), server_private_keyshare: parse!(values, "server_private_keyshare"),
client_identity: parse!(values, "client_identity"), client_identity: decode(values, "client_identity"),
server_identity: parse!(values, "server_identity"), server_identity: decode(values, "server_identity"),
credential_identifier: parse!(values, "credential_identifier"), credential_identifier: parse!(values, "credential_identifier"),
password: parse!(values, "password"), password: parse!(values, "password"),
blind_registration: parse!(values, "blind_registration"), blind_registration: parse!(values, "blind_registration"),
@@ -379,6 +493,18 @@ fn get_password_file_bytes(parameters: &TestVectorParameters) -> Result<Vec<u8>,
Ok(password_file.serialize()) Ok(password_file.serialize())
} }
fn parse_identifiers(
client_identity: Option<Vec<u8>>,
server_identity: Option<Vec<u8>>,
) -> Option<Identifiers> {
match (client_identity, server_identity) {
(None, None) => None,
(Some(x), None) => Some(Identifiers::ClientIdentifier(x)),
(None, Some(y)) => Some(Identifiers::ServerIdentifier(y)),
(Some(x), Some(y)) => Some(Identifiers::ClientAndServerIdentifiers(x, y)),
}
}
#[test] #[test]
fn test_registration_request() -> Result<(), ProtocolError> { fn test_registration_request() -> Result<(), ProtocolError> {
for parameters in rfc_to_params!(TEST_VECTORS) { for parameters in rfc_to_params!(TEST_VECTORS) {
@@ -403,6 +529,7 @@ fn test_registration_response() -> Result<(), ProtocolError> {
&[ &[
&parameters.oprf_seed[..], &parameters.oprf_seed[..],
&parameters.server_private_key[..], &parameters.server_private_key[..],
&parameters.dummy_private_key[..],
] ]
.concat(), .concat(),
)?; )?;
@@ -430,19 +557,13 @@ fn test_registration_upload() -> Result<(), ProtocolError> {
&parameters.password, &parameters.password,
)?; )?;
let sk_u_and_nonce: Vec<u8> = let mut finish_registration_rng = CycleRng::new(parameters.envelope_nonce);
[parameters.client_private_key, parameters.envelope_nonce].concat();
let mut finish_registration_rng = CycleRng::new(sk_u_and_nonce);
let result = client_registration_start_result.state.finish( let result = client_registration_start_result.state.finish(
&mut finish_registration_rng, &mut finish_registration_rng,
RegistrationResponse::deserialize(&parameters.registration_response[..]).unwrap(), RegistrationResponse::deserialize(&parameters.registration_response[..]).unwrap(),
if parameters.envelope_mode == EnvelopeMode::CustomIdentifier { match parse_identifiers(parameters.client_identity, parameters.server_identity) {
ClientRegistrationFinishParameters::WithIdentifiers( None => ClientRegistrationFinishParameters::Default,
parameters.client_identity, Some(ids) => ClientRegistrationFinishParameters::WithIdentifiers(ids),
parameters.server_identity,
)
} else {
ClientRegistrationFinishParameters::default()
}, },
)?; )?;
@@ -472,7 +593,6 @@ fn test_ke1() -> Result<(), ProtocolError> {
let client_login_start_result = ClientLogin::<Ristretto255Sha512NoSlowHash>::start( let client_login_start_result = ClientLogin::<Ristretto255Sha512NoSlowHash>::start(
&mut client_login_start_rng, &mut client_login_start_rng,
&parameters.password, &parameters.password,
ClientLoginStartParameters::WithInfo(parameters.client_info),
)?; )?;
assert_eq!( assert_eq!(
hex::encode(&parameters.KE1), hex::encode(&parameters.KE1),
@@ -489,10 +609,14 @@ fn test_ke2() -> Result<(), ProtocolError> {
&[ &[
&parameters.oprf_seed[..], &parameters.oprf_seed[..],
&parameters.server_private_key[..], &parameters.server_private_key[..],
&parameters.dummy_private_key[..],
] ]
.concat(), .concat(),
)?; )?;
let password_file_bytes = get_password_file_bytes(&parameters)?;
let record = ServerRegistration::<Ristretto255Sha512NoSlowHash>::deserialize(
&get_password_file_bytes(&parameters)?[..],
)?;
let mut server_private_keyshare_and_nonce_rng = CycleRng::new( let mut server_private_keyshare_and_nonce_rng = CycleRng::new(
[ [
@@ -505,24 +629,18 @@ fn test_ke2() -> Result<(), ProtocolError> {
let server_login_start_result = ServerLogin::<Ristretto255Sha512NoSlowHash>::start( let server_login_start_result = ServerLogin::<Ristretto255Sha512NoSlowHash>::start(
&mut server_private_keyshare_and_nonce_rng, &mut server_private_keyshare_and_nonce_rng,
&server_setup, &server_setup,
Some(ServerRegistration::deserialize(&password_file_bytes[..]).unwrap()), Some(record),
CredentialRequest::<Ristretto255Sha512NoSlowHash>::deserialize(&parameters.KE1[..]) CredentialRequest::<Ristretto255Sha512NoSlowHash>::deserialize(&parameters.KE1[..])
.unwrap(), .unwrap(),
&parameters.credential_identifier, &parameters.credential_identifier,
if parameters.envelope_mode == EnvelopeMode::CustomIdentifier { match parse_identifiers(parameters.client_identity, parameters.server_identity) {
ServerLoginStartParameters::WithInfoAndIdentifiers( None => ServerLoginStartParameters::WithContext(parameters.context.to_vec()),
parameters.server_info.to_vec(), Some(ids) => ServerLoginStartParameters::WithContextAndIdentifiers(
parameters.client_identity, parameters.context.to_vec(),
parameters.server_identity, ids,
) ),
} else {
ServerLoginStartParameters::WithInfo(parameters.server_info.to_vec())
}, },
)?; )?;
assert_eq!(
hex::encode(&parameters.client_info),
hex::encode(server_login_start_result.plain_info),
);
assert_eq!( assert_eq!(
hex::encode(&parameters.KE2), hex::encode(&parameters.KE2),
hex::encode(server_login_start_result.message.serialize()) hex::encode(server_login_start_result.message.serialize())
@@ -544,25 +662,18 @@ fn test_ke3() -> Result<(), ProtocolError> {
let client_login_start_result = ClientLogin::<Ristretto255Sha512NoSlowHash>::start( let client_login_start_result = ClientLogin::<Ristretto255Sha512NoSlowHash>::start(
&mut client_login_start_rng, &mut client_login_start_rng,
&parameters.password, &parameters.password,
ClientLoginStartParameters::WithInfo(parameters.client_info),
)?; )?;
let client_login_finish_result = client_login_start_result.state.finish( let client_login_finish_result = client_login_start_result.state.finish(
CredentialResponse::<Ristretto255Sha512NoSlowHash>::deserialize(&parameters.KE2[..])?, CredentialResponse::<Ristretto255Sha512NoSlowHash>::deserialize(&parameters.KE2[..])?,
if parameters.envelope_mode == EnvelopeMode::CustomIdentifier { match parse_identifiers(parameters.client_identity, parameters.server_identity) {
ClientLoginFinishParameters::WithIdentifiers( None => ClientLoginFinishParameters::WithContext(parameters.context),
parameters.client_identity, Some(ids) => {
parameters.server_identity, ClientLoginFinishParameters::WithContextAndIdentifiers(parameters.context, ids)
) }
} else {
ClientLoginFinishParameters::default()
}, },
)?; )?;
assert_eq!(
hex::encode(&parameters.server_info),
hex::encode(&client_login_finish_result.confidential_info)
);
assert_eq!( assert_eq!(
hex::encode(&parameters.session_key), hex::encode(&parameters.session_key),
hex::encode(&client_login_finish_result.session_key) hex::encode(&client_login_finish_result.session_key)
@@ -586,10 +697,14 @@ fn test_server_login_finish() -> Result<(), ProtocolError> {
&[ &[
&parameters.oprf_seed[..], &parameters.oprf_seed[..],
&parameters.server_private_key[..], &parameters.server_private_key[..],
&parameters.dummy_private_key[..],
] ]
.concat(), .concat(),
)?; )?;
let password_file_bytes = get_password_file_bytes(&parameters)?;
let record = ServerRegistration::<Ristretto255Sha512NoSlowHash>::deserialize(
&get_password_file_bytes(&parameters)?[..],
)?;
let mut server_private_keyshare_and_nonce_rng = CycleRng::new( let mut server_private_keyshare_and_nonce_rng = CycleRng::new(
[ [
@@ -602,18 +717,16 @@ fn test_server_login_finish() -> Result<(), ProtocolError> {
let server_login_start_result = ServerLogin::<Ristretto255Sha512NoSlowHash>::start( let server_login_start_result = ServerLogin::<Ristretto255Sha512NoSlowHash>::start(
&mut server_private_keyshare_and_nonce_rng, &mut server_private_keyshare_and_nonce_rng,
&server_setup, &server_setup,
Some(ServerRegistration::deserialize(&password_file_bytes[..]).unwrap()), Some(record),
CredentialRequest::<Ristretto255Sha512NoSlowHash>::deserialize(&parameters.KE1[..]) CredentialRequest::<Ristretto255Sha512NoSlowHash>::deserialize(&parameters.KE1[..])
.unwrap(), .unwrap(),
&parameters.credential_identifier, &parameters.credential_identifier,
if parameters.envelope_mode == EnvelopeMode::CustomIdentifier { match parse_identifiers(parameters.client_identity, parameters.server_identity) {
ServerLoginStartParameters::WithInfoAndIdentifiers( None => ServerLoginStartParameters::WithContext(parameters.context.to_vec()),
parameters.server_info.to_vec(), Some(ids) => ServerLoginStartParameters::WithContextAndIdentifiers(
parameters.client_identity, parameters.context.to_vec(),
parameters.server_identity, ids,
) ),
} else {
ServerLoginStartParameters::WithInfo(parameters.server_info.to_vec())
}, },
)?; )?;
@@ -628,3 +741,47 @@ fn test_server_login_finish() -> Result<(), ProtocolError> {
} }
Ok(()) Ok(())
} }
#[test]
fn test_fake_vectors() -> Result<(), ProtocolError> {
for parameters in rfc_to_params!(FAKE_TEST_VECTORS) {
let server_setup = ServerSetup::<Ristretto255Sha512NoSlowHash>::deserialize(
&[
&parameters.oprf_seed[..],
&parameters.server_private_key[..],
&parameters.dummy_private_key[..],
]
.concat(),
)?;
let mut server_private_keyshare_and_nonce_rng = CycleRng::new(
[
parameters.dummy_masking_key,
parameters.masking_nonce,
parameters.server_private_keyshare,
parameters.server_nonce,
]
.concat(),
);
let server_login_start_result = ServerLogin::<Ristretto255Sha512NoSlowHash>::start(
&mut server_private_keyshare_and_nonce_rng,
&server_setup,
None,
CredentialRequest::<Ristretto255Sha512NoSlowHash>::deserialize(&parameters.KE1[..])
.unwrap(),
&parameters.credential_identifier,
match parse_identifiers(parameters.client_identity, parameters.server_identity) {
None => ServerLoginStartParameters::WithContext(parameters.context.to_vec()),
Some(ids) => ServerLoginStartParameters::WithContextAndIdentifiers(
parameters.context.to_vec(),
ids,
),
},
)?;
assert_eq!(
hex::encode(&parameters.KE2),
hex::encode(server_login_start_result.message.serialize())
);
}
Ok(())
}