Adding client enumeration mitigations (#153)

This commit is contained in:
Kevin Lewi
2021-06-21 01:29:39 -07:00
committed by Kevin Lewi
parent 98f1821897
commit f0c13945d1
14 changed files with 895 additions and 901 deletions
Generated
+1 -1
View File
@@ -567,7 +567,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "opaque-ke"
version = "0.5.1-pre.1"
version = "0.6.0-pre.1"
dependencies = [
"anyhow",
"base64",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "opaque-ke"
version = "0.5.1-pre.1"
version = "0.6.0-pre.1"
repository = "https://github.com/novifinancial/opaque-ke"
keywords = ["cryptography", "crypto", "opaque", "passwords", "authentication"]
description = "An implementation of the OPAQUE password-authenticated key exchange protocol"
+23 -16
View File
@@ -36,7 +36,7 @@ use opaque_ke::{
ClientLogin, ClientLoginFinishParameters, ClientLoginStartParameters, ClientRegistration,
ClientRegistrationFinishParameters, CredentialFinalization, CredentialRequest,
CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin,
ServerLoginStartParameters, ServerRegistration,
ServerLoginStartParameters, ServerRegistration, ServerSetup,
};
// The ciphersuite trait allows to specify the underlying primitives
@@ -81,7 +81,8 @@ fn decrypt(key: &[u8], ciphertext: &[u8]) -> Vec<u8> {
// Password-based registration and encryption of client secret message between a client and server
fn register_locker(
server_kp: &opaque_ke::keypair::KeyPair<curve25519_dalek::ristretto::RistrettoPoint>,
server_setup: &ServerSetup<Default>,
locker_id: usize,
password: String,
secret_message: String,
) -> Locker {
@@ -91,12 +92,10 @@ fn register_locker(
let registration_request_bytes = client_registration_start_result.message.serialize();
// Client sends registration_request_bytes to server
let mut server_rng = OsRng;
let server_registration_start_result = ServerRegistration::<Default>::start(
&mut server_rng,
&server_setup,
RegistrationRequest::deserialize(&registration_request_bytes[..]).unwrap(),
server_kp.public(),
&locker_id.to_be_bytes(),
)
.unwrap();
let registration_response_bytes = server_registration_start_result.message.serialize();
@@ -121,10 +120,9 @@ fn register_locker(
// Client sends message_bytes to server
let password_file = server_registration_start_result
.state
.finish(RegistrationUpload::deserialize(&message_bytes[..]).unwrap())
.unwrap();
let password_file = ServerRegistration::finish(
RegistrationUpload::<Default>::deserialize(&message_bytes[..]).unwrap(),
);
Locker {
contents: ciphertext,
@@ -134,7 +132,8 @@ fn register_locker(
// Open the contents of a locker with a password between a client and server
fn open_locker(
server_kp: &opaque_ke::keypair::KeyPair<curve25519_dalek::ristretto::RistrettoPoint>,
server_setup: &ServerSetup<Default>,
locker_id: usize,
password: String,
locker: &Locker,
) -> Result<String, String> {
@@ -154,9 +153,10 @@ fn open_locker(
let mut server_rng = OsRng;
let server_login_start_result = ServerLogin::start(
&mut server_rng,
password_file,
&server_kp.private(),
&server_setup,
Some(password_file),
CredentialRequest::deserialize(&credential_request_bytes[..]).unwrap(),
&locker_id.to_be_bytes(),
ServerLoginStartParameters::default(),
)
.unwrap();
@@ -200,7 +200,7 @@ fn open_locker(
fn main() {
let mut rng = OsRng;
let server_kp = Default::generate_random_keypair(&mut rng);
let server_setup = ServerSetup::<Default>::new(&mut rng);
let mut rl = Editor::<()>::new();
let mut registered_lockers: Vec<Locker> = vec![];
@@ -225,8 +225,10 @@ fn main() {
&mut rl,
None,
);
let locker_id = registered_lockers.len();
registered_lockers.push(register_locker(
&server_kp,
&server_setup,
locker_id,
password,
secret_message,
));
@@ -252,7 +254,12 @@ fn main() {
continue;
}
match open_locker(&server_kp, password, &registered_lockers[locker_index]) {
match open_locker(
&server_setup,
locker_index,
password,
&registered_lockers[locker_index],
) {
Ok(contents) => {
println!("\n\nSuccess! Contents: {}\n\n", contents);
}
+20 -16
View File
@@ -30,7 +30,7 @@ use opaque_ke::{
ClientLoginStartParameters, ClientRegistration, ClientRegistrationFinishParameters,
CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest,
RegistrationResponse, RegistrationUpload, ServerLogin, ServerLoginStartParameters,
ServerRegistration,
ServerRegistration, ServerSetup,
};
// The ciphersuite trait allows to specify the underlying primitives
@@ -46,7 +46,8 @@ impl CipherSuite for Default {
// Password-based registration between a client and server
fn account_registration(
server_kp: &opaque_ke::keypair::KeyPair<curve25519_dalek::ristretto::RistrettoPoint>,
server_setup: &ServerSetup<Default>,
username: String,
password: String,
) -> Vec<u8> {
let mut client_rng = OsRng;
@@ -56,11 +57,10 @@ fn account_registration(
// Client sends registration_request_bytes to server
let mut server_rng = OsRng;
let server_registration_start_result = ServerRegistration::<Default>::start(
&mut server_rng,
&server_setup,
RegistrationRequest::deserialize(&registration_request_bytes[..]).unwrap(),
server_kp.public(),
username.as_bytes(),
)
.unwrap();
let registration_response_bytes = server_registration_start_result.message.serialize();
@@ -79,16 +79,16 @@ fn account_registration(
// Client sends message_bytes to server
let password_file = server_registration_start_result
.state
.finish(RegistrationUpload::deserialize(&message_bytes[..]).unwrap())
.unwrap();
let password_file = ServerRegistration::finish(
RegistrationUpload::<Default>::deserialize(&message_bytes[..]).unwrap(),
);
password_file.serialize()
}
// Password-based login between a client and server
fn account_login(
server_kp: &opaque_ke::keypair::KeyPair<curve25519_dalek::ristretto::RistrettoPoint>,
server_setup: &ServerSetup<Default>,
username: String,
password: String,
password_file_bytes: &[u8],
) -> bool {
@@ -107,9 +107,10 @@ fn account_login(
let mut server_rng = OsRng;
let server_login_start_result = ServerLogin::start(
&mut server_rng,
password_file,
&server_kp.private(),
&server_setup,
Some(password_file),
CredentialRequest::deserialize(&credential_request_bytes[..]).unwrap(),
username.as_bytes(),
ServerLoginStartParameters::default(),
)
.unwrap();
@@ -141,7 +142,7 @@ fn account_login(
fn main() {
let mut rng = OsRng;
let server_kp = Default::generate_random_keypair(&mut rng);
let server_setup = ServerSetup::<Default>::new(&mut rng);
let mut rl = Editor::<()>::new();
let mut registered_users = HashMap::<String, Vec<u8>>::new();
@@ -164,13 +165,16 @@ fn main() {
let (username, password) = get_two_strings("Username", "Password", &mut rl, None);
match line.as_ref() {
"1" => {
registered_users
.insert(username, account_registration(&server_kp, password));
registered_users.insert(
username.clone(),
account_registration(&server_setup, username, password),
);
continue;
}
"2" => match registered_users.get(&username) {
Some(password_file_bytes) => {
if account_login(&server_kp, password, password_file_bytes) {
if account_login(&server_setup, username, password, password_file_bytes)
{
println!("\nLogin success!");
} else {
// Note that at this point, the client knows whether or not the login
+2 -9
View File
@@ -6,13 +6,11 @@
//! Defines the CipherSuite trait to specify the underlying primitives for OPAQUE
use crate::{
hash::Hash, key_exchange::traits::KeyExchange, keypair::KeyPair,
map_to_curve::GroupWithMapToCurve, slow_hash::SlowHash,
hash::Hash, key_exchange::traits::KeyExchange, map_to_curve::GroupWithMapToCurve,
slow_hash::SlowHash,
};
use digest::Digest;
use rand::{CryptoRng, RngCore};
/// Configures the underlying primitives used in OPAQUE
/// * `Group`: a finite cyclic group along with a point representation, along
/// with an extension trait PasswordToCurve that allows some customization on
@@ -33,9 +31,4 @@ pub trait CipherSuite {
type Hash: Hash;
/// A slow hashing function, typically used for password hashing
type SlowHash: SlowHash<Self::Hash>;
/// Generating a random key pair given a cryptographic rng
fn generate_random_keypair<R: RngCore + CryptoRng>(rng: &mut R) -> KeyPair<Self::Group> {
KeyPair::<Self::Group>::generate_random(rng)
}
}
+47 -40
View File
@@ -4,7 +4,7 @@
// LICENSE file in the root directory of this source tree.
use crate::{
errors::{utils::check_slice_size_atleast, InternalPakeError, PakeError, ProtocolError},
errors::{utils::check_slice_size, InternalPakeError, PakeError, ProtocolError},
hash::Hash,
keypair::PublicKey,
serialization::serialize,
@@ -28,6 +28,7 @@ const NONCE_LEN: usize = 32;
#[derive(Clone, Copy, PartialEq, Zeroize)]
#[zeroize(drop)]
pub(crate) enum InnerEnvelopeMode {
Unused = 0,
Base = 1,
CustomIdentifier = 2,
}
@@ -131,44 +132,41 @@ impl<D: Hash> Envelope<D> {
<D as Digest>::OutputSize::to_usize()
}
pub(crate) fn len() -> usize {
1 + <PublicKey as SizedBytes>::Len::to_usize() + <D as Digest>::OutputSize::to_usize() + NONCE_LEN
}
pub(crate) fn get_mode(&self) -> InnerEnvelopeMode {
self.inner_envelope.mode
}
/// The format of the output is:
/// mode | nonce | ciphertext | hmac
/// u8 | nonce_size bytes | variable length | hmac_size bytes
pub(crate) fn from_bytes(bytes: &[u8]) -> Result<Self, InternalPakeError> {
let (result, remainder) = Self::deserialize(bytes)
.map_err(|_| InternalPakeError::InvalidEnvelopeStructureError)?;
if !remainder.is_empty() {
return Err(InternalPakeError::InvalidEnvelopeStructureError);
}
Ok(result)
}
pub(crate) fn to_bytes(&self) -> Vec<u8> {
self.serialize()
}
pub(crate) fn serialize(&self) -> Vec<u8> {
[&self.inner_envelope.serialize(), &self.hmac[..]].concat()
}
pub(crate) fn deserialize(input: &[u8]) -> Result<(Self, Vec<u8>), ProtocolError> {
let (inner_envelope, remainder) = InnerEnvelope::deserialize(input)?;
pub(crate) fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let (inner_envelope, remainder) = InnerEnvelope::deserialize(input)
.map_err(|_| ProtocolError::InvalidInnerEnvelopeError)?;
let hmac_key_size = Self::hmac_key_size();
let hmac_and_remainder =
check_slice_size_atleast(&remainder, hmac_key_size, "hmac_key_size")?;
let hmac = check_slice_size(&remainder, hmac_key_size, "hmac_key_size")?;
Ok((
Self {
inner_envelope,
hmac: GenericArray::clone_from_slice(&hmac_and_remainder[..hmac_key_size]),
Ok(Self {
inner_envelope,
hmac: GenericArray::clone_from_slice(&hmac),
})
}
// Creates a dummy envelope object that serializes to the all-zeros byte string
pub(crate) fn dummy() -> Self {
Self {
inner_envelope: InnerEnvelope {
mode: InnerEnvelopeMode::Unused,
nonce: vec![0u8; NONCE_LEN],
ciphertext: vec![0u8; <PublicKey as SizedBytes>::Len::to_usize()],
},
hmac_and_remainder[hmac_key_size..].to_vec(),
))
hmac: GenericArray::clone_from_slice(&vec![0u8; <D as Digest>::OutputSize::to_usize()]),
}
}
pub(crate) fn seal<R: RngCore + CryptoRng>(
@@ -194,16 +192,16 @@ impl<D: Hash> Envelope<D> {
let mut nonce = vec![0u8; NONCE_LEN];
rng.fill_bytes(&mut nonce);
let h = Hkdf::<D>::new(Some(&nonce), key);
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 export_key = vec![0u8; Self::export_key_size()];
h.expand(STR_PAD, &mut xor_key)
h.expand(&[&nonce, STR_PAD].concat(), &mut xor_key)
.map_err(|_| InternalPakeError::HkdfError)?;
h.expand(STR_AUTH_KEY, &mut hmac_key)
h.expand(&[&nonce, STR_AUTH_KEY].concat(), &mut hmac_key)
.map_err(|_| InternalPakeError::HkdfError)?;
h.expand(STR_EXPORT_KEY, &mut export_key)
h.expand(&[&nonce, STR_EXPORT_KEY].concat(), &mut export_key)
.map_err(|_| InternalPakeError::HkdfError)?;
let ciphertext: Vec<u8> = xor_key
@@ -266,17 +264,26 @@ impl<D: Hash> Envelope<D> {
key: &[u8],
aad: &[u8],
) -> Result<OpenedInnerEnvelope<D>, InternalPakeError> {
let h = Hkdf::<D>::new(Some(&self.inner_envelope.nonce), key);
let h = Hkdf::<D>::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 export_key = vec![0u8; Self::export_key_size()];
h.expand(STR_PAD, &mut xor_key)
.map_err(|_| InternalPakeError::HkdfError)?;
h.expand(STR_AUTH_KEY, &mut hmac_key)
.map_err(|_| InternalPakeError::HkdfError)?;
h.expand(STR_EXPORT_KEY, &mut export_key)
.map_err(|_| InternalPakeError::HkdfError)?;
h.expand(
&[&self.inner_envelope.nonce, STR_PAD].concat(),
&mut xor_key,
)
.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 =
Hmac::<D>::new_varkey(&hmac_key).map_err(|_| InternalPakeError::HmacError)?;
@@ -354,7 +361,7 @@ mod tests {
let mut msg = [0u8; 100];
rng.fill_bytes(&mut msg);
let (envelope, export_key_1) = Envelope::<sha2::Sha256>::seal_raw(
let (envelope, export_key) = Envelope::<sha2::Sha256>::seal_raw(
&mut rng,
&key,
&msg,
@@ -364,6 +371,6 @@ mod tests {
.unwrap();
let opened_envelope = envelope.open_raw(&key, b"aad").unwrap();
assert_eq!(&msg.to_vec(), &opened_envelope.plaintext);
assert_eq!(&export_key_1.to_vec(), &opened_envelope.export_key.to_vec());
assert_eq!(&export_key.to_vec(), &opened_envelope.export_key.to_vec());
}
}
+2
View File
@@ -92,6 +92,8 @@ pub enum ProtocolError {
///
/// Internal error during password verification: {0}
VerificationError(PakeError),
/// This error occurs when the inner envelope is malformed
InvalidInnerEnvelopeError,
/// This error occurs when the server answer cannot be handled
/// Server response cannot be handled.
ServerError,
+91 -78
View File
@@ -36,10 +36,11 @@
//! which can be enabled through the `slow-hash` feature. See more details in the [features](#features) section.
//!
//! ## Setup
//! To set up the protocol, the server begins by generating a static keypair:
//! To set up the protocol, the server begins by creating a `ServerSetup` object:
//! ```
//! # use opaque_ke::errors::ProtocolError;
//! # use opaque_ke::ciphersuite::CipherSuite;
//! # use opaque_ke::ServerSetup;
//! # struct Default;
//! # impl CipherSuite for Default {
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
@@ -49,17 +50,17 @@
//! # }
//! use rand::{rngs::OsRng, RngCore};
//! let mut rng = OsRng;
//! let server_kp = Default::generate_random_keypair(&mut rng);
//! let server_setup = ServerSetup::<Default>::new(&mut rng);
//! # Ok::<(), ProtocolError>(())
//! ```
//! The server must persist this keypair for the registration and login steps, where the public component will be
//! used by the client during both registration and login, and the private component will be used by the server during login.
//! The server must persist an instance of [ServerSetup] for the registration and login steps.
//!
//! ## Registration
//! The registration protocol between the client and server consists of four steps along with three messages:
//! [RegistrationRequest], [RegistrationResponse], and [RegistrationUpload]. A successful execution of the registration protocol results in the
//! server producing a password file corresponding to the password provided by
//! the client. This password file is typically stored server-side, and retrieved upon future login attempts made by the client.
//! server producing a password file corresponding to a server-side identifier for the client, along with the password provided by
//! the client. This password file is typically stored in a key-value database, where the keys consist of these server-side identifiers for each client,
//! and the values consist of their corresponding password files, to be retrieved upon future login attempts made by the client.
//!
//! ### Client Registration Start
//! In the first step of registration, the client chooses as input a registration password. The client runs [ClientRegistration::start]
@@ -90,15 +91,15 @@
//! ```
//!
//! ### Server Registration Start
//! In the second step of registration, the server takes as input the instance of [RegistrationRequest] from the client, and
//! the server's public key `server_kp.public()`.
//! The server runs [ServerRegistration::start] to produce an a [ServerRegistrationStartResult], which consists of
//! a [RegistrationResponse] to be returned to the client and
//! a [ServerRegistration] which must be persisted on the server for the final step of server registration.
//! In the second step of registration, the server takes as input a persisted instance of [ServerSetup], a [RegistrationRequest] from the client, and
//! a server-side identifier for the client.
//! The server runs [ServerRegistration::start] to produce a [ServerRegistrationStartResult], which consists of
//! a [RegistrationResponse] to be returned to the client.
//! ```
//! # use opaque_ke::{
//! # errors::ProtocolError,
//! # ClientRegistration,
//! # ServerSetup,
//! # slow_hash::NoOpHash,
//! # };
//! # use opaque_ke::ciphersuite::CipherSuite;
@@ -116,12 +117,12 @@
//! # b"password",
//! # )?;
//! use opaque_ke::ServerRegistration;
//! let mut server_rng = OsRng;
//! let server_kp = Default::generate_random_keypair(&mut server_rng);
//! # let mut server_rng = OsRng;
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! let server_registration_start_result = ServerRegistration::<Default>::start(
//! &mut server_rng,
//! &server_setup,
//! client_registration_start_result.message,
//! server_kp.public(),
//! b"[email protected]",
//! )?;
//! # Ok::<(), ProtocolError>(())
//! ```
@@ -135,7 +136,7 @@
//! ```
//! # use opaque_ke::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ServerSetup,
//! # slow_hash::NoOpHash,
//! # };
//! # use opaque_ke::ciphersuite::CipherSuite;
@@ -153,8 +154,8 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_kp = Default::generate_random_keypair(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
//! # 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 client_registration_finish_result = client_registration_start_result.state.finish(
//! &mut client_rng,
//! server_registration_start_result.message,
@@ -173,7 +174,7 @@
//! ```
//! # use opaque_ke::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ServerSetup,
//! # slow_hash::NoOpHash,
//! # };
//! # use opaque_ke::ciphersuite::CipherSuite;
@@ -191,12 +192,12 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_kp = Default::generate_random_keypair(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
//! # 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 client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
//! let password_file = server_registration_start_result.state.finish(
//! let password_file = ServerRegistration::<Default>::finish(
//! client_registration_finish_result.message,
//! )?;
//! );
//! # Ok::<(), ProtocolError>(())
//! ```
//!
@@ -238,16 +239,17 @@
//!
//! ### Server Login Start
//! In the second step of login, the server takes as input
//! a [CredentialRequest] from the client,
//! the server's private key `server_kp.private()`, and
//! the password file output from registration.
//! a persisted instance of [ServerSetup],
//! the password file output from registration,
//! a [CredentialRequest] from the client, and
//! a server-side identifier for the client.
//! The server runs [ServerLogin::start] to produce an output consisting of
//! a [CredentialResponse] which is returned to the client, and
//! a [ServerLogin] which must be persisted on the server for the final step of login.
//! ```
//! # use opaque_ke::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, CredentialFinalization,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, CredentialFinalization, ServerSetup,
//! # slow_hash::NoOpHash,
//! # };
//! # use opaque_ke::ciphersuite::CipherSuite;
@@ -265,10 +267,10 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_kp = Default::generate_random_keypair(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
//! # 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 client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
//! # let password_file_bytes = server_registration_start_result.state.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(
//! # &mut client_rng,
//! # b"password",
@@ -279,13 +281,19 @@
//! let mut server_rng = OsRng;
//! let server_login_start_result = ServerLogin::start(
//! &mut server_rng,
//! password_file,
//! &server_kp.private(),
//! &server_setup,
//! Some(password_file),
//! client_login_start_result.message,
//! b"[email protected]",
//! ServerLoginStartParameters::default(),
//! )?;
//! # Ok::<(), ProtocolError>(())
//! ```
//! Note that if there is no corresponding password file found for the user,
//! the server can use `None` in place of `Some(password_file)` in order to generate
//! a [CredentialResponse] that is indistinguishable from a valid [CredentialResponse]
//! returned for a registered client. This allows the server to prevent leaking information
//! about whether or not a client has previously registered with the server.
//!
//! ### Client Login Finish
//! In the third step of login, the client takes as input a [CredentialResponse] from the server.
@@ -295,7 +303,7 @@
//! ```
//! # use opaque_ke::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup,
//! # slow_hash::NoOpHash,
//! # };
//! # use opaque_ke::ciphersuite::CipherSuite;
@@ -313,10 +321,10 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_kp = Default::generate_random_keypair(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
//! # 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 client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
//! # let password_file_bytes = server_registration_start_result.state.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(
//! # &mut client_rng,
//! # b"password",
@@ -327,7 +335,7 @@
//! # &password_file_bytes[..],
//! # )?;
//! # let server_login_start_result =
//! # ServerLogin::start(&mut server_rng, password_file, &server_kp.private(), client_login_start_result.message, ServerLoginStartParameters::default())?;
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginStartParameters::default())?;
//! let client_login_finish_result = client_login_start_result.state.finish(
//! server_login_start_result.message,
//! ClientLoginFinishParameters::default(),
@@ -341,7 +349,7 @@
//! ```
//! # use opaque_ke::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup,
//! # slow_hash::NoOpHash,
//! # };
//! # use opaque_ke::ciphersuite::CipherSuite;
@@ -359,10 +367,10 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_kp = Default::generate_random_keypair(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
//! # 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 client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
//! # let password_file_bytes = server_registration_start_result.state.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(
//! # &mut client_rng,
//! # b"password",
@@ -373,7 +381,7 @@
//! # &password_file_bytes[..],
//! # )?;
//! # let server_login_start_result =
//! # ServerLogin::start(&mut server_rng, password_file, &server_kp.private(), client_login_start_result.message, ServerLoginStartParameters::default())?;
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginStartParameters::default())?;
//! # let client_login_finish_result = client_login_start_result.state.finish(
//! # server_login_start_result.message,
//! # ClientLoginFinishParameters::default(),
@@ -416,7 +424,7 @@
//! ```
//! # use opaque_ke::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup,
//! # slow_hash::NoOpHash,
//! # };
//! # use opaque_ke::ciphersuite::CipherSuite;
@@ -435,13 +443,13 @@
//! # )?;
//! # let mut server_rng = OsRng;
//! // During setup, server generates its static keypair
//! let server_kp = Default::generate_random_keypair(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
//! 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]")?;
//!
//! // During setup or registration, the server transmits its static public key to the client
//! let server_s_pk = server_kp.public(); // obtained from the server
//! let server_s_pk = server_setup.keypair().public(); // obtained from the server
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
//! # let password_file_bytes = server_registration_start_result.state.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(
//! # &mut client_rng,
//! # b"password",
@@ -452,7 +460,7 @@
//! # &password_file_bytes[..],
//! # )?;
//! # let server_login_start_result =
//! # ServerLogin::start(&mut server_rng, password_file, &server_kp.private(), client_login_start_result.message, ServerLoginStartParameters::default())?;
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginStartParameters::default())?;
//!
//! // And then later, during login...
//! let client_login_finish_result = client_login_start_result.state.finish(
@@ -492,7 +500,7 @@
//! ```
//! # use opaque_ke::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup,
//! # slow_hash::NoOpHash,
//! # };
//! # use opaque_ke::ciphersuite::CipherSuite;
@@ -510,15 +518,15 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_kp = Default::generate_random_keypair(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
//! # 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]")?;
//! // During registration...
//! let client_registration_finish_result = client_registration_start_result.state.finish(
//! &mut client_rng,
//! server_registration_start_result.message,
//! ClientRegistrationFinishParameters::default()
//! )?;
//! # let password_file_bytes = server_registration_start_result.state.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(
//! # &mut client_rng,
//! # b"password",
@@ -529,7 +537,7 @@
//! # &password_file_bytes[..],
//! # )?;
//! # let server_login_start_result =
//! # ServerLogin::start(&mut server_rng, password_file, &server_kp.private(), client_login_start_result.message, ServerLoginStartParameters::default())?;
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginStartParameters::default())?;
//!
//! // And then later, during login...
//! let client_login_finish_result = client_login_start_result.state.finish(
@@ -546,8 +554,10 @@
//!
//! ## Custom Identifiers
//!
//! Typically when applications use OPAQUE to authenticate a client to a server, the client has a registered "username" which is sent to the server to
//! identify the corresponding password file established during registration. The server may also have an identifier corresponding to an entity (e.g. facebook.com).
//! Typically when applications use OPAQUE to authenticate a client to a server, the client has a registered username which is sent to the server to
//! identify the corresponding password file established during registration. This username may or may not coincide with the server-side identifier;
//! however, this username must be known to both the client and the server (whereas the server-side identifier does not need to be exposed to the client).
//! The server may also have an identifier corresponding to an entity (e.g. Facebook).
//! By default, neither of these public identifiers need to be supplied to the OPAQUE protocol.
//!
//! But, for applications that wish to cryptographically bind these identities to
@@ -556,7 +566,7 @@
//! ```
//! # use opaque_ke::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ServerSetup,
//! # slow_hash::NoOpHash,
//! # };
//! # use opaque_ke::ciphersuite::CipherSuite;
@@ -574,14 +584,14 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_kp = Default::generate_random_keypair(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
//! # 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 client_registration_finish_result = client_registration_start_result.state.finish(
//! &mut client_rng,
//! server_registration_start_result.message,
//! ClientRegistrationFinishParameters::WithIdentifiers(
//! b"username".to_vec(),
//! b"facebook.com".to_vec(),
//! b"Alice_the_Cryptographer".to_vec(),
//! b"Facebook".to_vec(),
//! ),
//! )?;
//! # Ok::<(), ProtocolError>(())
@@ -591,7 +601,7 @@
//! ```
//! # use opaque_ke::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, CredentialFinalization,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, CredentialFinalization, ServerSetup,
//! # slow_hash::NoOpHash,
//! # };
//! # use opaque_ke::ciphersuite::CipherSuite;
@@ -609,10 +619,10 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_kp = Default::generate_random_keypair(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::WithIdentifiers(b"username".to_vec(), b"facebook.com".to_vec()))?;
//! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.serialize();
//! # 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 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 password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
//! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng,
//! # b"password",
@@ -623,12 +633,13 @@
//! # let mut server_rng = OsRng;
//! let server_login_start_result = ServerLogin::start(
//! &mut server_rng,
//! password_file,
//! &server_kp.private(),
//! &server_setup,
//! Some(password_file),
//! client_login_start_result.message,
//! b"[email protected]",
//! ServerLoginStartParameters::WithIdentifiers(
//! b"username".to_vec(),
//! b"facebook.com".to_vec(),
//! b"Alice_the_Cryptographer".to_vec(),
//! b"Facebook".to_vec(),
//! ),
//! )?;
//! # Ok::<(), ProtocolError>(())
@@ -638,7 +649,7 @@
//! ```
//! # use opaque_ke::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup,
//! # slow_hash::NoOpHash,
//! # };
//! # use opaque_ke::ciphersuite::CipherSuite;
@@ -656,10 +667,10 @@
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let server_kp = Default::generate_random_keypair(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::WithIdentifiers(b"username".to_vec(), b"facebook.com".to_vec()))?;
//! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.serialize();
//! # 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 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 password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
//! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng,
//! # b"password",
@@ -670,12 +681,12 @@
//! # &password_file_bytes[..],
//! # )?;
//! # let server_login_start_result =
//! # ServerLogin::start(&mut server_rng, password_file, &server_kp.private(), client_login_start_result.message, ServerLoginStartParameters::WithIdentifiers(b"username".to_vec(), b"facebook.com".to_vec()))?;
//! # 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()))?;
//! let client_login_finish_result = client_login_start_result.state.finish(
//! server_login_start_result.message,
//! ClientLoginFinishParameters::WithIdentifiers(
//! b"username".to_vec(),
//! b"facebook.com".to_vec(),
//! b"Alice_the_Cryptographer".to_vec(),
//! b"Facebook".to_vec(),
//! ),
//! )?;
//! # Ok::<(), ProtocolError>(())
@@ -766,7 +777,9 @@ pub use crate::messages::{
CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest,
RegistrationResponse, RegistrationUpload,
};
pub use crate::opaque::{ClientLogin, ClientRegistration, ServerLogin, ServerRegistration};
pub use crate::opaque::{
ClientLogin, ClientRegistration, ServerLogin, ServerRegistration, ServerSetup,
};
pub use crate::opaque::{
ClientLoginFinishParameters, ClientLoginStartParameters, ClientRegistrationFinishParameters,
ServerLoginStartParameters,
+12
View File
@@ -24,6 +24,9 @@ pub trait GroupWithMapToCurve: Group {
/// transforms a password and domain separation tag (DST) into a curve point
fn map_to_curve<H: Hash>(msg: &[u8], dst: &[u8]) -> Result<Self, InternalPakeError>;
/// Hashes a slice of pseudo-random bytes to a scalar
fn hash_to_scalar<H: Hash>(input: &[u8]) -> Result<Self::Scalar, InternalPakeError>;
/// Generates the contextString parameter as defined in
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-05.txt>
fn get_context_string(mode: u8) -> Vec<u8> {
@@ -43,6 +46,15 @@ impl GroupWithMapToCurve for RistrettoPoint {
&GenericArray::clone_from_slice(&uniform_bytes[..]),
))
}
fn hash_to_scalar<H: Hash>(input: &[u8]) -> Result<Self::Scalar, InternalPakeError> {
const LEN_IN_BYTES: usize = 64;
let uniform_bytes = expand_message_xmd::<H>(input, b"", LEN_IN_BYTES)?;
let mut bits = [0u8; LEN_IN_BYTES];
bits.copy_from_slice(&uniform_bytes[..]);
Ok(Self::Scalar::from_bytes_mod_order_wide(&bits))
}
}
// Computes ceil(x / y)
+56 -39
View File
@@ -10,14 +10,16 @@ use crate::{
envelope::Envelope,
errors::{
utils::{check_slice_size, check_slice_size_atleast},
PakeError, ProtocolError,
ProtocolError, PakeError,
},
group::Group,
key_exchange::traits::{FromBytes, KeyExchange, ToBytes},
keypair::{KeyPair, PublicKey, SizedBytesExt},
};
use digest::Digest;
use generic_array::{typenum::Unsigned, GenericArray};
use generic_bytes::SizedBytes;
use rand::{CryptoRng, RngCore};
// Messages
// =========
@@ -127,6 +129,8 @@ pub struct RegistrationUpload<CS: CipherSuite> {
/// The "envelope" generated by the user, containing sealed
/// cryptographic identifiers
pub(crate) envelope: Envelope<CS::Hash>,
/// The masking key used to mask the envelope
pub(crate) masking_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
/// The user's public key
pub(crate) client_s_pk: PublicKey,
}
@@ -136,6 +140,7 @@ impl<CS: CipherSuite> Clone for RegistrationUpload<CS> {
fn clone(&self) -> Self {
Self {
envelope: self.envelope.clone(),
masking_key: self.masking_key.clone(),
client_s_pk: self.client_s_pk.clone(),
}
}
@@ -146,6 +151,7 @@ impl<CS: CipherSuite> RegistrationUpload<CS> {
pub fn serialize(&self) -> Vec<u8> {
[
self.client_s_pk.to_arr().to_vec(),
self.masking_key.to_vec(),
self.envelope.serialize(),
]
.concat()
@@ -154,22 +160,37 @@ impl<CS: CipherSuite> RegistrationUpload<CS> {
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let key_len = <PublicKey as SizedBytes>::Len::to_usize();
let checked_slice = check_slice_size_atleast(input, key_len, "registration_upload_bytes")?;
let (envelope, remainder) = Envelope::<CS::Hash>::deserialize(&checked_slice[key_len..])?;
if !remainder.is_empty() {
return Err(PakeError::SerializationError.into());
}
let hash_len = <CS::Hash as Digest>::OutputSize::to_usize();
let checked_slice = check_slice_size(
&input,
key_len + hash_len + Envelope::<CS::Hash>::len(),
"registration_upload_bytes",
)?;
let envelope = Envelope::<CS::Hash>::deserialize(&checked_slice[key_len + hash_len..])?;
Ok(Self {
envelope,
masking_key: GenericArray::clone_from_slice(
&checked_slice[key_len..key_len + hash_len],
),
client_s_pk: KeyPair::<CS::Group>::check_public_key(PublicKey::from_bytes(
&checked_slice[..key_len],
)?)?,
})
}
// Creates a dummy instance used for faking a [CredentialResponse]
pub(crate) fn dummy<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
let mut masking_key = vec![0u8; <CS::Hash as Digest>::OutputSize::to_usize()];
rng.fill_bytes(&mut masking_key);
let keypair = KeyPair::<CS::Group>::generate_random(rng);
Self {
envelope: Envelope::<CS::Hash>::dummy(),
masking_key: GenericArray::clone_from_slice(&masking_key),
client_s_pk: keypair.public().clone(),
}
}
}
impl_serialize_and_deserialize_for!(RegistrationUpload);
@@ -229,9 +250,8 @@ impl_serialize_and_deserialize_for!(CredentialRequest);
pub struct CredentialResponse<CS: CipherSuite> {
/// the server's oprf output
pub(crate) beta: CS::Group,
pub(crate) server_s_pk: PublicKey,
/// the user's sealed information,
pub(crate) envelope: Envelope<CS::Hash>,
pub(crate) masking_nonce: Vec<u8>,
pub(crate) masked_response: Vec<u8>,
pub(crate) ke2_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE2Message,
}
@@ -240,8 +260,8 @@ impl<CS: CipherSuite> Clone for CredentialResponse<CS> {
fn clone(&self) -> Self {
Self {
beta: self.beta,
server_s_pk: self.server_s_pk.clone(),
envelope: self.envelope.clone(),
masking_nonce: self.masking_nonce.clone(),
masked_response: self.masked_response.clone(),
ke2_message: self.ke2_message.clone(),
}
}
@@ -251,7 +271,7 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
[
Self::serialize_without_ke(&self.beta, &self.server_s_pk, &self.envelope),
Self::serialize_without_ke(&self.beta, &self.masking_nonce, &self.masked_response),
self.ke2_message.to_bytes(),
]
.concat()
@@ -259,23 +279,26 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
pub(crate) fn serialize_without_ke(
beta: &CS::Group,
server_s_pk: &PublicKey,
envelope: &Envelope<CS::Hash>,
masking_nonce: &[u8],
masked_response: &[u8],
) -> Vec<u8> {
[
&beta.to_arr(),
&server_s_pk.to_arr()[..],
&envelope.to_bytes(),
]
.concat()
[&beta.to_arr(), masking_nonce, masked_response].concat()
}
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let elem_len = <CS::Group as Group>::ElemLen::to_usize();
let key_len = <PublicKey as SizedBytes>::Len::to_usize();
let checked_slice =
check_slice_size_atleast(input, elem_len + key_len, "login_second_message_bytes")?;
let nonce_len: usize = 32;
let envelope_len = Envelope::<CS::Hash>::len();
let masked_response_len = key_len + envelope_len;
let ke2_message_len = CS::KeyExchange::ke2_message_size();
let checked_slice = check_slice_size_atleast(
input,
elem_len + nonce_len + masked_response_len + ke2_message_len,
"credential_response_bytes",
)?;
// Check that the message is actually containing an element of the
// correct subgroup
@@ -288,25 +311,19 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
return Err(PakeError::IdentityGroupElementError.into());
}
let unchecked_server_s_pk =
PublicKey::from_bytes(&checked_slice[elem_len..elem_len + key_len])?;
let server_s_pk = KeyPair::<CS::Group>::check_public_key(unchecked_server_s_pk)?;
let (envelope, remainder) =
Envelope::<CS::Hash>::deserialize(&checked_slice[elem_len + key_len..])?;
let ke2_message_size = CS::KeyExchange::ke2_message_size();
let checked_remainder =
check_slice_size_atleast(&remainder, ke2_message_size, "login_second_message_bytes")?;
let masking_nonce = checked_slice[elem_len..elem_len + nonce_len].to_vec();
let masked_response = checked_slice
[elem_len + nonce_len..elem_len + nonce_len + masked_response_len]
.to_vec();
let ke2_message =
<CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE2Message::from_bytes::<CS>(
checked_remainder,
&checked_slice[elem_len + nonce_len + masked_response_len..],
)?;
Ok(Self {
beta,
server_s_pk,
envelope,
masking_nonce,
masked_response,
ke2_message,
})
}
+224 -316
View File
@@ -8,7 +8,7 @@
use crate::{
ciphersuite::CipherSuite,
envelope::{mode_from_ids, Envelope},
errors::{utils::check_slice_size_atleast, InternalPakeError, PakeError, ProtocolError},
errors::{utils::check_slice_size, InternalPakeError, PakeError, ProtocolError},
group::Group,
hash::Hash,
key_exchange::traits::{FromBytes, KeyExchange, ToBytesWithPointers},
@@ -23,10 +23,66 @@ use crate::{
use digest::Digest;
use generic_array::{typenum::Unsigned, GenericArray};
use generic_bytes::SizedBytes;
use hkdf::Hkdf;
use rand::{CryptoRng, RngCore};
use std::marker::PhantomData;
use zeroize::Zeroize;
const STR_CREDENTIAL_RESPONSE_PAD: &[u8] = b"CredentialResponsePad";
const STR_MASKING_KEY: &[u8] = b"MaskingKey";
const STR_OPRF_KEY: &[u8] = b"OprfKey";
// Server Setup
// ============
/// The state elements the server holds upon setup
pub struct ServerSetup<CS: CipherSuite> {
oprf_seed: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
keypair: KeyPair<CS::Group>,
}
impl<CS: CipherSuite> ServerSetup<CS> {
/// Generate a new instance of server setup
pub fn new<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
let mut seed = vec![0u8; <CS::Hash as Digest>::OutputSize::to_usize()];
rng.fill_bytes(&mut seed);
Self {
oprf_seed: GenericArray::clone_from_slice(&seed[..]),
keypair: KeyPair::<CS::Group>::generate_random(rng),
}
}
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
[
self.oprf_seed.to_vec(),
self.keypair.private().to_arr().to_vec(),
]
.concat()
}
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let seed_len = <CS::Hash as Digest>::OutputSize::to_usize();
let checked_slice = check_slice_size(
input,
seed_len + <PrivateKey as SizedBytes>::Len::to_usize(),
"server_setup",
)?;
Ok(Self {
oprf_seed: GenericArray::clone_from_slice(&checked_slice[..seed_len]),
keypair: KeyPair::from_private_key_slice(&checked_slice[seed_len..])?,
})
}
/// Returns the keypair
pub fn keypair(&self) -> &KeyPair<CS::Group> {
&self.keypair
}
}
// Registration
// ============
@@ -48,12 +104,11 @@ impl<CS: CipherSuite> Clone for ClientRegistration<CS> {
impl<CS: CipherSuite> ClientRegistration<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
let output: Vec<u8> = [
[
&CS::Group::scalar_as_bytes(&self.token.blind)[..],
&self.token.data,
]
.concat();
output
.concat()
}
/// Deserialization from bytes
@@ -129,28 +184,6 @@ impl<CS: CipherSuite> Clone for ClientRegistrationStartResult<CS> {
impl<CS: CipherSuite> ClientRegistration<CS> {
/// Returns an initial "blinded" request to send to the server, as well as a ClientRegistration
///
/// # Arguments
/// * `password` - A user password
///
/// # Example
///
/// ```
/// use opaque_ke::ClientRegistration;
/// # 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_registration_start_result = ClientRegistration::<Default>::start(&mut client_rng, b"hunter2")?;
/// # Ok::<(), ProtocolError>(())
/// ```
pub fn start<R: RngCore + CryptoRng>(
blinding_factor_rng: &mut R,
password: &[u8],
@@ -190,35 +223,6 @@ impl<CS: CipherSuite> Clone for ClientRegistrationFinishResult<CS> {
impl<CS: CipherSuite> ClientRegistration<CS> {
/// "Unblinds" the server's answer and returns a final message containing
/// cryptographic identifiers, to be sent to the server on setup finalization
///
/// # Arguments
/// * `message` - the server's answer to the initial registration attempt
///
/// # Example
///
/// ```
/// use opaque_ke::{ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration};
/// # use opaque_ke::errors::ProtocolError;
/// # use opaque_ke::keypair::KeyPair;
/// 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 mut server_rng = OsRng;
/// let server_kp = Default::generate_random_keypair(&mut server_rng);
/// let client_registration_start_result = ClientRegistration::<Default>::start(&mut client_rng, b"hunter2")?;
/// let server_registration_start_result =
/// ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
/// let mut client_rng = OsRng;
/// let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
/// # Ok::<(), ProtocolError>(())
/// ```
pub fn finish<R: CryptoRng + RngCore>(
self,
rng: &mut R,
@@ -229,11 +233,16 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
ClientRegistrationFinishParameters::WithIdentifiers(id_u, id_s) => Some((id_u, id_s)),
ClientRegistrationFinishParameters::Default => None,
};
let client_static_keypair = CS::generate_random_keypair(rng);
let client_static_keypair = KeyPair::<CS::Group>::generate_random(rng);
let password_derived_key =
get_password_derived_key::<CS::Group, CS::SlowHash, CS::Hash>(&self.token, r2.beta)?;
let h = Hkdf::<CS::Hash>::new(None, &password_derived_key);
let mut masking_key = vec![0u8; <CS::Hash as Digest>::OutputSize::to_usize()];
h.expand(STR_MASKING_KEY, &mut masking_key)
.map_err(|_| InternalPakeError::HkdfError)?;
let (envelope, export_key) = Envelope::<CS::Hash>::seal(
rng,
&password_derived_key,
@@ -245,6 +254,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
Ok(ClientRegistrationFinishResult {
message: RegistrationUpload {
envelope,
masking_key: GenericArray::clone_from_slice(&masking_key[..]),
client_s_pk: client_static_keypair.public().clone(),
},
export_key,
@@ -254,12 +264,11 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
}
}
/// Contains the fields that are returned by a server registration start
/// Contains the fields that are returned by a server registration start.
/// Note that there is no state output in this step
pub struct ServerRegistrationStartResult<CS: CipherSuite> {
/// The registration resposne message to send to the client
pub message: RegistrationResponse<CS>,
/// The state that the server must keep in order to complete registration
pub state: ServerRegistration<CS>,
}
// Cannot be derived because it would require for CS to be Clone.
@@ -267,123 +276,51 @@ impl<CS: CipherSuite> Clone for ServerRegistrationStartResult<CS> {
fn clone(&self) -> Self {
Self {
message: self.message.clone(),
state: self.state.clone(),
}
}
}
/// The state elements the server holds to record a registration
pub struct ServerRegistration<CS: CipherSuite> {
envelope: Option<Envelope<CS::Hash>>,
client_s_pk: Option<PublicKey>,
pub(crate) oprf_key: <CS::Group as Group>::Scalar,
}
pub struct ServerRegistration<CS: CipherSuite>(RegistrationUpload<CS>);
// Cannot be derived because it would require for CS to be Clone.
impl<CS: CipherSuite> Clone for ServerRegistration<CS> {
fn clone(&self) -> Self {
Self {
envelope: self.envelope.clone(),
client_s_pk: self.client_s_pk.clone(),
oprf_key: self.oprf_key.clone(),
}
Self(self.0.clone())
}
}
impl<CS: CipherSuite> ServerRegistration<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
let mut output: Vec<u8> = CS::Group::scalar_as_bytes(&self.oprf_key).to_vec();
self.client_s_pk
.iter()
.for_each(|v| output.extend_from_slice(&v.to_arr()));
self.envelope
.iter()
.for_each(|v| output.extend_from_slice(&v.to_bytes()));
output
self.0.serialize()
}
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let scalar_len = <CS::Group as Group>::ScalarLen::to_usize();
if input.len() == scalar_len {
return Ok(Self {
oprf_key: CS::Group::from_scalar_slice(GenericArray::from_slice(input))?,
client_s_pk: None,
envelope: None,
});
}
// Need to do this check manually because envelope is variable-size
let key_len = <PublicKey as SizedBytes>::Len::to_usize();
let checked_bytes =
check_slice_size_atleast(input, scalar_len + key_len, "server_registration_bytes")?;
let oprf_key_bytes = GenericArray::from_slice(&checked_bytes[..scalar_len]);
let oprf_key = CS::Group::from_scalar_slice(oprf_key_bytes)?;
let unchecked_client_s_pk =
PublicKey::from_bytes(&checked_bytes[scalar_len..scalar_len + key_len])?;
let client_s_pk = KeyPair::<CS::Group>::check_public_key(unchecked_client_s_pk)?;
let envelope = Envelope::<CS::Hash>::from_bytes(&checked_bytes[scalar_len + key_len..])?;
Ok(Self {
envelope: Some(envelope),
client_s_pk: Some(client_s_pk),
oprf_key,
})
Ok(Self(RegistrationUpload::deserialize(input)?))
}
#[cfg(test)]
pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
[
match &self.envelope {
Some(env) => env.as_byte_ptrs(),
None => vec![],
},
match &self.client_s_pk {
Some(pk) => vec![(pk.as_ptr(), pk.len())],
None => vec![],
},
self.0.envelope.as_byte_ptrs(),
vec![(self.0.client_s_pk.as_ptr(), self.0.client_s_pk.len())],
/* cannot provide raw pointer to self.oprf_key until this is exposed in curve25519_dalek::scalar::Scalar */
].concat()
}
/// From the client's "blinded" password, returns a response to be
/// sent back to the client, as well as a ServerRegistration
///
/// # Arguments
/// * `message` - the initial registration message
///
/// # Example
///
/// ```
/// use opaque_ke::*;
/// # 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 mut server_rng = OsRng;
/// let server_kp = Default::generate_random_keypair(&mut server_rng);
/// let client_registration_start_result = ClientRegistration::<Default>::start(&mut client_rng, b"hunter2")?;
/// let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
/// # Ok::<(), ProtocolError>(())
/// ```
pub fn start<R: RngCore + CryptoRng>(
rng: &mut R,
pub fn start(
server_setup: &ServerSetup<CS>,
message: RegistrationRequest<CS>,
server_s_pk: &PublicKey,
credential_identifier: &[u8],
) -> Result<ServerRegistrationStartResult<CS>, ProtocolError> {
// RFC: generate oprf_key (salt) and v_u = g^oprf_key
let oprf_key = CS::Group::random_nonzero_scalar(rng);
let oprf_key = oprf_key_from_seed::<CS::Group, CS::Hash>(
&server_setup.oprf_seed,
credential_identifier,
)?;
// Compute beta = alpha^oprf_key
let beta = oprf::evaluate::<CS::Group>(message.alpha, &oprf_key);
@@ -391,52 +328,20 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
Ok(ServerRegistrationStartResult {
message: RegistrationResponse {
beta,
server_s_pk: server_s_pk.clone(),
},
state: Self {
envelope: None,
client_s_pk: None,
oprf_key,
server_s_pk: server_setup.keypair.public().clone(),
},
})
}
/// From the client's cryptographic identifiers, fully populates and
/// returns a ServerRegistration
///
/// # Arguments
/// * `message` - the final client message
///
/// # Example
///
/// ```
/// use opaque_ke::{*, keypair::KeyPair};
/// # 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 mut server_rng = OsRng;
/// let server_kp = Default::generate_random_keypair(&mut server_rng);
/// let client_registration_start_result = ClientRegistration::<Default>::start(&mut client_rng, b"hunter2")?;
/// let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
/// let mut client_rng = OsRng;
/// let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
/// let client_record = server_registration_start_result.state.finish(client_registration_finish_result.message)?;
/// # Ok::<(), ProtocolError>(())
/// ```
pub fn finish(self, message: RegistrationUpload<CS>) -> Result<Self, ProtocolError> {
Ok(Self {
envelope: Some(message.envelope),
client_s_pk: Some(message.client_s_pk),
oprf_key: self.oprf_key.clone(),
})
pub fn finish(message: RegistrationUpload<CS>) -> Self {
Self(message)
}
// Creates a dummy instance used for faking a [CredentialResponse]
pub(crate) fn dummy<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
Self(RegistrationUpload::dummy(rng))
}
}
@@ -653,41 +558,9 @@ impl<CS: CipherSuite> ClientLogin<CS> {
/// "Unblinds" the server's answer and returns the opened assets from
/// the server
///
/// # Arguments
/// * `message` - the server's answer to the initial login attempt
///
/// # Example
///
/// ```
/// use opaque_ke::{ClientLogin, ClientLoginStartParameters, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters};
/// # use opaque_ke::{ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration};
/// # use opaque_ke::errors::ProtocolError;
/// # use opaque_ke::keypair::KeyPair;
/// 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 mut server_rng = OsRng;
/// # let client_registration_start_result = ClientRegistration::<Default>::start(&mut client_rng, b"hunter2")?;
/// # let server_kp = Default::generate_random_keypair(&mut server_rng);
/// # let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
/// # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
/// # let p_file = server_registration_start_result.state.finish(client_registration_finish_result.message)?;
/// let client_login_start_result = ClientLogin::<Default>::start(&mut client_rng, b"hunter2", ClientLoginStartParameters::default())?;
/// let server_login_start_result = ServerLogin::start(&mut server_rng, p_file, &server_kp.private(), client_login_start_result.message, ServerLoginStartParameters::default())?;
/// let client_login_finish_result = client_login_start_result.state.finish(server_login_start_result.message, ClientLoginFinishParameters::default())?;
/// # Ok::<(), ProtocolError>(())
/// ```
pub fn finish(
self,
l2: CredentialResponse<CS>,
credential_response: CredentialResponse<CS>,
params: ClientLoginFinishParameters,
) -> Result<ClientLoginFinishResult<CS>, ProtocolError> {
let optional_ids = match params {
@@ -695,12 +568,28 @@ impl<CS: CipherSuite> ClientLogin<CS> {
ClientLoginFinishParameters::WithIdentifiers(id_u, id_s) => Some((id_u, id_s)),
};
let server_s_pk_bytes = l2.server_s_pk.to_arr().to_vec();
let password_derived_key = get_password_derived_key::<CS::Group, CS::SlowHash, CS::Hash>(
&self.token,
credential_response.beta,
)?;
let password_derived_key =
get_password_derived_key::<CS::Group, CS::SlowHash, CS::Hash>(&self.token, l2.beta)?;
let opened_envelope = &l2
.envelope
let h = Hkdf::<CS::Hash>::new(None, &password_derived_key);
let mut masking_key = vec![0u8; <CS::Hash as Digest>::OutputSize::to_usize()];
h.expand(STR_MASKING_KEY, &mut masking_key)
.map_err(|_| InternalPakeError::HkdfError)?;
let (server_s_pk, envelope) = unmask_response::<CS::Hash>(
&masking_key,
&credential_response.masking_nonce,
&credential_response.masked_response,
)
.map_err(|e| match e {
ProtocolError::InvalidInnerEnvelopeError => PakeError::InvalidLoginError.into(),
err => err,
})?;
let server_s_pk_bytes = server_s_pk.to_arr().to_vec();
let opened_envelope = &envelope
.open(&password_derived_key, &server_s_pk_bytes, &optional_ids)
.map_err(|e| match e {
InternalPakeError::SealOpenHmacError => PakeError::InvalidLoginError,
@@ -719,15 +608,18 @@ impl<CS: CipherSuite> ClientLogin<CS> {
Some((id_u, id_s)) => (id_u, id_s),
};
let credential_response_component =
CredentialResponse::<CS>::serialize_without_ke(&l2.beta, &l2.server_s_pk, &l2.envelope);
let credential_response_component = CredentialResponse::<CS>::serialize_without_ke(
&credential_response.beta,
&credential_response.masking_nonce,
&credential_response.masked_response,
);
let (confidential_info, session_key, ke3_message) = CS::KeyExchange::generate_ke3(
credential_response_component,
l2.ke2_message,
credential_response.ke2_message,
&self.ke1_state,
&self.serialized_credential_request,
l2.server_s_pk.clone(),
server_s_pk.clone(),
client_s_sk,
id_u,
id_s,
@@ -738,7 +630,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
message: CredentialFinalization { ke3_message },
session_key,
export_key: opened_envelope.export_key.clone(),
server_s_pk: l2.server_s_pk,
server_s_pk,
#[cfg(test)]
state: self,
})
@@ -839,50 +731,24 @@ impl<CS: CipherSuite> ServerLogin<CS> {
})
}
/// From the client's "blinded"" password, returns a challenge to be
/// From the client's "blinded" password, returns a challenge to be
/// sent back to the client, as well as a ServerLogin
///
/// # Arguments
/// * `message` - the initial registration message
///
/// # Example
///
/// ```
/// use opaque_ke::{ClientLogin, ClientLoginStartParameters, ServerLogin, ServerLoginStartParameters};
/// # use opaque_ke::{ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration};
/// # use opaque_ke::errors::ProtocolError;
/// # use opaque_ke::keypair::KeyPair;
/// 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 mut server_rng = OsRng;
/// let server_kp = Default::generate_random_keypair(&mut server_rng);
/// # let client_registration_start_result = ClientRegistration::<Default>::start(&mut client_rng, b"hunter2")?;
/// # let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
/// # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
/// # let p_file = server_registration_start_result.state.finish(client_registration_finish_result.message)?;
/// let client_login_start_result = ClientLogin::<Default>::start(&mut client_rng, b"hunter2", ClientLoginStartParameters::default())?;
/// let server_login_start_result = ServerLogin::start(&mut server_rng, p_file, &server_kp.private(), client_login_start_result.message, ServerLoginStartParameters::default())?;
/// # Ok::<(), ProtocolError>(())
/// ```
pub fn start<R: RngCore + CryptoRng>(
rng: &mut R,
password_file: ServerRegistration<CS>,
server_s_sk: &PrivateKey,
server_setup: &ServerSetup<CS>,
password_file: Option<ServerRegistration<CS>>,
l1: CredentialRequest<CS>,
credential_identifier: &[u8],
params: ServerLoginStartParameters,
) -> Result<ServerLoginStartResult<CS>, ProtocolError> {
let client_s_pk = password_file
.client_s_pk
.clone()
.ok_or(InternalPakeError::SealError)?;
// FIXME: handle optional password_file case, ensure that there is no timing attack by generating a random pubkey anyway
let record = match password_file {
Some(x) => x,
None => ServerRegistration::dummy(rng),
};
let client_s_pk = record.0.client_s_pk.clone();
let (e_info, optional_ids) = match params {
ServerLoginStartParameters::WithInfo(e_info) => (e_info, None),
@@ -894,30 +760,39 @@ impl<CS: CipherSuite> ServerLogin<CS> {
}
};
let envelope = password_file
.envelope
.clone()
.ok_or(InternalPakeError::SealError)?;
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_pk = KeyPair::<CS::Group>::public_from_private(&server_s_sk);
let mut masking_nonce = vec![0u8; 32];
rng.fill_bytes(&mut masking_nonce);
let masked_response = mask_response(
&record.0.masking_key,
&masking_nonce,
&server_s_pk,
&envelope,
)?;
let (id_u, id_s) = match optional_ids {
None => (
client_s_pk.to_arr().to_vec(),
KeyPair::<CS::Group>::public_from_private(server_s_sk)
.to_arr()
.to_vec(),
),
None => (client_s_pk.to_arr().to_vec(), server_s_pk.to_arr().to_vec()),
Some((id_u, id_s)) => (id_u, id_s),
};
let l1_bytes = &l1.serialize();
let beta = oprf::evaluate(l1.alpha, &password_file.oprf_key);
let server_s_pk = KeyPair::<CS::Group>::public_from_private(server_s_sk);
let oprf_key = oprf_key_from_seed::<CS::Group, CS::Hash>(
&server_setup.oprf_seed,
credential_identifier,
)?;
let beta = oprf::evaluate(l1.alpha, &oprf_key);
let credential_response_component =
CredentialResponse::<CS>::serialize_without_ke(&beta, &server_s_pk, &envelope);
CredentialResponse::<CS>::serialize_without_ke(&beta, &masking_nonce, &masked_response);
let (plain_info, ke2_state, ke2_message) = CS::KeyExchange::generate_ke2(
rng,
@@ -933,8 +808,8 @@ impl<CS: CipherSuite> ServerLogin<CS> {
let credential_response = CredentialResponse {
beta,
server_s_pk,
envelope,
masking_nonce,
masked_response,
ke2_message,
};
@@ -950,39 +825,6 @@ impl<CS: CipherSuite> ServerLogin<CS> {
/// From the client's second and final message, check the client's
/// authentication and produce a message transport
///
/// # Arguments
/// * `message` - the client's second login message
///
/// # Example
///
/// ```
/// use opaque_ke::{ClientLogin, ClientLoginFinishParameters, ClientLoginStartParameters, ServerLogin, ServerLoginStartParameters};
/// # use opaque_ke::{ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration};
/// # use opaque_ke::errors::ProtocolError;
/// # use opaque_ke::keypair::KeyPair;
/// 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 mut server_rng = OsRng;
/// let server_kp = Default::generate_random_keypair(&mut server_rng);
/// # let client_registration_start_result = ClientRegistration::<Default>::start(&mut client_rng, b"hunter2")?;
/// # let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
/// # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
/// # let p_file = server_registration_start_result.state.finish(client_registration_finish_result.message)?;
/// let client_login_start_result = ClientLogin::<Default>::start(&mut client_rng, b"hunter2", ClientLoginStartParameters::default())?;
/// let server_login_start_result = ServerLogin::start(&mut server_rng, p_file, &server_kp.private(), client_login_start_result.message, ServerLoginStartParameters::default())?;
/// let client_login_finish_result = client_login_start_result.state.finish(server_login_start_result.message, ClientLoginFinishParameters::default())?;
/// let mut server_transport = server_login_start_result.state.finish(client_login_finish_result.message)?;
/// # Ok::<(), ProtocolError>(())
/// ```
pub fn finish(
self,
message: CredentialFinalization<CS>,
@@ -1033,9 +875,9 @@ impl<CS: CipherSuite> Drop for ClientRegistration<CS> {
// This can't be derived because of the use of a phantom parameter
impl<CS: CipherSuite> Zeroize for ServerRegistration<CS> {
fn zeroize(&mut self) {
self.envelope.zeroize();
self.client_s_pk.zeroize();
self.oprf_key.zeroize();
self.0.envelope.zeroize();
self.0.masking_key.zeroize();
self.0.client_s_pk.zeroize();
}
}
@@ -1083,3 +925,69 @@ fn get_password_derived_key<G: GroupWithMapToCurve, SH: SlowHash<D>, D: Hash>(
let oprf_output = oprf::finalize::<G, D>(&token.data, &token.blind, beta);
SH::hash(oprf_output)
}
fn oprf_key_from_seed<G: GroupWithMapToCurve, D: Hash>(
oprf_seed: &GenericArray<u8, D::OutputSize>,
credential_identifier: &[u8],
) -> Result<G::Scalar, InternalPakeError> {
let mut oprf_key_bytes = vec![0u8; <PrivateKey as SizedBytes>::Len::to_usize()];
Hkdf::<D>::from_prk(oprf_seed)
.map_err(|_| InternalPakeError::HkdfError)?
.expand(
&[credential_identifier, &STR_OPRF_KEY].concat(),
&mut oprf_key_bytes,
)
.map_err(|_| InternalPakeError::HkdfError)?;
G::hash_to_scalar::<D>(&oprf_key_bytes[..])
}
fn mask_response<D: Hash>(
masking_key: &[u8],
masking_nonce: &[u8],
server_s_pk: &PublicKey,
envelope: &Envelope<D>,
) -> Result<Vec<u8>, ProtocolError> {
let mut xor_pad = vec![0u8; <PublicKey as SizedBytes>::Len::to_usize() + Envelope::<D>::len()];
Hkdf::<D>::from_prk(&masking_key)
.map_err(|_| InternalPakeError::HkdfError)?
.expand(
&[masking_nonce, &STR_CREDENTIAL_RESPONSE_PAD].concat(),
&mut xor_pad,
)
.map_err(|_| InternalPakeError::HkdfError)?;
let plaintext = [&server_s_pk.to_arr()[..], &envelope.serialize()].concat();
Ok(xor_pad
.iter()
.zip(plaintext.iter())
.map(|(&x1, &x2)| x1 ^ x2)
.collect())
}
fn unmask_response<D: Hash>(
masking_key: &[u8],
masking_nonce: &[u8],
masked_response: &[u8],
) -> Result<(PublicKey, Envelope<D>), ProtocolError> {
let mut xor_pad = vec![0u8; <PublicKey as SizedBytes>::Len::to_usize() + Envelope::<D>::len()];
Hkdf::<D>::from_prk(&masking_key)
.map_err(|_| InternalPakeError::HkdfError)?
.expand(
&[masking_nonce, &STR_CREDENTIAL_RESPONSE_PAD].concat(),
&mut xor_pad,
)
.map_err(|_| InternalPakeError::HkdfError)?;
let plaintext: Vec<u8> = xor_pad
.iter()
.zip(masked_response.iter())
.map(|(&x1, &x2)| x1 ^ x2)
.collect();
let key_len = <PublicKey as SizedBytes>::Len::to_usize();
let unchecked_server_s_pk =
PublicKey::from_arr(&GenericArray::clone_from_slice(&plaintext[..key_len]))?;
let envelope = Envelope::deserialize(&plaintext[key_len..])?;
// FIXME check server_s_pk
Ok((unchecked_server_s_pk, envelope))
}
+29 -37
View File
@@ -12,7 +12,7 @@ use crate::{
traits::{FromBytes, KeyExchange, ToBytes},
tripledh::{NonceLen, TripleDH},
},
opaque::*,
keypair::{PublicKey, KeyPair},
serialization::{i2osp, os2ip, serialize},
*,
};
@@ -34,6 +34,7 @@ impl CipherSuite for Default {
}
const MAX_INFO_LENGTH: usize = 10;
const HASH_SIZE: usize = 64; // Because of SHA512
const MAC_SIZE: usize = 64; // Because of SHA512
fn random_ristretto_point() -> RistrettoPoint {
@@ -68,12 +69,8 @@ fn server_registration_roundtrip() {
// If we don't have envelope and client_pk, the server registration just
// contains the prf key
let mut rng = OsRng;
let oprf_key = <RistrettoPoint as Group>::random_nonzero_scalar(&mut rng);
let mut oprf_bytes: Vec<u8> = vec![];
oprf_bytes.extend_from_slice(oprf_key.as_bytes());
let reg = ServerRegistration::<Default>::deserialize(&oprf_bytes[..]).unwrap();
let reg_bytes = reg.serialize();
assert_eq!(reg_bytes, oprf_bytes);
let mut masking_key = [0u8; HASH_SIZE];
rng.fill_bytes(&mut masking_key);
let mut ciphertext = [0u8; 32];
rng.fill_bytes(&mut ciphertext);
@@ -85,11 +82,11 @@ fn server_registration_roundtrip() {
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
let mock_client_kp = Default::generate_random_keypair(&mut rng);
let mock_client_kp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng);
// serialization order: oprf_key, public key, envelope
let mut bytes = Vec::<u8>::new();
bytes.extend_from_slice(oprf_key.as_bytes());
bytes.extend_from_slice(&mock_client_kp.public().to_arr());
bytes.extend_from_slice(&masking_key);
bytes.extend_from_slice(&mock_envelope_bytes);
let reg = ServerRegistration::<Default>::deserialize(&bytes[..]).unwrap();
let reg_bytes = reg.serialize();
@@ -125,7 +122,7 @@ fn registration_response_roundtrip() {
let pt = random_ristretto_point();
let beta_bytes = pt.to_arr();
let mut rng = OsRng;
let skp = Default::generate_random_keypair(&mut rng);
let skp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng);
let pubkey_bytes = skp.public().to_arr();
let mut input = Vec::new();
@@ -151,12 +148,15 @@ fn registration_response_roundtrip() {
#[test]
fn registration_upload_roundtrip() {
let mut rng = OsRng;
let skp = Default::generate_random_keypair(&mut rng);
let skp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng);
let pubkey_bytes = skp.public().to_arr();
let mut key = [0u8; 32];
rng.fill_bytes(&mut key);
let mut masking_key = vec![0u8; <sha2::Sha512 as Digest>::OutputSize::to_usize()];
rng.fill_bytes(&mut masking_key);
let mut msg = [0u8; 32];
rng.fill_bytes(&mut msg);
@@ -172,6 +172,7 @@ fn registration_upload_roundtrip() {
let mut input = Vec::new();
input.extend_from_slice(&pubkey_bytes[..]);
input.extend_from_slice(&masking_key[..]);
input.extend_from_slice(&envelope_bytes);
let r3 = RegistrationUpload::<Default>::deserialize(&input[..]).unwrap();
@@ -185,7 +186,7 @@ fn credential_request_roundtrip() {
let alpha = random_ristretto_point();
let alpha_bytes = alpha.to_arr().to_vec();
let client_e_kp = Default::generate_random_keypair(&mut rng);
let client_e_kp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng);
let mut client_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut client_nonce);
@@ -225,25 +226,18 @@ fn credential_response_roundtrip() {
let pt_bytes = pt.to_arr().to_vec();
let mut rng = OsRng;
let skp = Default::generate_random_keypair(&mut rng);
let pubkey_bytes = skp.public().to_arr();
let mut key = [0u8; 32];
rng.fill_bytes(&mut key);
let mut masking_nonce = vec![0u8; 32];
rng.fill_bytes(&mut masking_nonce);
let mut msg = [0u8; 32];
rng.fill_bytes(&mut msg);
let mut masked_response = vec![
0u8;
<PublicKey as SizedBytes>::Len::to_usize()
+ Envelope::<<Default as CipherSuite>::Hash>::len()
];
rng.fill_bytes(&mut masked_response);
let (envelope, _) = Envelope::<sha2::Sha512>::seal_raw(
&mut rng,
&key,
&msg,
&pubkey_bytes,
InnerEnvelopeMode::Base,
)
.unwrap();
let server_e_kp = Default::generate_random_keypair(&mut rng);
let server_e_kp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng);
let mut mac = [0u8; MAC_SIZE];
rng.fill_bytes(&mut mac);
let mut server_nonce = vec![0u8; NonceLen::to_usize()];
@@ -260,12 +254,10 @@ fn credential_response_roundtrip() {
]
.concat();
let serialized_envelope = envelope.serialize();
let mut input = Vec::new();
input.extend_from_slice(pt_bytes.as_slice());
input.extend_from_slice(&pubkey_bytes.as_slice());
input.extend_from_slice(&serialized_envelope);
input.extend_from_slice(&masking_nonce);
input.extend_from_slice(&masked_response);
input.extend_from_slice(&ke2m[..]);
let l2 = CredentialResponse::<Default>::deserialize(&input).unwrap();
@@ -279,8 +271,8 @@ fn credential_response_roundtrip() {
assert!(match CredentialRequest::<Default>::deserialize(
&[
identity_bytes,
pubkey_bytes.to_vec(),
serialized_envelope,
masking_nonce.to_vec(),
masked_response,
ke2m.to_vec()
]
.concat()
@@ -309,7 +301,7 @@ fn client_login_roundtrip() {
let mut rng = OsRng;
let sc = <RistrettoPoint as Group>::random_nonzero_scalar(&mut rng);
let client_e_kp = Default::generate_random_keypair(&mut rng);
let client_e_kp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng);
let mut client_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut client_nonce);
@@ -333,7 +325,7 @@ fn client_login_roundtrip() {
fn ke1_message_roundtrip() {
let mut rng = OsRng;
let client_e_kp = Default::generate_random_keypair(&mut rng);
let client_e_kp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng);
let mut client_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut client_nonce);
@@ -358,7 +350,7 @@ fn ke1_message_roundtrip() {
fn ke2_message_roundtrip() {
let mut rng = OsRng;
let server_e_kp = Default::generate_random_keypair(&mut rng);
let server_e_kp = KeyPair::<<Default as CipherSuite>::Group>::generate_random(&mut rng);
let mut mac = [0u8; MAC_SIZE];
rng.fill_bytes(&mut mac);
let mut server_nonce = vec![0u8; NonceLen::to_usize()];
+139 -147
View File
@@ -10,7 +10,7 @@ use crate::{
errors::*,
group::Group,
key_exchange::tripledh::{NonceLen, TripleDH},
keypair::{PrivateKey, PublicKey, SizedBytesExt},
keypair::KeyPair,
opaque::*,
slow_hash::NoOpHash,
tests::mock_rng::CycleRng,
@@ -44,11 +44,13 @@ pub struct TestVectorParameters {
pub server_s_sk: Vec<u8>,
pub server_e_pk: Vec<u8>,
pub server_e_sk: Vec<u8>,
pub credential_identifier: Vec<u8>,
pub id_u: Vec<u8>,
pub id_s: Vec<u8>,
pub password: Vec<u8>,
pub blinding_factor: Vec<u8>,
pub oprf_key: Vec<u8>,
pub oprf_seed: Vec<u8>,
pub masking_nonce: Vec<u8>,
pub envelope_nonce: Vec<u8>,
pub client_nonce: Vec<u8>,
pub server_nonce: Vec<u8>,
@@ -61,7 +63,6 @@ pub struct TestVectorParameters {
pub credential_response: Vec<u8>,
pub credential_finalization: Vec<u8>,
client_registration_state: Vec<u8>,
server_registration_state: Vec<u8>,
client_login_state: Vec<u8>,
server_login_state: Vec<u8>,
pub password_file: Vec<u8>,
@@ -70,40 +71,42 @@ pub struct TestVectorParameters {
}
static STR_PASSWORD: &str = "password";
static STR_CREDENTIAL_IDENTIFIER: &str = "credential_identifier";
static TEST_VECTOR: &str = r#"
{
"client_s_pk": "6e0a6082dd29936c44b47ecb8a5fe72e4b321a0ac314b0080ca4c48afdabd215",
"client_s_sk": "3000848b34d9073885d427e766b7093cc13bdce992ef31cd00ba2c77ff074504",
"client_e_pk": "5260ad6eb47ac1db44babcce9327327d50e1a0133c0425acca3efcf41b119718",
"client_e_sk": "1526e0bed9af28830da956589d65768ed2a20d9689e82c90b89e4e33904e4009",
"server_s_pk": "c21a38653eb19437669bfa066a446b6eea7c1f253ac7adf4798d6c68e171b273",
"server_s_sk": "2a38b385e9fb7e0a89aa0f005c729b0c4e22eeedea8c105cf136d9c5c334880f",
"server_e_pk": "125f4a2dd9353c8c37a08527a323908835d3fbc374260d32e829d5c3fa81f325",
"server_e_sk": "7ef6ada36f3983c0f24705a29d3a453e3e17c28d347f226b24c0aa5ab12a700d",
"client_s_pk": "e6201a178ea486df66929dc15e6b7cf522a02c4ff05afdfe221bbda08f9c5142",
"client_s_sk": "901f0363ae5b8606740ad0f4849c3f993743d3b1750244c27aa8a53035b42306",
"client_e_pk": "823dc5bf4e3479cf1be9cf2f9fe08f31a5171eb4ef012747b16364f4485e0022",
"client_e_sk": "288cece1cfbca7441b35fa4f6a1f4291855835e623c4874edefd735e507c3f0f",
"server_s_pk": "c44776fd33a5fb022bf6e26c4a5cb2c1ddbb785bd5822324df8eca6d80c1d279",
"server_s_sk": "5f1c3382a1cc430beeb9bdd044d4453d930fac8fe0d6bb65740b6baca208aa01",
"server_e_pk": "987fcf872beaf9547adbc66a63481e0f315960836d46956af2b1eb2608142f2e",
"server_e_sk": "a5d8174ade9f423a18438af605ee5b11047cf9973c21fffefcd336f690dd6103",
"credential_identifier": "637265644964656e746966696572",
"id_u": "696455",
"id_s": "696453",
"password": "70617373776f7264",
"blinding_factor": "a32862d66eb57246321fb6b229e83786745c3afdf8957ebe38b01c17571ba105",
"oprf_key": "0851f5ec43e0b702bfcb9d8ec208085b51c0fc5200234901407c176327aa0b02",
"envelope_nonce": "78b006042d011bdca5d0058a978d2103a6d546de311a2e8cd025fbc67303a468",
"client_nonce": "43497a6f86ba31a6a7f399271fb3b4b4f82c4af086bc431ebaeab7d768ff8a64",
"server_nonce": "680cd27da18bc56d4317e7db61de1726a70ebe4e49aee38a3bdb6787cf85466e",
"blinding_factor": "ac66fda6a29b52b1c719453adea5370852f906448609601e7da7892c29017b0b",
"oprf_seed": "c682275f90adc465ff551a82e54b6e16bd2e49cda05f7c6e6988a269b43a10f824ae31e90c8f8bbbb8cc85f552c0809acab37169332f68351dc7edc575d524f3",
"masking_nonce": "596b51d2a8a4485ae8d506684942cc2be327b72bac3ccf9864376264b2f5c5dbc5283e39cde28751430c6ab072c7c769c481ef61999488d740203a0b3dbce92d",
"envelope_nonce": "9b33bc17a2daa8346449bffe8c479202237a0e074ba82b88b6c4d122b22edac7",
"client_nonce": "a854353a0d88014af9eda6cb7ee3aff7d12fd4174c05221f3300d6ae12561ac0",
"server_nonce": "949f7773f95493a238f20a2212924108c6492d9eff5e1151b082c869496d1a7c",
"info1": "696e666f31",
"einfo2": "65696e666f32",
"registration_request": "14ba86e53018ce5507d2bfb2d98ad3f60e302d826bff3410a5ec669c8e1ef17d",
"registration_response": "aee5c937a85acfbdbd71faf1c5519bfe9e44b0b7489dcc663df9f1ca5b520b6ac21a38653eb19437669bfa066a446b6eea7c1f253ac7adf4798d6c68e171b273",
"registration_upload": "6e0a6082dd29936c44b47ecb8a5fe72e4b321a0ac314b0080ca4c48afdabd2150278b006042d011bdca5d0058a978d2103a6d546de311a2e8cd025fbc67303a4687618b32fe2ec2a5c2b6efecec1e6e535106de80af68733673daf0b644965966fa3f279e532d7ecef363f8d55ff6df4c473cfb1049a73f632972bfcc6744185d13a671dd6678d49fb1629a6fbfbe266937378fd9e772c2dd72692d1a35c020010",
"credential_request": "14ba86e53018ce5507d2bfb2d98ad3f60e302d826bff3410a5ec669c8e1ef17d43497a6f86ba31a6a7f399271fb3b4b4f82c4af086bc431ebaeab7d768ff8a640005696e666f315260ad6eb47ac1db44babcce9327327d50e1a0133c0425acca3efcf41b119718",
"credential_response": "aee5c937a85acfbdbd71faf1c5519bfe9e44b0b7489dcc663df9f1ca5b520b6ac21a38653eb19437669bfa066a446b6eea7c1f253ac7adf4798d6c68e171b2730278b006042d011bdca5d0058a978d2103a6d546de311a2e8cd025fbc67303a4687618b32fe2ec2a5c2b6efecec1e6e535106de80af68733673daf0b644965966fa3f279e532d7ecef363f8d55ff6df4c473cfb1049a73f632972bfcc6744185d13a671dd6678d49fb1629a6fbfbe266937378fd9e772c2dd72692d1a35c020010680cd27da18bc56d4317e7db61de1726a70ebe4e49aee38a3bdb6787cf85466e125f4a2dd9353c8c37a08527a323908835d3fbc374260d32e829d5c3fa81f32500068a4c321f3c375613862ba83f7e5abb8f1d26dbd8035d39f192eb1324c2214457098054a2cbdf5d4ab2894eaf152af8c4be61d701c5a1ab1ec3e1cee5810898140b81771db0be",
"credential_finalization": "330aa8ac01bbc9d9642fe1c286187379efe12da14aceab86b22449d21f242d89adabc0295751b4e007beabc413ae9cbf9979e324749953705fd85b87c9c1b1a2",
"client_registration_state": "a32862d66eb57246321fb6b229e83786745c3afdf8957ebe38b01c17571ba10570617373776f7264",
"client_login_state": "a32862d66eb57246321fb6b229e83786745c3afdf8957ebe38b01c17571ba105006714ba86e53018ce5507d2bfb2d98ad3f60e302d826bff3410a5ec669c8e1ef17d43497a6f86ba31a6a7f399271fb3b4b4f82c4af086bc431ebaeab7d768ff8a640005696e666f315260ad6eb47ac1db44babcce9327327d50e1a0133c0425acca3efcf41b11971800401526e0bed9af28830da956589d65768ed2a20d9689e82c90b89e4e33904e400943497a6f86ba31a6a7f399271fb3b4b4f82c4af086bc431ebaeab7d768ff8a6470617373776f7264",
"server_registration_state": "0851f5ec43e0b702bfcb9d8ec208085b51c0fc5200234901407c176327aa0b02",
"server_login_state": "89cd93dce8f59cf7b187736c50cbc3ca2e0bbbad0a0be1ddee180a2d95db60695b384be2dd673434ef94bcdbb1b457f63f41cd79ed2422c021c34ca1433b70576920ebb950ad1c40c8c015ec6832e12427e755ba21d005f0b6d5d66d2368ab5ec6f32151fced5a3aa25472c425912242de3638ef57f28b0dd02a956064e5bb9b8fcd73add52d233a454d6b20125e9506a95aae8772ebbfac4d70efe1fb10078fd40f93d84aa7db53853ca74436c917c427cd5c2e408860937e6f7ab80816ca47",
"password_file": "0851f5ec43e0b702bfcb9d8ec208085b51c0fc5200234901407c176327aa0b026e0a6082dd29936c44b47ecb8a5fe72e4b321a0ac314b0080ca4c48afdabd2150278b006042d011bdca5d0058a978d2103a6d546de311a2e8cd025fbc67303a4687618b32fe2ec2a5c2b6efecec1e6e535106de80af68733673daf0b644965966fa3f279e532d7ecef363f8d55ff6df4c473cfb1049a73f632972bfcc6744185d13a671dd6678d49fb1629a6fbfbe266937378fd9e772c2dd72692d1a35c020010",
"export_key": "8197f91f0d4de1ab126d8dfd06abd0d5df420ce40a135ef376e4ffe515930f413632390e7dc3dcfd19afff62b9113e10eb6c359fc327df6e9ad4d0f06c242322",
"session_key": "8fcd73add52d233a454d6b20125e9506a95aae8772ebbfac4d70efe1fb10078fd40f93d84aa7db53853ca74436c917c427cd5c2e408860937e6f7ab80816ca47"
"registration_request": "3a5d9fb60e6e91e97da6ca425a7ae8c5defce2ac0c6d89a8189f42f928fa3f04",
"registration_response": "145476844f947d9b639540c66045fc0e9c755d7a03c10ed618135fcd53cbdf30c44776fd33a5fb022bf6e26c4a5cb2c1ddbb785bd5822324df8eca6d80c1d279",
"registration_upload": "e6201a178ea486df66929dc15e6b7cf522a02c4ff05afdfe221bbda08f9c514276430085ed2a1f6f9ec49e0ee31f886c806ef1a15b07b0e243a736f38cfed069f97788f2843fc74b8dd6906186af8b77503a5e7932b22c0b772741a475352581029b33bc17a2daa8346449bffe8c479202237a0e074ba82b88b6c4d122b22edac7f32699e8be005286b985ec51d2b37212b04ccfc566cdd38a941177c31cae8158949c584667bf6bec49701e2ee9ac080d51919607993e31e9abf6a31368072e293011a8fce6781db258f1ccdff6b603eed9f405891d72c6e00145d8939ed226ad",
"credential_request": "3a5d9fb60e6e91e97da6ca425a7ae8c5defce2ac0c6d89a8189f42f928fa3f04a854353a0d88014af9eda6cb7ee3aff7d12fd4174c05221f3300d6ae12561ac00005696e666f31823dc5bf4e3479cf1be9cf2f9fe08f31a5171eb4ef012747b16364f4485e0022",
"credential_response": "145476844f947d9b639540c66045fc0e9c755d7a03c10ed618135fcd53cbdf30596b51d2a8a4485ae8d506684942cc2be327b72bac3ccf9864376264b2f5c5dba3f796185711239a9e6ef70e66331a52aaaa93629d44987f10a4374ac6a91ab27779da2bf6b2fe37bb9defb73c1a25fdfe8eb06d55d1e40588173b52d075853c57a919f0e088fbdbefa58659a011384145667a12c0ee7474d8f72e86ea52ed1de3617be02b90a90da1b7dd5d5f606da651ea97ceee7a00eb0d56ef76e587921d0a79ed45e37258a7933a31e0d378be00cbcfa1096b62feadbdf9612a33183909aea5d8174ade9f423a18438af605ee5b11047cf9973c21fffefcd336f690dd6103fca1950aac58db4be36a8902ef986dccf93ac6d8ac3203fb5ab2bed06996f27d0006d61f243b55af0cc2a44b7c0f3e060a9091b166cdf4dc3a1017aefd9c419462246f8b4998a853495fb13ebada79f98807d804189e03055047cbf7cf8374924aa137840342e566",
"credential_finalization": "716d4d7cb3090d6897cd01a693df9f8254b910c75a872f62dc5e08f5c23af724c3d499d0d0e8a888ab4ea3fc1eefb870ab64f0513a978a97dc0e99cd47d05142",
"client_registration_state": "ac66fda6a29b52b1c719453adea5370852f906448609601e7da7892c29017b0b70617373776f7264",
"client_login_state": "ac66fda6a29b52b1c719453adea5370852f906448609601e7da7892c29017b0b00673a5d9fb60e6e91e97da6ca425a7ae8c5defce2ac0c6d89a8189f42f928fa3f04a854353a0d88014af9eda6cb7ee3aff7d12fd4174c05221f3300d6ae12561ac00005696e666f31823dc5bf4e3479cf1be9cf2f9fe08f31a5171eb4ef012747b16364f4485e00220040288cece1cfbca7441b35fa4f6a1f4291855835e623c4874edefd735e507c3f0fa854353a0d88014af9eda6cb7ee3aff7d12fd4174c05221f3300d6ae12561ac070617373776f7264",
"server_login_state": "20508608406858c2038cd6ceef7ef4363648b341576b89f0fbf1c5dd262efade90e16a1cc74f73971fc7c37a0fca8209bf13b0015fefe3e32637d2e951b8500282a29681b6264fa2dd4c3b88cd7d654bdc8e8fb50ed6d78869b403978a1dd05a2c0c602037c1eba611ea2780c5c9ea68a620440adc9ec828e203590420207ad5396a1ffedeb4048f38e8197866c256dd034d3481615fa7f269a3512c99e461e21e8e1be737f7064ed8881205d9271c2c01453eef262140e0561fb64499abe863",
"password_file": "e6201a178ea486df66929dc15e6b7cf522a02c4ff05afdfe221bbda08f9c514276430085ed2a1f6f9ec49e0ee31f886c806ef1a15b07b0e243a736f38cfed069f97788f2843fc74b8dd6906186af8b77503a5e7932b22c0b772741a475352581029b33bc17a2daa8346449bffe8c479202237a0e074ba82b88b6c4d122b22edac7f32699e8be005286b985ec51d2b37212b04ccfc566cdd38a941177c31cae8158949c584667bf6bec49701e2ee9ac080d51919607993e31e9abf6a31368072e293011a8fce6781db258f1ccdff6b603eed9f405891d72c6e00145d8939ed226ad",
"export_key": "b01eea8b84b9fa10a179d8002cc782d69cc82c9c02c19200322457126624b24cf981c99257ec09b2d738b7a7a474b50149c8c855e63f2ba7aa0cfe4493334742",
"session_key": "396a1ffedeb4048f38e8197866c256dd034d3481615fa7f269a3512c99e461e21e8e1be737f7064ed8881205d9271c2c01453eef262140e0561fb64499abe863"
}
"#;
@@ -123,11 +126,13 @@ fn populate_test_vectors(values: &Value) -> TestVectorParameters {
server_s_sk: decode(&values, "server_s_sk").unwrap(),
server_e_pk: decode(&values, "server_e_pk").unwrap(),
server_e_sk: decode(&values, "server_e_sk").unwrap(),
credential_identifier: decode(&values, "credential_identifier").unwrap(),
id_u: decode(&values, "id_u").unwrap(),
id_s: decode(&values, "id_s").unwrap(),
password: decode(&values, "password").unwrap(),
blinding_factor: decode(&values, "blinding_factor").unwrap(),
oprf_key: decode(&values, "oprf_key").unwrap(),
oprf_seed: decode(&values, "oprf_seed").unwrap(),
masking_nonce: decode(&values, "masking_nonce").unwrap(),
envelope_nonce: decode(&values, "envelope_nonce").unwrap(),
client_nonce: decode(&values, "client_nonce").unwrap(),
server_nonce: decode(&values, "server_nonce").unwrap(),
@@ -141,7 +146,6 @@ fn populate_test_vectors(values: &Value) -> TestVectorParameters {
credential_finalization: decode(&values, "credential_finalization").unwrap(),
client_registration_state: decode(&values, "client_registration_state").unwrap(),
client_login_state: decode(&values, "client_login_state").unwrap(),
server_registration_state: decode(&values, "server_registration_state").unwrap(),
server_login_state: decode(&values, "server_login_state").unwrap(),
password_file: decode(&values, "password_file").unwrap(),
export_key: decode(&values, "export_key").unwrap(),
@@ -160,6 +164,13 @@ fn stringify_test_vectors(p: &TestVectorParameters) -> String {
s.push_str(format!("\"server_s_sk\": \"{}\",\n", hex::encode(&p.server_s_sk)).as_str());
s.push_str(format!("\"server_e_pk\": \"{}\",\n", hex::encode(&p.server_e_pk)).as_str());
s.push_str(format!("\"server_e_sk\": \"{}\",\n", hex::encode(&p.server_e_sk)).as_str());
s.push_str(
format!(
"\"credential_identifier\": \"{}\",\n",
hex::encode(&p.credential_identifier)
)
.as_str(),
);
s.push_str(format!("\"id_u\": \"{}\",\n", hex::encode(&p.id_u)).as_str());
s.push_str(format!("\"id_s\": \"{}\",\n", hex::encode(&p.id_s)).as_str());
s.push_str(format!("\"password\": \"{}\",\n", hex::encode(&p.password)).as_str());
@@ -170,7 +181,14 @@ fn stringify_test_vectors(p: &TestVectorParameters) -> String {
)
.as_str(),
);
s.push_str(format!("\"oprf_key\": \"{}\",\n", hex::encode(&p.oprf_key)).as_str());
s.push_str(format!("\"oprf_seed\": \"{}\",\n", hex::encode(&p.oprf_seed)).as_str());
s.push_str(
format!(
"\"masking_nonce\": \"{}\",\n",
hex::encode(&p.masking_nonce)
)
.as_str(),
);
s.push_str(
format!(
"\"envelope_nonce\": \"{}\",\n",
@@ -238,13 +256,6 @@ fn stringify_test_vectors(p: &TestVectorParameters) -> String {
)
.as_str(),
);
s.push_str(
format!(
"\"server_registration_state\": \"{}\",\n",
hex::encode(&p.server_registration_state)
)
.as_str(),
);
s.push_str(
format!(
"\"server_login_state\": \"{}\",\n",
@@ -269,15 +280,18 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
let mut rng = OsRng;
// Inputs
let server_s_kp = CS::generate_random_keypair(&mut rng);
let server_e_kp = CS::generate_random_keypair(&mut rng);
let client_s_kp = CS::generate_random_keypair(&mut rng);
let client_e_kp = CS::generate_random_keypair(&mut rng);
let server_s_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_e_kp = KeyPair::<CS::Group>::generate_random(&mut rng);
let credential_identifier = b"credIdentifier";
let id_u = b"idU";
let id_s = b"idS";
let password = b"password";
let mut oprf_key_raw = [0u8; 32];
rng.fill_bytes(&mut oprf_key_raw);
let mut oprf_seed = [0u8; 64];
rng.fill_bytes(&mut oprf_seed);
let mut masking_nonce = [0u8; 64];
rng.fill_bytes(&mut masking_nonce);
let mut envelope_nonce = [0u8; 32];
rng.fill_bytes(&mut envelope_nonce);
let mut client_nonce = vec![0u8; NonceLen::to_usize()];
@@ -285,6 +299,10 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
let mut server_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut server_nonce);
let server_setup =
ServerSetup::<CS>::deserialize(&[&oprf_seed, &server_s_kp.private().to_arr()[..]].concat())
.unwrap();
let blinding_factor = CS::Group::random_nonzero_scalar(&mut rng);
let blinding_factor_bytes = CS::Group::scalar_as_bytes(&blinding_factor).clone();
@@ -307,20 +325,16 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
.to_vec();
let client_registration_state = client_registration_start_result.state.serialize().to_vec();
let mut oprf_key_rng = CycleRng::new(oprf_key_raw.to_vec());
let server_registration_start_result = ServerRegistration::<CS>::start(
&mut oprf_key_rng,
&server_setup,
client_registration_start_result.message,
server_s_kp.public(),
&credential_identifier[..],
)
.unwrap();
let registration_response_bytes = server_registration_start_result
.message
.serialize()
.to_vec();
let oprf_key_bytes =
CS::Group::scalar_as_bytes(&server_registration_start_result.state.oprf_key).clone();
let server_registration_state = server_registration_start_result.state.serialize().to_vec();
let mut client_s_sk_and_nonce: Vec<u8> = Vec::new();
client_s_sk_and_nonce.extend_from_slice(&client_s_kp.private().to_arr());
@@ -340,10 +354,7 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
.serialize()
.to_vec();
let password_file = server_registration_start_result
.state
.finish(client_registration_finish_result.message)
.unwrap();
let password_file = ServerRegistration::finish(client_registration_finish_result.message);
let password_file_bytes = password_file.serialize();
let mut client_login_start: Vec<u8> = Vec::new();
@@ -363,6 +374,7 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
let mut server_e_sk_and_nonce_rng = CycleRng::new(
[
masking_nonce.to_vec(),
server_e_kp.private().to_arr().to_vec(),
server_nonce.to_vec(),
]
@@ -370,9 +382,10 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
);
let server_login_start_result = ServerLogin::<CS>::start(
&mut server_e_sk_and_nonce_rng,
password_file,
server_s_kp.private(),
&server_setup,
Some(password_file),
client_login_start_result.message,
credential_identifier,
ServerLoginStartParameters::WithInfoAndIdentifiers(
einfo2.to_vec(),
id_u.to_vec(),
@@ -401,11 +414,13 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
server_s_sk: server_s_kp.private().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(),
credential_identifier: credential_identifier.to_vec(),
id_u: id_u.to_vec(),
id_s: id_s.to_vec(),
password: password.to_vec(),
blinding_factor: blinding_factor_bytes.to_vec(),
oprf_key: oprf_key_bytes.to_vec(),
oprf_seed: oprf_seed.to_vec(),
masking_nonce: masking_nonce.to_vec(),
envelope_nonce: envelope_nonce.to_vec(),
client_nonce: client_nonce.to_vec(),
server_nonce: server_nonce.to_vec(),
@@ -419,7 +434,6 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
credential_finalization: credential_finalization_bytes,
password_file: password_file_bytes,
client_registration_state,
server_registration_state,
client_login_state,
server_login_state,
session_key: client_login_finish_result.session_key,
@@ -463,7 +477,7 @@ fn test_serialization() -> Result<(), ProtocolError> {
serde_json::to_string(&client_registration_start_result.message).unwrap();
assert_eq!(
registration_request_json,
r#""FLqG5TAYzlUH0r+y2YrT9g4wLYJr/zQQpexmnI4e8X0=""#
r#""Ol2ftg5ukel9pspCWnroxd784qwMbYmoGJ9C+Sj6PwQ=""#
);
let registration_request: RegistrationRequest<RistrettoSha5123dhNoSlowHash> =
serde_json::from_str(&registration_request_json).unwrap();
@@ -489,21 +503,21 @@ fn test_serialization() -> Result<(), ProtocolError> {
#[test]
fn test_registration_response() -> Result<(), ProtocolError> {
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
let mut oprf_key_rng = CycleRng::new(parameters.oprf_key);
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::deserialize(
&[&parameters.oprf_seed[..], &parameters.server_s_sk[..]].concat(),
)?;
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut oprf_key_rng,
&server_setup,
RegistrationRequest::deserialize(&parameters.registration_request[..])?,
&PublicKey::from_bytes(&parameters.server_s_pk[..])?,
&parameters.credential_identifier,
)?;
assert_eq!(
hex::encode(parameters.registration_response),
hex::encode(server_registration_start_result.message.serialize())
);
assert_eq!(
hex::encode(&parameters.server_registration_state),
hex::encode(server_registration_start_result.state.serialize())
);
Ok(())
}
@@ -539,12 +553,11 @@ fn test_registration_upload() -> Result<(), ProtocolError> {
fn test_password_file() -> Result<(), ProtocolError> {
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
let server_registration = ServerRegistration::<RistrettoSha5123dhNoSlowHash>::deserialize(
&parameters.server_registration_state[..],
)?;
let password_file = server_registration.finish(RegistrationUpload::deserialize(
&parameters.registration_upload[..],
)?)?;
let password_file = ServerRegistration::finish(RegistrationUpload::<
RistrettoSha5123dhNoSlowHash,
>::deserialize(
&parameters.registration_upload[..]
)?);
assert_eq!(
hex::encode(parameters.password_file),
@@ -584,15 +597,28 @@ fn test_credential_request() -> Result<(), ProtocolError> {
fn test_credential_response() -> Result<(), ProtocolError> {
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
let mut server_e_sk_and_nonce_rng =
CycleRng::new([parameters.server_e_sk, parameters.server_nonce].concat());
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::deserialize(
&[&parameters.oprf_seed[..], &parameters.server_s_sk[..]].concat(),
)?;
let mut server_e_sk_and_nonce_rng = CycleRng::new(
[
parameters.masking_nonce,
parameters.server_e_sk,
parameters.server_nonce,
]
.concat(),
);
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_e_sk_and_nonce_rng,
ServerRegistration::deserialize(&parameters.password_file[..])?,
&PrivateKey::from_bytes(&parameters.server_s_sk[..])?,
&server_setup,
Some(ServerRegistration::deserialize(
&parameters.password_file[..],
)?),
CredentialRequest::<RistrettoSha5123dhNoSlowHash>::deserialize(
&parameters.credential_request[..],
)?,
&parameters.credential_identifier,
ServerLoginStartParameters::WithInfoAndIdentifiers(
parameters.einfo2.to_vec(),
parameters.id_u,
@@ -675,9 +701,10 @@ fn test_complete_flow(
registration_password: &[u8],
login_password: &[u8],
) -> Result<(), ProtocolError> {
let credential_identifier = b"credentialIdentifier";
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng);
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng);
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
@@ -685,18 +712,16 @@ fn test_complete_flow(
)?;
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
&server_setup,
client_registration_start_result.message,
server_kp.public(),
credential_identifier,
)?;
let client_registration_finish_result = client_registration_start_result.state.finish(
&mut client_rng,
server_registration_start_result.message,
ClientRegistrationFinishParameters::default(),
)?;
let p_file = server_registration_start_result
.state
.finish(client_registration_finish_result.message)?;
let p_file = ServerRegistration::finish(client_registration_finish_result.message);
let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
login_password,
@@ -704,9 +729,10 @@ fn test_complete_flow(
)?;
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
p_file,
&server_kp.private(),
&server_setup,
Some(p_file),
client_login_start_result.message,
credential_identifier,
ServerLoginStartParameters::default(),
)?;
@@ -772,40 +798,11 @@ fn test_zeroize_client_registration_start() -> Result<(), ProtocolError> {
Ok(())
}
#[test]
fn test_zeroize_server_registration_start() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng);
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
STR_PASSWORD.as_bytes(),
)?;
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
client_registration_start_result.message,
server_kp.public(),
)?;
let mut state = server_registration_start_result.state;
let ptrs = state.as_byte_ptrs();
state.zeroize();
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
assert!(bytes.iter().all(|&x| x == 0));
}
Ok(())
}
#[test]
fn test_zeroize_client_registration_finish() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng);
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng);
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
@@ -813,9 +810,9 @@ fn test_zeroize_client_registration_finish() -> Result<(), ProtocolError> {
)?;
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
&server_setup,
client_registration_start_result.message,
server_kp.public(),
STR_CREDENTIAL_IDENTIFIER.as_bytes(),
)?;
let client_registration_finish_result = client_registration_start_result.state.finish(
&mut client_rng,
@@ -839,7 +836,7 @@ fn test_zeroize_client_registration_finish() -> Result<(), ProtocolError> {
fn test_zeroize_server_registration_finish() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng);
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng);
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
@@ -847,18 +844,16 @@ fn test_zeroize_server_registration_finish() -> Result<(), ProtocolError> {
)?;
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
&server_setup,
client_registration_start_result.message,
server_kp.public(),
STR_CREDENTIAL_IDENTIFIER.as_bytes(),
)?;
let client_registration_finish_result = client_registration_start_result.state.finish(
&mut client_rng,
server_registration_start_result.message,
ClientRegistrationFinishParameters::default(),
)?;
let p_file = server_registration_start_result
.state
.finish(client_registration_finish_result.message)?;
let p_file = ServerRegistration::finish(client_registration_finish_result.message);
let mut state = p_file;
let ptrs = state.as_byte_ptrs();
@@ -897,7 +892,7 @@ fn test_zeroize_client_login_start() -> Result<(), ProtocolError> {
fn test_zeroize_server_login_start() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng);
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng);
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
@@ -905,18 +900,16 @@ fn test_zeroize_server_login_start() -> Result<(), ProtocolError> {
)?;
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
&server_setup,
client_registration_start_result.message,
server_kp.public(),
STR_CREDENTIAL_IDENTIFIER.as_bytes(),
)?;
let client_registration_finish_result = client_registration_start_result.state.finish(
&mut client_rng,
server_registration_start_result.message,
ClientRegistrationFinishParameters::default(),
)?;
let p_file = server_registration_start_result
.state
.finish(client_registration_finish_result.message)?;
let p_file = ServerRegistration::finish(client_registration_finish_result.message);
let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
STR_PASSWORD.as_bytes(),
@@ -924,9 +917,10 @@ fn test_zeroize_server_login_start() -> Result<(), ProtocolError> {
)?;
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
p_file,
&server_kp.private(),
&server_setup,
Some(p_file),
client_login_start_result.message,
STR_CREDENTIAL_IDENTIFIER.as_bytes(),
ServerLoginStartParameters::default(),
)?;
@@ -946,7 +940,7 @@ fn test_zeroize_server_login_start() -> Result<(), ProtocolError> {
fn test_zeroize_client_login_finish() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng);
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng);
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
@@ -954,18 +948,16 @@ fn test_zeroize_client_login_finish() -> Result<(), ProtocolError> {
)?;
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
&server_setup,
client_registration_start_result.message,
server_kp.public(),
STR_CREDENTIAL_IDENTIFIER.as_bytes(),
)?;
let client_registration_finish_result = client_registration_start_result.state.finish(
&mut client_rng,
server_registration_start_result.message,
ClientRegistrationFinishParameters::default(),
)?;
let p_file = server_registration_start_result
.state
.finish(client_registration_finish_result.message)?;
let p_file = ServerRegistration::finish(client_registration_finish_result.message);
let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
STR_PASSWORD.as_bytes(),
@@ -973,9 +965,10 @@ fn test_zeroize_client_login_finish() -> Result<(), ProtocolError> {
)?;
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
p_file,
&server_kp.private(),
&server_setup,
Some(p_file),
client_login_start_result.message,
STR_CREDENTIAL_IDENTIFIER.as_bytes(),
ServerLoginStartParameters::default(),
)?;
let client_login_finish_result = client_login_start_result.state.finish(
@@ -999,7 +992,7 @@ fn test_zeroize_client_login_finish() -> Result<(), ProtocolError> {
fn test_zeroize_server_login_finish() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng);
let server_setup = ServerSetup::<RistrettoSha5123dhNoSlowHash>::new(&mut server_rng);
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
@@ -1007,18 +1000,16 @@ fn test_zeroize_server_login_finish() -> Result<(), ProtocolError> {
)?;
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
&server_setup,
client_registration_start_result.message,
server_kp.public(),
STR_CREDENTIAL_IDENTIFIER.as_bytes(),
)?;
let client_registration_finish_result = client_registration_start_result.state.finish(
&mut client_rng,
server_registration_start_result.message,
ClientRegistrationFinishParameters::default(),
)?;
let p_file = server_registration_start_result
.state
.finish(client_registration_finish_result.message)?;
let p_file = ServerRegistration::finish(client_registration_finish_result.message);
let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
STR_PASSWORD.as_bytes(),
@@ -1026,9 +1017,10 @@ fn test_zeroize_server_login_finish() -> Result<(), ProtocolError> {
)?;
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
p_file,
&server_kp.private(),
&server_setup,
Some(p_file),
client_login_start_result.message,
STR_CREDENTIAL_IDENTIFIER.as_bytes(),
ServerLoginStartParameters::default(),
)?;
let client_login_finish_result = client_login_start_result.state.finish(
+248 -201
View File
@@ -7,7 +7,6 @@ use crate::{
ciphersuite::CipherSuite,
errors::*,
key_exchange::tripledh::TripleDH,
keypair::{PrivateKey, PublicKey, SizedBytesExt},
opaque::*,
slow_hash::NoOpHash,
tests::mock_rng::CycleRng,
@@ -46,9 +45,11 @@ pub struct TestVectorParameters {
pub server_private_keyshare: Vec<u8>,
pub client_identity: Vec<u8>,
pub server_identity: Vec<u8>,
pub credential_identifier: Vec<u8>,
pub password: Vec<u8>,
pub blind_registration: Vec<u8>,
pub oprf_key: Vec<u8>,
pub oprf_seed: Vec<u8>,
pub masking_nonce: Vec<u8>,
pub envelope_nonce: Vec<u8>,
pub client_nonce: Vec<u8>,
pub server_nonce: Vec<u8>,
@@ -71,196 +72,214 @@ static TEST_VECTORS: &[&str] = &[
r#"
OPRF: 0001
Hash: SHA512
SlowHash: Identity
MHF: Identity
KDF: HKDF-SHA512
MAC: HMAC-SHA512
Name: 3DH
EnvelopeMode: 01
Group: ristretto255
Nh: 64
Npk: 32
Nsk: 32
oprf_seed: 7bc32c4249689ebdf218d04a2cbfb8d06850d4f1d1acb2b0413b9b3e40
b45b3f9f4647df5bbf6dd32e7d41f7dbc2ddf053f047cbf26a684f2b341ad7459373f
1
credential_identifier: 31323334
password: 436f7272656374486f72736542617474657279537461706c65
envelope_nonce: cc7abb200199d5071c94efa49fb62435d3e70d03cf9573a95da54
20d3eebcd2b
client_private_key: 8bcb0b70dac18de24eef12e737d6b28724d3e37774e0b092f
9f70b255defaf04
client_public_key: 360e716c676cfe4d9968d1a352ed3faf17603863e0a7aa1905
df6ea129343b09
server_private_key: f3a0829898a89239dce29ccc98ec8b449a34b255ba1e6f944
829d18e0d589b0f
server_public_key: 66e130c6eb5b41f851b235b03a0eafeaa883f64147bc62cb74
9c22c762389c3c
envelope_nonce: 69f191aeb1af61c1feea7688a7c433a645d0f81c3168b4558b3f1
d83d08b7a25
masking_nonce: 154b361eb3a95bcb64700b8c26898a1e2f78eeb6232b2361b84721
778ca93686
client_private_key: 533c2e6d91c934f919ac218973be55ba0d7b234160a0d4cf3
bddafbda99e2e0c
client_public_key: a07d9609083613e2d7521b8f77f1cd7a07d89ea03aa0045080
775edc37949341
server_private_key: 3af5aec325791592eee4a8860522f8444c8e71ac33af5186a
9706137886dce08
server_public_key: 4c6dff3083c068b8ca6fec4dbaabc16b5fdac5d98832f25a5b
78624cbd10b371
client_info: 68656c6c6f20626f62
server_info: 6772656574696e677320616c696365
server_nonce: 98b8081059f60ffed9336f026fd8e124737205ac73f5348ae5bebdb
49456c70f
client_nonce: 58dc21475ff730342f807bf031c7ae47a11f0d4dfaa63a7feb15d7e
36427ca44
server_keyshare: 5214e3ddc73db786480b79fa2da787f2080b82cbe922c2a9592b
44597d9a702e
client_keyshare: a4084c7296b1a3d5a5e4a24358750489575acfd8fcfa6e787492
b98265a5e651
server_private_keyshare: c4d002aa4cfcf281657cf36fe562bc60d9133e0e72a7
4432f685b2b6a4b42a0c
client_private_keyshare: de2e98f422bf7b99be19f7da7cac62f1599d35a225ec
6340149a0aaff3102003
blind_registration: 7e5bcbf82a46109ee0d24e9bcab41fc830a6ce8b82fc1e921
3a043b743b95800
blind_login: c4d5a15f0d5ffc354e340454ec779f575e4573a3886ab5e57e4da298
4bdd5306
oprf_key: 080d0a4d352de92672ab709b1ae1888cb48dfabc2d6ca5b914b335512fe
70508
auth_key: 7bb7f2b831ee30d3e5cc4012c8f721a4d8f9dd494932d53776e043df9bd
2aa284025b8b006fd8449536446ff50698f46c73fccb53f20d80898f185307d1d39e5
prk: b0aefddbb21d1b97bc40c07b172e0bf172ec740de4f6274f69d46350a447e9b1
b3fb1e4cefc7d8e393ff58a5c45c74d0615ee0eecde116f3d4e744142eb2ee89
pseudorandom_pad: 36a828b3b57bf242c4c47ccd9cb84e5b3cefaffe09629c6b94d
eba0ccec5fa39
envelope: 01cc7abb200199d5071c94efa49fb62435d3e70d03cf9573a95da5420d3
eebcd2bbd6323c36fba7fa08a2b6e2aab6efcdc183c4c897d822cf96d29b129932a55
3d469ffa9999fcbd37a1e8b6c1e579bcf83fed355c9ff413e6158d72d16f3ccd8699e
906027842694b6293b6303bbb7f324e0fccb4ae0f01edb60ee1d32992696e
handshake_secret: 2b041dcf12ac9b75dded88f891c25d76746ce9e2c1a43118ac4
aa5721cdc1bc2f0691e6c012a1ea9eb95ab4899b3e7058d37fe9546c46b0511877e40
f55aac6c
handshake_encrypt_key: ceef10f15d869a4cea8174fa98d0d96c7aaf8602d006fe
0c5274a40173db76cac820138c5890bb63fb974d1e3e925850cc2464e2c10f0a9a776
9a45e80889b1e
server_mac_key: f8fd7fdc349b5ae1339515e05912c89a795f561a117cdc84d8d8b
5f05b05751abfb87fa01c799c5d367244d1e32eab67ff926833c6025c556acffa4af1
f3871a
client_mac_key: 92a30cc82c374c06895aa07e81f0cf5f25309a24b595faefcd225
1f9219b47e47d17da4fe8b572dedefa350ed365f87b217973e90d0b647a2ccf1d796a
8970f6
registration_request: ec9027daa5e9a901d641286a7ded51364142936ac7636e1
42e3f4368b4bd8124
registration_response: 8867d7c8c2c576a6322d49d46078ea32f479aed917c70a
636d3ada4397ea1c0e66e130c6eb5b41f851b235b03a0eafeaa883f64147bc62cb749
c22c762389c3c
registration_upload: 360e716c676cfe4d9968d1a352ed3faf17603863e0a7aa19
05df6ea129343b0901cc7abb200199d5071c94efa49fb62435d3e70d03cf9573a95da
5420d3eebcd2bbd6323c36fba7fa08a2b6e2aab6efcdc183c4c897d822cf96d29b129
932a553d469ffa9999fcbd37a1e8b6c1e579bcf83fed355c9ff413e6158d72d16f3cc
d8699e906027842694b6293b6303bbb7f324e0fccb4ae0f01edb60ee1d32992696e
KE1: e06a32011e1b1704eb686b263e5d132fff4e9f6429cd93b98db107485006792c
58dc21475ff730342f807bf031c7ae47a11f0d4dfaa63a7feb15d7e36427ca4400096
8656c6c6f20626f62a4084c7296b1a3d5a5e4a24358750489575acfd8fcfa6e787492
b98265a5e651
KE2: 66f6b5fa1a4eb6bd7a0c93ed2639a31cba0d02e2df744003641d5a30a4a12364
66e130c6eb5b41f851b235b03a0eafeaa883f64147bc62cb749c22c762389c3c01cc7
abb200199d5071c94efa49fb62435d3e70d03cf9573a95da5420d3eebcd2bbd6323c3
6fba7fa08a2b6e2aab6efcdc183c4c897d822cf96d29b129932a553d469ffa9999fcb
d37a1e8b6c1e579bcf83fed355c9ff413e6158d72d16f3ccd8699e906027842694b62
93b6303bbb7f324e0fccb4ae0f01edb60ee1d32992696e98b8081059f60ffed9336f0
26fd8e124737205ac73f5348ae5bebdb49456c70f5214e3ddc73db786480b79fa2da7
87f2080b82cbe922c2a9592b44597d9a702e000f72f38a0945819089c44c86820c51d
89cc35f77df03d330101bbed3b2f69066112f32529bdda0998657350fc9f8da4cde73
408ad931f4c2ea6237ccae4696483388b174f50cf96d439139b0f8680c3b
KE3: 9f0e4f73455ca9fe06bb52ad02670b09be5a03db11a73be4422f19963be082b0
eb55871022e8d1d87adc3ab50de7c738058eb659866d091648f2fed12e23fd53
export_key: 66c0b72aa829f13a166fb1a1168f1e26023921f0eed1126def4f81ba0
4924ad6012e42b63656ec199ba27670d1e7f23dc0a927714edc140134dde5a5d2063d
fc
session_key: 951c2bb1b876725fa7d3829db791dddd406a688507b47e24101bd0cc
5d071760b6fba59e8758a6ea6d7e5f51a715b49a47c50fee9a7c8a0451243c3ee837f
d30
server_nonce: a71b6f2ff4c8baae05637f574deec70050dffda1f68d10e8648c838
b696e1918
client_nonce: acd3d4ffff4667b6a6b2d82b95bb8a171caacaa063e102a3a10077a
a6c7ac211
server_keyshare: ca372e52516d51c19763ad5eb1a5b60dafb68c264dcf6bcc692f
667a71c5a617
client_keyshare: 4c415eebd7a9bb5f921cbcfc5863e48c9e79fd2ecc1788e2b616
bea0853f627a
server_private_keyshare: 080d0a4d352de92672ab709b1ae1888cb48dfabc2d6c
a5b914b335512fe70508
client_private_keyshare: 7e5bcbf82a46109ee0d24e9bcab41fc830a6ce8b82fc
1e9213a043b743b95800
blind_registration: 8bcb0b70dac18de24eef12e737d6b28724d3e37774e0b092f
9f70b255defaf04
blind_login: f3a0829898a89239dce29ccc98ec8b449a34b255ba1e6f944829d18e
0d589b0f
oprf_key: f993af4bc97f6e752da6d97eb0a489a68ec1dc2f3327e3f480880f26a2b
73b0a
auth_key: 8ec5878a7d305c1252f11e4c1a5b8219919a4b5b71c85ab3130d0b8cbd9
e5302669a3b1b4275b5ffb67b33db3955eac60b37bea96d3dfad169be702720a00b3b
prk: 2e400d7a64d6abbe9d36a83f9b5a89bbc1cef2fef1094292c4b76b6a3c1d987e
de2228b6d36ae174bc304302af542b6ccb1e7f2fb6bd302b30536112cc11110e
pseudorandom_pad: b21d130015d7f6d33bbf27f8e1ea3d9a7c7c05f94a6f197bc85
1313f4a4f2d2f
envelope: 0169f191aeb1af61c1feea7688a7c433a645d0f81c3168b4558b3f1d83d
08b7a25e1213d6d841ec22a2213067192546820710726b82acfcdb4f38c9e82e3d103
234e3f37b5afd99144411550b9dc3f4f0c920d923cdfb952f5a9050a6c97440230262
4bc5da9563a768f73b195f992741e399cfa9d95eb48b220998b59360b01a3
handshake_secret: 7b416efcce7df41f7b9698c68a6b278286b89d378f5b3fac7f4
d96a0d45f5457e2c4ca2f3bf693325a8710d223a988aedcab7a4deb4892ddb7d13dec
1989d298
handshake_encrypt_key: 122f419c0457e01dda8382e65e92b614015b37cba09666
92c6363f28fad97d00d2a0e6ab112e39a81a11aae074696e2e10b433918c5ffca6114
43a7bea8ad353
server_mac_key: 9aeaacea81de96d2115bc9f86c1bedfe04357afc69a16c4e3c0bc
eb07409d5a9e4f76df2844f5f493feb55c9384af39bb9bd3e928cb796b2c8f1dc03a9
ecf93d
client_mac_key: ee1ccd9c1d66bd6137aec52e323de67c914a35980fc384adeb15e
d10dfea0c389ffaebd889ee36ec4e03253f814e07d93b13e1d8c9a9fe683ea39c6b9d
58e53f
registration_request: 24bbcabb15452642f709cb8567eff38f4cda6044aca3356
87a62b8453d849c18
registration_response: fe8865a1916ad735de5e131930f14922582e3205665cbf
2e9e2f3fbcfb776e2b4c6dff3083c068b8ca6fec4dbaabc16b5fdac5d98832f25a5b7
8624cbd10b371
registration_upload: a07d9609083613e2d7521b8f77f1cd7a07d89ea03aa00450
80775edc379493418183c015b1b27290fc3cf14c963da55f59ed34f70ef871bcc9888
9d53039e5c589b3474f1e3439bfefaf55100334a578ca3d5ec8437d7b06ba02689536
e8b6ae0169f191aeb1af61c1feea7688a7c433a645d0f81c3168b4558b3f1d83d08b7
a25e1213d6d841ec22a2213067192546820710726b82acfcdb4f38c9e82e3d103234e
3f37b5afd99144411550b9dc3f4f0c920d923cdfb952f5a9050a6c974402302624bc5
da9563a768f73b195f992741e399cfa9d95eb48b220998b59360b01a3
KE1: 0e8eeeb2ca0dbf5f690cfe0b76783d7667245f399b874a989f168fdd3e572663
acd3d4ffff4667b6a6b2d82b95bb8a171caacaa063e102a3a10077aa6c7ac21100096
8656c6c6f20626f624c415eebd7a9bb5f921cbcfc5863e48c9e79fd2ecc1788e2b616
bea0853f627a
KE2: 085ab15bca16570dfea48efee2e12d1b13b95c1530a5e9352acb6fc10485353f
154b361eb3a95bcb64700b8c26898a1e2f78eeb6232b2361b84721778ca9368696b92
2224f1082627dcb1912944d5df179fe27633a6deff35649bd8192f6c2012241823265
51d7b485a071b2af1fb86dea63a8e9d3b40cd43bf42e97bb9d1763508478412c1a495
1add4c82e622e2a14b612376364d7fd3a3cdfc1bb36bb789afe1567cbf1c10a006eb8
85ef412395e6d4a1823197254fb5d1a1345228712fd27ed49355735d30dbf548fea57
48cfdc3b58e8062d0060e43e12c6a4b3788682c62a71b6f2ff4c8baae05637f574dee
c70050dffda1f68d10e8648c838b696e1918ca372e52516d51c19763ad5eb1a5b60da
fb68c264dcf6bcc692f667a71c5a617000f723180269119d5c2c2348cabb9ed372e24
70210d9de3ff1970b449d7bac51d0a3b10d58d3f8c58d5698230d6dc102794f658538
7b6b412a9ce973109ebeb62ea444c46180448a3a0af89055786d7ce
KE3: 737427092049117f0a75e42515b81572c9e971687509bf7919950fe078bc6cfd
0cd8e7f32232f6eb296a000c2df29f6b65a4a858229766526380d687eda004d4
export_key: b3816d8a69b2a3f4d15113ae5b474353e99fbad6be7e331376688cc94
6e19e063a604ee3532981e756fe60f0a3822b5881729752e2309ee21072b215e82bd4
25
session_key: 3cf3ce41bd59b6e299aab4f26575f8bb8d4d8b12d2406c0d7a3eed92
11387c363949199665317c5500aef2fb8aa081aa7d74191b92b928382d57b03f13140
9cf
"#,
r#"
OPRF: 0001
Hash: SHA512
SlowHash: Identity
MHF: Identity
KDF: HKDF-SHA512
MAC: HMAC-SHA512
Name: 3DH
EnvelopeMode: 02
Group: ristretto255
Nh: 64
Npk: 32
Nsk: 32
client_identity: 20fa92f2e4b7ea5b5e677ac4930ff3b93b0043481ab70bc613b2
e16a6dde6b05
server_identity: eae9dfa6b8348d34418c32d385e1eac99efbce1af320901f7c8e
de8d6d272c65
client_identity: 36840cc4f3fd4f57bad888ff6e75a120a6ae132b128df738fbc3
16e5bd57356a
server_identity: 3e2651ff8442883bb83ec1b46a76f99a556ab182fd4828da3fe9
65b145a0dc7f
oprf_seed: 258b268d6ec9a468c30e7f009e5d631a31dede64596c8dde12e377d319
3efe2c90e609e135c4bf7d2c7326306ba1f45c510ca9d2dfe4816c680fadfef82bfbe
c
credential_identifier: 31323334
password: 436f7272656374486f72736542617474657279537461706c65
envelope_nonce: f41e8b3c5a999aa946f9b562a150e5c5e36748a31a79feb241809
0438877888c
client_private_key: dc70a99bbabf1ebe98b192e93cedceb9c0164e95b891bd8bc
81721b83d66b00b
client_public_key: 20fa92f2e4b7ea5b5e677ac4930ff3b93b0043481ab70bc613
b2e16a6dde6b05
server_private_key: 709687a36c94592ab76579f42ce1be6961f0700496e71df80
6ebd5320554720d
server_public_key: eae9dfa6b8348d34418c32d385e1eac99efbce1af320901f7c
8ede8d6d272c65
envelope_nonce: e8cd944521ab8459398b39ef6c8b2bc12473281ec34a3220db1c7
88fad2a001b
masking_nonce: 24907c8e1151cae5dfb583a99ec1d74d93166e6ee5089137e7a924
a11d60137b
client_private_key: ed811b4cca7c0e51a886c4343d83c4e5228b87399f1dbf033
ee131fe4ad75c05
client_public_key: 36840cc4f3fd4f57bad888ff6e75a120a6ae132b128df738fb
c316e5bd57356a
server_private_key: 0db27eb7aef2af92c3b297c662a87631531aade91c0558d87
224d922a8573f08
server_public_key: 3e2651ff8442883bb83ec1b46a76f99a556ab182fd4828da3f
e965b145a0dc7f
client_info: 68656c6c6f20626f62
server_info: 6772656574696e677320616c696365
server_nonce: ef49d83cef5f1411ea30abb82b08bd85423aadb86e2c19df5930b3c
8498b9f97
client_nonce: 4ab1227db632bc079f79c0f5279df2dfa75cfbd4434ab40dcf844d6
77165cd3b
server_keyshare: 96a9587e233e67f2397f10fec6355b68102534f1f1b115b4ddf7
485840efcd7c
client_keyshare: 54f35db3a52fb0cf2a97918a6987993231d227e28711eaef19a3
e5033632611a
server_private_keyshare: 6650d64df70618a878504ce73dcca27b1af125c67e48
1e7bd49d0b24709b200f
client_private_keyshare: ebb01c59f99bc955df622548e247f7ef180732909ff3
c5f87ff8c7867b8be704
blind_registration: 308f1d3fa1fea402f3c90b04601274050a3c6f467387c2f48
878823949b0e109
blind_login: 141e21373228a44b09d4c00da9a6bbaf9a5e54a1687c07f327833643
4245510b
oprf_key: b7126967aa0cb69c311b71343843ea041bae30e2bde41b548b8fbd8bced
97604
auth_key: 9f761a5a56a74269b382403aeba47c1d24b9e200e1839efcb616fb280da
1b6ba7bd71d6455dd3cd979c545608cfbfc1c4e9ba677e1d40848054a00696c4b2589
prk: 30bc3f37a757890ac17ce46043f3c5ed30c96fb8743205e77e84dc167d98e114
6093150ff7b4d002f793bfe717e88d174ed2669abdd9e96af473a7ac82973b0a
pseudorandom_pad: b909f990c94b9c5949a2b8d0874d602f846b7981b331fd79978
b530cc46c8670
envelope: 02f41e8b3c5a999aa946f9b562a150e5c5e36748a31a79feb2418090438
877888c6579500b73f482e7d1132a39bba0ae96447d37140ba040f25f9c72b4f90a36
7bdb425fa1dd4c49e17780f33b821e1e019668fe7f45520e26996ac8cb08e3d2566cc
439c83030464effecb8350e7b1ca31087d87f6a45ed3910c185a24a89d282
handshake_secret: ff89f264f8c3974238f4c8d736af7b0a55f2e4edc487cbf3e5c
7b4bbf21acd7c1d28354c2c8555fba57c4d4b1fbb4b772bfdf909881f67dd517cc9f4
f6ebaeac
handshake_encrypt_key: 3071b181f639062cf70b74d0ffe5ec8fa695da13cd2f00
e74b8b7ef348ae7a5df9c3a32c9f7aeaad5a28379712cf849b9707e221dce124abfad
d0225a8e8e045
server_mac_key: 7c37b344d189cbbeff80bbb4b78e2703d1a80dc28239923094287
62f7ce2a93b11f6e85dd45c02809afe8583d4aad6377e72788773af92eef33c690692
20ae76
client_mac_key: 2c4aff12ba7aa911a51f9e5b7a7c01439d854c97e4b8ec842a9db
d78345760328fd5a72424e49e25ec8fe1b6d9d42f774516f400948bd5a105d995d000
2fc83b
registration_request: 3c8b89966e261a5aaf7aeb6dcdd94c87ce311bf197221b8
7ef44632d58f18a05
registration_response: caf9243d7ef3e267815632bf79c85a27a23f218a438815
2a523f6a310949807beae9dfa6b8348d34418c32d385e1eac99efbce1af320901f7c8
ede8d6d272c65
registration_upload: 20fa92f2e4b7ea5b5e677ac4930ff3b93b0043481ab70bc6
13b2e16a6dde6b0502f41e8b3c5a999aa946f9b562a150e5c5e36748a31a79feb2418
090438877888c6579500b73f482e7d1132a39bba0ae96447d37140ba040f25f9c72b4
f90a367bdb425fa1dd4c49e17780f33b821e1e019668fe7f45520e26996ac8cb08e3d
2566cc439c83030464effecb8350e7b1ca31087d87f6a45ed3910c185a24a89d282
KE1: 8261a1efd78bea73faf256a23c200d729259886530fa43b875c1ca124b09bc7e
4ab1227db632bc079f79c0f5279df2dfa75cfbd4434ab40dcf844d677165cd3b00096
8656c6c6f20626f6254f35db3a52fb0cf2a97918a6987993231d227e28711eaef19a3
e5033632611a
KE2: fa1f33a43a03123ebe35345ef93aa23b57ea8bfbee7022b05a179d60768ba02e
eae9dfa6b8348d34418c32d385e1eac99efbce1af320901f7c8ede8d6d272c6502f41
e8b3c5a999aa946f9b562a150e5c5e36748a31a79feb2418090438877888c6579500b
73f482e7d1132a39bba0ae96447d37140ba040f25f9c72b4f90a367bdb425fa1dd4c4
9e17780f33b821e1e019668fe7f45520e26996ac8cb08e3d2566cc439c83030464eff
ecb8350e7b1ca31087d87f6a45ed3910c185a24a89d282ef49d83cef5f1411ea30abb
82b08bd85423aadb86e2c19df5930b3c8498b9f9796a9587e233e67f2397f10fec635
5b68102534f1f1b115b4ddf7485840efcd7c000f7ebe71d4ab326006a3aeca802435d
c995a38ac6662221f974cb920992d82b8ef8d147c77e29b628a82b5ccb01ea2f7bb60
af94cd1860e1bd974a11a1c9bd827789f663c4758eb71058c244138de0c2
KE3: d81f93397cdba85a43993d4d9afbdc67f147adfa2b223213b19692cb820eef48
5073eda4c8236b2f47702404ad60d9a875d189626fc7b7cc861825385470ae54
export_key: 03192555940b5b42e64e6200bf55cc701f1bace3d402a2f8d83977843
51a1e3fa1f07a471b783b208acb1d92be47903b6fa3a0df9f4d4b7956ee4f431e2950
f6
session_key: 58a7fa98bf3b7b52da21406abfb11d98734354edd47d7b32462c0513
f0617c89824ea6031d4147a86fc9f6c6837ce640c12fb937d764f296d1a9421ad1b2a
5d5
server_nonce: ee9675eb495049b7d24c33691d9b150406e646d1f6f2911bf5c665b
cb71e3649
client_nonce: 10603d5ae57cdd698410d49769c00bc248ebba0f5709ba9d6179ab4
42d8a883c
server_keyshare: 264af8bc6a2c78acb503cd838ae3e5e3715df02d19dd4ddfbca9
e4f46b0a0e2d
client_keyshare: 223228d3df70ac6e0b179a48609517304386692952f49cff086e
0bea06f5363f
server_private_keyshare: 94b049e1b0d73ab5b8d914b08dff3e52e62ea8898d35
b2862d28ff4c2bb1a607
client_private_keyshare: 362f233a8a73971925abc79daa9fcc06f6d3acf12df8
2de919be4937fe716a03
blind_registration: 9b53af4cbdb352b0a2016e5e5f6c0bee4a642526ef9910289
315b71feff26f0f
blind_login: 275e46a6aea42c40b78bd2f1281617519f3f790c8d0f42eacce68456
380b8405
oprf_key: 09da9c8c1ff925937a04c07b613a8bcdf1335db32e11db0a4ee45bfc297
2380f
auth_key: 20e401aeb76dac20c8f5054685d2531aa3ebe53569b5a50d9d34d19b93b
e7488d7e7b6f0ae0f5c36a876464ac0b69aa2199438d7ac1be69059dea6394dcbe067
prk: 91a4c98e88faa5f30fd0bdc2fb2f0cc808d6808822d079e251bef46a399e4a25
6bd3c6207bd01b22bdd713ca3c2c3e1945bfddbbce193e5073c9f47b29928279
pseudorandom_pad: c08e7bb57b5b4e6c866119a8d29c51f4f97378e938342f596ff
56075bb06dcf0
envelope: 02e8cd944521ab8459398b39ef6c8b2bc12473281ec34a3220db1c788fa
d2a001b2d0f60f9b127403d2ee7dd9cef1f9511dbf8ffd0a729905a5114518bf1d180
f5023e24c1dba1c8940220cf5475fb7d73419a4896096b37403ccdec1da295365cad4
a423674850602e44a524ca3ecf7a3364ab106350e29cfae3937489b30601c
handshake_secret: 54a0b775fda0a405c3e2b585ffd427f8002ff6ed14fb13dbb2f
c2597984e14b40830c131a6c72035fd40a0c0ac7a24ace5682d73ac1e3a2dff0d36b3
75cd3146
handshake_encrypt_key: 2ecbf7901784137571505028bcd2c119c77038ed024d87
20810c50e076fbf01be82326df2d3c52a5fa2da623603c65caa078002abb9c9d0d5f7
07c5fcade01bf
server_mac_key: fc7bafe7c51426b18bee3fa7cd895c145ba36481946f4fa7b4355
3746331ec201891eb3b3cf28590569e3f7d0863da5542ff728c31282bba2ebcc642be
533fa2
client_mac_key: da7cb58102085bb05de9e175a004e862c653aac24a529f416b33e
3308cf9a9cd9d15e655c02f44d24f8e8e257a27e4db82049ba1eaa456a4f0ba73cfc4
ef1ef4
registration_request: 1e026d981ad38a4c03e5785f151fc42cf932ec153a1134a
3e6f7f3cb9b2c632d
registration_response: d4d75537ab05e41746c6ed5de6d985e8d08e47a433f04e
9b97f0be760dc870093e2651ff8442883bb83ec1b46a76f99a556ab182fd4828da3fe
965b145a0dc7f
registration_upload: 36840cc4f3fd4f57bad888ff6e75a120a6ae132b128df738
fbc316e5bd57356a1008b12b8ab4c890f937b961554635298ea2696433c7a285c7df2
46da4bef777cc3b4b1004222263792be00050b20385c6f22abd40dc61bcfa823e3c83
4f4fa702e8cd944521ab8459398b39ef6c8b2bc12473281ec34a3220db1c788fad2a0
01b2d0f60f9b127403d2ee7dd9cef1f9511dbf8ffd0a729905a5114518bf1d180f502
3e24c1dba1c8940220cf5475fb7d73419a4896096b37403ccdec1da295365cad4a423
674850602e44a524ca3ecf7a3364ab106350e29cfae3937489b30601c
KE1: be5993d16412c8452d6b320ea8025a8f0b405a0d62dce14bee5dda17c3ef2645
10603d5ae57cdd698410d49769c00bc248ebba0f5709ba9d6179ab442d8a883c00096
8656c6c6f20626f62223228d3df70ac6e0b179a48609517304386692952f49cff086e
0bea06f5363f
KE2: a2668282db653a9ed70e46556a78381bba08a8163a71463afc499289a2bf9632
24907c8e1151cae5dfb583a99ec1d74d93166e6ee5089137e7a924a11d60137b6e10b
ed0e73a37ea9617af60b3d295d6bd0761c2acdaae6a88614bd10f2ce5cd5dcec97b3e
bdd1508e8aebdb48f86bec27dc21aaf6e0aaf67cd44b62a28b4377b4dee4634922bfb
7b30550fafd287d00c3a2776f4c31f5a568b2d564cc9281b9cb2fea2ec45feba2d7f0
0fe1f595647a515b8f7178139f668720de10c8e5d41169a79f7809d120ebe8b156855
4ebc9cf4b85454bdfca44cb1d4552871e0ffb1373ee9675eb495049b7d24c33691d9b
150406e646d1f6f2911bf5c665bcb71e3649264af8bc6a2c78acb503cd838ae3e5e37
15df02d19dd4ddfbca9e4f46b0a0e2d000f5e02b66c4e83b9933b4ff5dc10a8e5e064
3cbf26f738bca0d25b0db9740dcbabbeb23e54c59deec56dab96bc96daa727aa0b730
bf81e15d82641f49bc5d4c998c2bd0416f27de631f445c93e43522f
KE3: 01be33d72a3d430b0bf3d5e3ef3450652c23354e818460b315a644635882899f
4f6746efabb3da6a5a8a645bbeba96d32d133e8d6e0ec13e96af61c74fe814f5
export_key: 3d4d4d6c586ef538d08756f5a1a04cc190aee59e8dea997d0aaff89a8
da58ac712fb5002666d8fb09b18a4f46a54a858592dc3c0b683a37a06d5a29728349b
91
session_key: eaa0057dde12425c05b185fc490579987638a9a2d56dece67db5acc3
b526241174635a7ffff2c2ad6359a82eff279cc23c8f6768dcbffc801420fbfd6f147
ae2
"#,
];
@@ -330,9 +349,11 @@ fn populate_test_vectors(values: &Value) -> TestVectorParameters {
server_private_keyshare: parse!(values, "server_private_keyshare"),
client_identity: parse!(values, "client_identity"),
server_identity: parse!(values, "server_identity"),
credential_identifier: parse!(values, "credential_identifier"),
password: parse!(values, "password"),
blind_registration: parse!(values, "blind_registration"),
oprf_key: parse!(values, "oprf_key"),
oprf_seed: parse!(values, "oprf_seed"),
masking_nonce: parse!(values, "masking_nonce"),
envelope_nonce: parse!(values, "envelope_nonce"),
client_nonce: parse!(values, "client_nonce"),
server_nonce: parse!(values, "server_nonce"),
@@ -351,17 +372,9 @@ fn populate_test_vectors(values: &Value) -> TestVectorParameters {
}
fn get_password_file_bytes(parameters: &TestVectorParameters) -> Result<Vec<u8>, ProtocolError> {
let mut oprf_key_rng = CycleRng::new(parameters.oprf_key.clone());
let server_registration_start_result =
ServerRegistration::<Ristretto255Sha512NoSlowHash>::start(
&mut oprf_key_rng,
RegistrationRequest::deserialize(&parameters.registration_request[..]).unwrap(),
&PublicKey::from_bytes(&parameters.server_public_key[..]).unwrap(),
)?;
let password_file = server_registration_start_result
.state
.finish(RegistrationUpload::deserialize(&parameters.registration_upload[..]).unwrap())?;
let password_file = ServerRegistration::<Ristretto255Sha512NoSlowHash>::finish(
RegistrationUpload::deserialize(&parameters.registration_upload[..]).unwrap(),
);
Ok(password_file.serialize())
}
@@ -386,12 +399,18 @@ fn test_registration_request() -> Result<(), ProtocolError> {
#[test]
fn test_registration_response() -> Result<(), ProtocolError> {
for parameters in rfc_to_params!(TEST_VECTORS) {
let mut oprf_key_rng = CycleRng::new(parameters.oprf_key);
let server_setup = ServerSetup::<Ristretto255Sha512NoSlowHash>::deserialize(
&[
&parameters.oprf_seed[..],
&parameters.server_private_key[..],
]
.concat(),
)?;
let server_registration_start_result =
ServerRegistration::<Ristretto255Sha512NoSlowHash>::start(
&mut oprf_key_rng,
&server_setup,
RegistrationRequest::deserialize(&parameters.registration_request[..]).unwrap(),
&PublicKey::from_bytes(&parameters.server_public_key[..]).unwrap(),
&parameters.credential_identifier,
)?;
assert_eq!(
hex::encode(parameters.registration_response),
@@ -466,16 +485,30 @@ fn test_ke1() -> Result<(), ProtocolError> {
#[test]
fn test_ke2() -> Result<(), ProtocolError> {
for parameters in rfc_to_params!(TEST_VECTORS) {
let server_setup = ServerSetup::<Ristretto255Sha512NoSlowHash>::deserialize(
&[
&parameters.oprf_seed[..],
&parameters.server_private_key[..],
]
.concat(),
)?;
let password_file_bytes = get_password_file_bytes(&parameters)?;
let mut server_private_keyshare_and_nonce_rng =
CycleRng::new([parameters.server_private_keyshare, parameters.server_nonce].concat());
let mut server_private_keyshare_and_nonce_rng = CycleRng::new(
[
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,
ServerRegistration::deserialize(&password_file_bytes[..]).unwrap(),
&PrivateKey::from_bytes(&parameters.server_private_key[..]).unwrap(),
&server_setup,
Some(ServerRegistration::deserialize(&password_file_bytes[..]).unwrap()),
CredentialRequest::<Ristretto255Sha512NoSlowHash>::deserialize(&parameters.KE1[..])
.unwrap(),
&parameters.credential_identifier,
if parameters.envelope_mode == EnvelopeMode::CustomIdentifier {
ServerLoginStartParameters::WithInfoAndIdentifiers(
parameters.server_info.to_vec(),
@@ -549,16 +582,30 @@ fn test_ke3() -> Result<(), ProtocolError> {
#[test]
fn test_server_login_finish() -> Result<(), ProtocolError> {
for parameters in rfc_to_params!(TEST_VECTORS) {
let server_setup = ServerSetup::<Ristretto255Sha512NoSlowHash>::deserialize(
&[
&parameters.oprf_seed[..],
&parameters.server_private_key[..],
]
.concat(),
)?;
let password_file_bytes = get_password_file_bytes(&parameters)?;
let mut server_private_keyshare_and_nonce_rng =
CycleRng::new([parameters.server_private_keyshare, parameters.server_nonce].concat());
let mut server_private_keyshare_and_nonce_rng = CycleRng::new(
[
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,
ServerRegistration::deserialize(&password_file_bytes[..]).unwrap(),
&PrivateKey::from_bytes(&parameters.server_private_key[..]).unwrap(),
&server_setup,
Some(ServerRegistration::deserialize(&password_file_bytes[..]).unwrap()),
CredentialRequest::<Ristretto255Sha512NoSlowHash>::deserialize(&parameters.KE1[..])
.unwrap(),
&parameters.credential_identifier,
if parameters.envelope_mode == EnvelopeMode::CustomIdentifier {
ServerLoginStartParameters::WithInfoAndIdentifiers(
parameters.server_info.to_vec(),