diff --git a/Cargo.lock b/Cargo.lock index 10b95e9..529e9db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index 5a5b17a..7749533 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/examples/digital_locker.rs b/examples/digital_locker.rs index 8107238..dbaf094 100644 --- a/examples/digital_locker.rs +++ b/examples/digital_locker.rs @@ -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 { // Password-based registration and encryption of client secret message between a client and server fn register_locker( - server_kp: &opaque_ke::keypair::KeyPair, + server_setup: &ServerSetup, + 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::::start( - &mut server_rng, + &server_setup, RegistrationRequest::deserialize(®istration_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::::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, + server_setup: &ServerSetup, + locker_id: usize, password: String, locker: &Locker, ) -> Result { @@ -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::::new(&mut rng); let mut rl = Editor::<()>::new(); let mut registered_lockers: Vec = 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, ®istered_lockers[locker_index]) { + match open_locker( + &server_setup, + locker_index, + password, + ®istered_lockers[locker_index], + ) { Ok(contents) => { println!("\n\nSuccess! Contents: {}\n\n", contents); } diff --git a/examples/simple_login.rs b/examples/simple_login.rs index 44f9572..9f9105c 100644 --- a/examples/simple_login.rs +++ b/examples/simple_login.rs @@ -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, + server_setup: &ServerSetup, + username: String, password: String, ) -> Vec { 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::::start( - &mut server_rng, + &server_setup, RegistrationRequest::deserialize(®istration_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::::deserialize(&message_bytes[..]).unwrap(), + ); password_file.serialize() } // Password-based login between a client and server fn account_login( - server_kp: &opaque_ke::keypair::KeyPair, + server_setup: &ServerSetup, + 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::::new(&mut rng); let mut rl = Editor::<()>::new(); let mut registered_users = HashMap::>::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 diff --git a/src/ciphersuite.rs b/src/ciphersuite.rs index f1b9b24..9c90601 100644 --- a/src/ciphersuite.rs +++ b/src/ciphersuite.rs @@ -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; - - /// Generating a random key pair given a cryptographic rng - fn generate_random_keypair(rng: &mut R) -> KeyPair { - KeyPair::::generate_random(rng) - } } diff --git a/src/envelope.rs b/src/envelope.rs index bfed3d9..c565c7f 100644 --- a/src/envelope.rs +++ b/src/envelope.rs @@ -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 Envelope { ::OutputSize::to_usize() } + pub(crate) fn len() -> usize { + 1 + ::Len::to_usize() + ::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 { - 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 { - self.serialize() - } - pub(crate) fn serialize(&self) -> Vec { [&self.inner_envelope.serialize(), &self.hmac[..]].concat() } - pub(crate) fn deserialize(input: &[u8]) -> Result<(Self, Vec), ProtocolError> { - let (inner_envelope, remainder) = InnerEnvelope::deserialize(input)?; + pub(crate) fn deserialize(input: &[u8]) -> Result { + 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; ::Len::to_usize()], }, - hmac_and_remainder[hmac_key_size..].to_vec(), - )) + hmac: GenericArray::clone_from_slice(&vec![0u8; ::OutputSize::to_usize()]), + } } pub(crate) fn seal( @@ -194,16 +192,16 @@ impl Envelope { let mut nonce = vec![0u8; NONCE_LEN]; rng.fill_bytes(&mut nonce); - let h = Hkdf::::new(Some(&nonce), key); + let h = Hkdf::::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 = xor_key @@ -266,17 +264,26 @@ impl Envelope { key: &[u8], aad: &[u8], ) -> Result, InternalPakeError> { - let h = Hkdf::::new(Some(&self.inner_envelope.nonce), key); + let h = Hkdf::::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::::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::::seal_raw( + let (envelope, export_key) = Envelope::::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()); } } diff --git a/src/errors.rs b/src/errors.rs index e422250..8b6ce9b 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -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, diff --git a/src/lib.rs b/src/lib.rs index 0d162a1..e849831 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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::::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::::new(&mut server_rng); //! let server_registration_start_result = ServerRegistration::::start( -//! &mut server_rng, +//! &server_setup, //! client_registration_start_result.message, -//! server_kp.public(), +//! b"alice@example.com", //! )?; //! # 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::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; +//! # let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; //! 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::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; +//! # let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; //! # 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::::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::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; +//! # let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; //! # 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::::finish(client_registration_finish_result.message).serialize(); //! # let client_login_start_result = ClientLogin::::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"alice@example.com", //! 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::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; +//! # let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; //! # 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::::finish(client_registration_finish_result.message).serialize(); //! # let client_login_start_result = ClientLogin::::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"alice@example.com", 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::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; +//! # let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; //! # 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::::finish(client_registration_finish_result.message).serialize(); //! # let client_login_start_result = ClientLogin::::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"alice@example.com", 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::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; +//! let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; //! //! // 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::::finish(client_registration_finish_result.message).serialize(); //! # let client_login_start_result = ClientLogin::::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"alice@example.com", 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::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; +//! # let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; //! // 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::::finish(client_registration_finish_result.message).serialize(); //! # let client_login_start_result = ClientLogin::::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"alice@example.com", 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::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; +//! # let server_setup = ServerSetup::::new(&mut server_rng); +//! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; //! 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::::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::::new(&mut server_rng); +//! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; +//! # 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::::finish(client_registration_finish_result.message).serialize(); //! # let client_login_start_result = ClientLogin::::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"alice@example.com", //! 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::::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::::new(&mut server_rng); +//! # let server_registration_start_result = ServerRegistration::::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?; +//! # 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::::finish(client_registration_finish_result.message).serialize(); //! # let client_login_start_result = ClientLogin::::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"alice@example.com", 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, diff --git a/src/map_to_curve.rs b/src/map_to_curve.rs index 3875b88..36d37a1 100644 --- a/src/map_to_curve.rs +++ b/src/map_to_curve.rs @@ -24,6 +24,9 @@ pub trait GroupWithMapToCurve: Group { /// transforms a password and domain separation tag (DST) into a curve point fn map_to_curve(msg: &[u8], dst: &[u8]) -> Result; + /// Hashes a slice of pseudo-random bytes to a scalar + fn hash_to_scalar(input: &[u8]) -> Result; + /// Generates the contextString parameter as defined in /// fn get_context_string(mode: u8) -> Vec { @@ -43,6 +46,15 @@ impl GroupWithMapToCurve for RistrettoPoint { &GenericArray::clone_from_slice(&uniform_bytes[..]), )) } + + fn hash_to_scalar(input: &[u8]) -> Result { + const LEN_IN_BYTES: usize = 64; + let uniform_bytes = expand_message_xmd::(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) diff --git a/src/messages.rs b/src/messages.rs index 32c4956..84bd4e8 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -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 { /// The "envelope" generated by the user, containing sealed /// cryptographic identifiers pub(crate) envelope: Envelope, + /// The masking key used to mask the envelope + pub(crate) masking_key: GenericArray::OutputSize>, /// The user's public key pub(crate) client_s_pk: PublicKey, } @@ -136,6 +140,7 @@ impl Clone for RegistrationUpload { 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 RegistrationUpload { pub fn serialize(&self) -> Vec { [ self.client_s_pk.to_arr().to_vec(), + self.masking_key.to_vec(), self.envelope.serialize(), ] .concat() @@ -154,22 +160,37 @@ impl RegistrationUpload { /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let key_len = ::Len::to_usize(); - - let checked_slice = check_slice_size_atleast(input, key_len, "registration_upload_bytes")?; - - let (envelope, remainder) = Envelope::::deserialize(&checked_slice[key_len..])?; - - if !remainder.is_empty() { - return Err(PakeError::SerializationError.into()); - } - + let hash_len = ::OutputSize::to_usize(); + let checked_slice = check_slice_size( + &input, + key_len + hash_len + Envelope::::len(), + "registration_upload_bytes", + )?; + let envelope = Envelope::::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::::check_public_key(PublicKey::from_bytes( &checked_slice[..key_len], )?)?, }) } + + // Creates a dummy instance used for faking a [CredentialResponse] + pub(crate) fn dummy(rng: &mut R) -> Self { + let mut masking_key = vec![0u8; ::OutputSize::to_usize()]; + rng.fill_bytes(&mut masking_key); + + let keypair = KeyPair::::generate_random(rng); + + Self { + envelope: Envelope::::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 { /// 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, + pub(crate) masking_nonce: Vec, + pub(crate) masked_response: Vec, pub(crate) ke2_message: >::KE2Message, } @@ -240,8 +260,8 @@ impl Clone for CredentialResponse { 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 CredentialResponse { /// Serialization into bytes pub fn serialize(&self) -> Vec { [ - 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 CredentialResponse { pub(crate) fn serialize_without_ke( beta: &CS::Group, - server_s_pk: &PublicKey, - envelope: &Envelope, + masking_nonce: &[u8], + masked_response: &[u8], ) -> Vec { - [ - &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 { let elem_len = ::ElemLen::to_usize(); let key_len = ::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::::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 CredentialResponse { 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::::check_public_key(unchecked_server_s_pk)?; - - let (envelope, remainder) = - Envelope::::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 = >::KE2Message::from_bytes::( - checked_remainder, + &checked_slice[elem_len + nonce_len + masked_response_len..], )?; Ok(Self { beta, - server_s_pk, - envelope, + masking_nonce, + masked_response, ke2_message, }) } diff --git a/src/opaque.rs b/src/opaque.rs index 32930af..5f037aa 100644 --- a/src/opaque.rs +++ b/src/opaque.rs @@ -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 { + oprf_seed: GenericArray::OutputSize>, + keypair: KeyPair, +} + +impl ServerSetup { + /// Generate a new instance of server setup + pub fn new(rng: &mut R) -> Self { + let mut seed = vec![0u8; ::OutputSize::to_usize()]; + rng.fill_bytes(&mut seed); + + Self { + oprf_seed: GenericArray::clone_from_slice(&seed[..]), + keypair: KeyPair::::generate_random(rng), + } + } + + /// Serialization into bytes + pub fn serialize(&self) -> Vec { + [ + self.oprf_seed.to_vec(), + self.keypair.private().to_arr().to_vec(), + ] + .concat() + } + + /// Deserialization from bytes + pub fn deserialize(input: &[u8]) -> Result { + let seed_len = ::OutputSize::to_usize(); + let checked_slice = check_slice_size( + input, + seed_len + ::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 { + &self.keypair + } +} + // Registration // ============ @@ -48,12 +104,11 @@ impl Clone for ClientRegistration { impl ClientRegistration { /// Serialization into bytes pub fn serialize(&self) -> Vec { - let output: Vec = [ + [ &CS::Group::scalar_as_bytes(&self.token.blind)[..], &self.token.data, ] - .concat(); - output + .concat() } /// Deserialization from bytes @@ -129,28 +184,6 @@ impl Clone for ClientRegistrationStartResult { impl ClientRegistration { /// 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::::start(&mut client_rng, b"hunter2")?; - /// # Ok::<(), ProtocolError>(()) - /// ``` pub fn start( blinding_factor_rng: &mut R, password: &[u8], @@ -190,35 +223,6 @@ impl Clone for ClientRegistrationFinishResult { impl ClientRegistration { /// "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::::start(&mut client_rng, b"hunter2")?; - /// let server_registration_start_result = - /// ServerRegistration::::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( self, rng: &mut R, @@ -229,11 +233,16 @@ impl ClientRegistration { 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::::generate_random(rng); let password_derived_key = get_password_derived_key::(&self.token, r2.beta)?; + let h = Hkdf::::new(None, &password_derived_key); + let mut masking_key = vec![0u8; ::OutputSize::to_usize()]; + h.expand(STR_MASKING_KEY, &mut masking_key) + .map_err(|_| InternalPakeError::HkdfError)?; + let (envelope, export_key) = Envelope::::seal( rng, &password_derived_key, @@ -245,6 +254,7 @@ impl ClientRegistration { 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 ClientRegistration { } } -/// 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 { /// The registration resposne message to send to the client pub message: RegistrationResponse, - /// The state that the server must keep in order to complete registration - pub state: ServerRegistration, } // Cannot be derived because it would require for CS to be Clone. @@ -267,123 +276,51 @@ impl Clone for ServerRegistrationStartResult { 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 { - envelope: Option>, - client_s_pk: Option, - pub(crate) oprf_key: ::Scalar, -} +pub struct ServerRegistration(RegistrationUpload); // Cannot be derived because it would require for CS to be Clone. impl Clone for ServerRegistration { 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 ServerRegistration { /// Serialization into bytes pub fn serialize(&self) -> Vec { - let mut output: Vec = 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 { - let scalar_len = ::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 = ::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::::check_public_key(unchecked_client_s_pk)?; - - let envelope = Envelope::::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::::start(&mut client_rng, b"hunter2")?; - /// let server_registration_start_result = ServerRegistration::::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?; - /// # Ok::<(), ProtocolError>(()) - /// ``` - pub fn start( - rng: &mut R, + pub fn start( + server_setup: &ServerSetup, message: RegistrationRequest, - server_s_pk: &PublicKey, + credential_identifier: &[u8], ) -> Result, 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::( + &server_setup.oprf_seed, + credential_identifier, + )?; // Compute beta = alpha^oprf_key let beta = oprf::evaluate::(message.alpha, &oprf_key); @@ -391,52 +328,20 @@ impl ServerRegistration { 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::::start(&mut client_rng, b"hunter2")?; - /// let server_registration_start_result = ServerRegistration::::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) -> Result { - 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) -> Self { + Self(message) + } + + // Creates a dummy instance used for faking a [CredentialResponse] + pub(crate) fn dummy(rng: &mut R) -> Self { + Self(RegistrationUpload::dummy(rng)) } } @@ -653,41 +558,9 @@ impl ClientLogin { /// "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::::start(&mut client_rng, b"hunter2")?; - /// # let server_kp = Default::generate_random_keypair(&mut server_rng); - /// # let server_registration_start_result = ServerRegistration::::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::::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, + credential_response: CredentialResponse, params: ClientLoginFinishParameters, ) -> Result, ProtocolError> { let optional_ids = match params { @@ -695,12 +568,28 @@ impl ClientLogin { 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::( + &self.token, + credential_response.beta, + )?; - let password_derived_key = - get_password_derived_key::(&self.token, l2.beta)?; - let opened_envelope = &l2 - .envelope + let h = Hkdf::::new(None, &password_derived_key); + let mut masking_key = vec![0u8; ::OutputSize::to_usize()]; + h.expand(STR_MASKING_KEY, &mut masking_key) + .map_err(|_| InternalPakeError::HkdfError)?; + + let (server_s_pk, envelope) = unmask_response::( + &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 ClientLogin { Some((id_u, id_s)) => (id_u, id_s), }; - let credential_response_component = - CredentialResponse::::serialize_without_ke(&l2.beta, &l2.server_s_pk, &l2.envelope); + let credential_response_component = CredentialResponse::::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 ClientLogin { 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 ServerLogin { }) } - /// 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::::start(&mut client_rng, b"hunter2")?; - /// # let server_registration_start_result = ServerRegistration::::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::::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( rng: &mut R, - password_file: ServerRegistration, - server_s_sk: &PrivateKey, + server_setup: &ServerSetup, + password_file: Option>, l1: CredentialRequest, + credential_identifier: &[u8], params: ServerLoginStartParameters, ) -> Result, 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 ServerLogin { } }; - 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::::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::::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::::public_from_private(server_s_sk); + + let oprf_key = oprf_key_from_seed::( + &server_setup.oprf_seed, + credential_identifier, + )?; + let beta = oprf::evaluate(l1.alpha, &oprf_key); let credential_response_component = - CredentialResponse::::serialize_without_ke(&beta, &server_s_pk, &envelope); + CredentialResponse::::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 ServerLogin { let credential_response = CredentialResponse { beta, - server_s_pk, - envelope, + masking_nonce, + masked_response, ke2_message, }; @@ -950,39 +825,6 @@ impl ServerLogin { /// 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::::start(&mut client_rng, b"hunter2")?; - /// # let server_registration_start_result = ServerRegistration::::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::::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, @@ -1033,9 +875,9 @@ impl Drop for ClientRegistration { // This can't be derived because of the use of a phantom parameter impl Zeroize for ServerRegistration { 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, D: Hash>( let oprf_output = oprf::finalize::(&token.data, &token.blind, beta); SH::hash(oprf_output) } + +fn oprf_key_from_seed( + oprf_seed: &GenericArray, + credential_identifier: &[u8], +) -> Result { + let mut oprf_key_bytes = vec![0u8; ::Len::to_usize()]; + Hkdf::::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::(&oprf_key_bytes[..]) +} + +fn mask_response( + masking_key: &[u8], + masking_nonce: &[u8], + server_s_pk: &PublicKey, + envelope: &Envelope, +) -> Result, ProtocolError> { + let mut xor_pad = vec![0u8; ::Len::to_usize() + Envelope::::len()]; + Hkdf::::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( + masking_key: &[u8], + masking_nonce: &[u8], + masked_response: &[u8], +) -> Result<(PublicKey, Envelope), ProtocolError> { + let mut xor_pad = vec![0u8; ::Len::to_usize() + Envelope::::len()]; + Hkdf::::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 = xor_pad + .iter() + .zip(masked_response.iter()) + .map(|(&x1, &x2)| x1 ^ x2) + .collect(); + let key_len = ::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)) +} diff --git a/src/serialization/tests.rs b/src/serialization/tests.rs index 78052f4..91afbb1 100644 --- a/src/serialization/tests.rs +++ b/src/serialization/tests.rs @@ -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 = ::random_nonzero_scalar(&mut rng); - let mut oprf_bytes: Vec = vec![]; - oprf_bytes.extend_from_slice(oprf_key.as_bytes()); - let reg = ServerRegistration::::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::<::Group>::generate_random(&mut rng); // serialization order: oprf_key, public key, envelope let mut bytes = Vec::::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::::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::<::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::<::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; ::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::::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::<::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; + ::Len::to_usize() + + Envelope::<::Hash>::len() + ]; + rng.fill_bytes(&mut masked_response); - let (envelope, _) = Envelope::::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::<::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::::deserialize(&input).unwrap(); @@ -279,8 +271,8 @@ fn credential_response_roundtrip() { assert!(match CredentialRequest::::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 = ::random_nonzero_scalar(&mut rng); - let client_e_kp = Default::generate_random_keypair(&mut rng); + let client_e_kp = KeyPair::<::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::<::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::<::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()]; diff --git a/src/tests/full_test.rs b/src/tests/full_test.rs index 3c9142e..9e6c030 100644 --- a/src/tests/full_test.rs +++ b/src/tests/full_test.rs @@ -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, pub server_e_pk: Vec, pub server_e_sk: Vec, + pub credential_identifier: Vec, pub id_u: Vec, pub id_s: Vec, pub password: Vec, pub blinding_factor: Vec, - pub oprf_key: Vec, + pub oprf_seed: Vec, + pub masking_nonce: Vec, pub envelope_nonce: Vec, pub client_nonce: Vec, pub server_nonce: Vec, @@ -61,7 +63,6 @@ pub struct TestVectorParameters { pub credential_response: Vec, pub credential_finalization: Vec, client_registration_state: Vec, - server_registration_state: Vec, client_login_state: Vec, server_login_state: Vec, pub password_file: Vec, @@ -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() -> 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::::generate_random(&mut rng); + let server_e_kp = KeyPair::::generate_random(&mut rng); + let client_s_kp = KeyPair::::generate_random(&mut rng); + let client_e_kp = KeyPair::::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() -> TestVectorParameters { let mut server_nonce = vec![0u8; NonceLen::to_usize()]; rng.fill_bytes(&mut server_nonce); + let server_setup = + ServerSetup::::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() -> 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::::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 = Vec::new(); client_s_sk_and_nonce.extend_from_slice(&client_s_kp.private().to_arr()); @@ -340,10 +354,7 @@ fn generate_parameters() -> 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 = Vec::new(); @@ -363,6 +374,7 @@ fn generate_parameters() -> 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() -> TestVectorParameters { ); let server_login_start_result = ServerLogin::::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() -> 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() -> 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 = serde_json::from_str(®istration_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::::deserialize( + &[¶meters.oprf_seed[..], ¶meters.server_s_sk[..]].concat(), + )?; + let server_registration_start_result = ServerRegistration::::start( - &mut oprf_key_rng, + &server_setup, RegistrationRequest::deserialize(¶meters.registration_request[..])?, - &PublicKey::from_bytes(¶meters.server_s_pk[..])?, + ¶meters.credential_identifier, )?; assert_eq!( hex::encode(parameters.registration_response), hex::encode(server_registration_start_result.message.serialize()) ); - assert_eq!( - hex::encode(¶meters.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::::deserialize( - ¶meters.server_registration_state[..], - )?; - let password_file = server_registration.finish(RegistrationUpload::deserialize( - ¶meters.registration_upload[..], - )?)?; + let password_file = ServerRegistration::finish(RegistrationUpload::< + RistrettoSha5123dhNoSlowHash, + >::deserialize( + ¶meters.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::::deserialize( + &[¶meters.oprf_seed[..], ¶meters.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::::start( &mut server_e_sk_and_nonce_rng, - ServerRegistration::deserialize(¶meters.password_file[..])?, - &PrivateKey::from_bytes(¶meters.server_s_sk[..])?, + &server_setup, + Some(ServerRegistration::deserialize( + ¶meters.password_file[..], + )?), CredentialRequest::::deserialize( ¶meters.credential_request[..], )?, + ¶meters.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::::new(&mut server_rng); let client_registration_start_result = ClientRegistration::::start( &mut client_rng, @@ -685,18 +712,16 @@ fn test_complete_flow( )?; let server_registration_start_result = ServerRegistration::::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::::start( &mut client_rng, login_password, @@ -704,9 +729,10 @@ fn test_complete_flow( )?; let server_login_start_result = ServerLogin::::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::::start( - &mut client_rng, - STR_PASSWORD.as_bytes(), - )?; - let server_registration_start_result = - ServerRegistration::::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::::new(&mut server_rng); let client_registration_start_result = ClientRegistration::::start( &mut client_rng, @@ -813,9 +810,9 @@ fn test_zeroize_client_registration_finish() -> Result<(), ProtocolError> { )?; let server_registration_start_result = ServerRegistration::::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::::new(&mut server_rng); let client_registration_start_result = ClientRegistration::::start( &mut client_rng, @@ -847,18 +844,16 @@ fn test_zeroize_server_registration_finish() -> Result<(), ProtocolError> { )?; let server_registration_start_result = ServerRegistration::::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::::new(&mut server_rng); let client_registration_start_result = ClientRegistration::::start( &mut client_rng, @@ -905,18 +900,16 @@ fn test_zeroize_server_login_start() -> Result<(), ProtocolError> { )?; let server_registration_start_result = ServerRegistration::::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::::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::::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::::new(&mut server_rng); let client_registration_start_result = ClientRegistration::::start( &mut client_rng, @@ -954,18 +948,16 @@ fn test_zeroize_client_login_finish() -> Result<(), ProtocolError> { )?; let server_registration_start_result = ServerRegistration::::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::::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::::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::::new(&mut server_rng); let client_registration_start_result = ClientRegistration::::start( &mut client_rng, @@ -1007,18 +1000,16 @@ fn test_zeroize_server_login_finish() -> Result<(), ProtocolError> { )?; let server_registration_start_result = ServerRegistration::::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::::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::::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( diff --git a/src/tests/opaque_test_vectors.rs b/src/tests/opaque_test_vectors.rs index 68db601..e6ccc58 100644 --- a/src/tests/opaque_test_vectors.rs +++ b/src/tests/opaque_test_vectors.rs @@ -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, pub client_identity: Vec, pub server_identity: Vec, + pub credential_identifier: Vec, pub password: Vec, pub blind_registration: Vec, - pub oprf_key: Vec, + pub oprf_seed: Vec, + pub masking_nonce: Vec, pub envelope_nonce: Vec, pub client_nonce: Vec, pub server_nonce: Vec, @@ -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, ProtocolError> { - let mut oprf_key_rng = CycleRng::new(parameters.oprf_key.clone()); - let server_registration_start_result = - ServerRegistration::::start( - &mut oprf_key_rng, - RegistrationRequest::deserialize(¶meters.registration_request[..]).unwrap(), - &PublicKey::from_bytes(¶meters.server_public_key[..]).unwrap(), - )?; - - let password_file = server_registration_start_result - .state - .finish(RegistrationUpload::deserialize(¶meters.registration_upload[..]).unwrap())?; + let password_file = ServerRegistration::::finish( + RegistrationUpload::deserialize(¶meters.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::::deserialize( + &[ + ¶meters.oprf_seed[..], + ¶meters.server_private_key[..], + ] + .concat(), + )?; let server_registration_start_result = ServerRegistration::::start( - &mut oprf_key_rng, + &server_setup, RegistrationRequest::deserialize(¶meters.registration_request[..]).unwrap(), - &PublicKey::from_bytes(¶meters.server_public_key[..]).unwrap(), + ¶meters.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::::deserialize( + &[ + ¶meters.oprf_seed[..], + ¶meters.server_private_key[..], + ] + .concat(), + )?; let password_file_bytes = get_password_file_bytes(¶meters)?; - 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::::start( &mut server_private_keyshare_and_nonce_rng, - ServerRegistration::deserialize(&password_file_bytes[..]).unwrap(), - &PrivateKey::from_bytes(¶meters.server_private_key[..]).unwrap(), + &server_setup, + Some(ServerRegistration::deserialize(&password_file_bytes[..]).unwrap()), CredentialRequest::::deserialize(¶meters.KE1[..]) .unwrap(), + ¶meters.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::::deserialize( + &[ + ¶meters.oprf_seed[..], + ¶meters.server_private_key[..], + ] + .concat(), + )?; let password_file_bytes = get_password_file_bytes(¶meters)?; - 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::::start( &mut server_private_keyshare_and_nonce_rng, - ServerRegistration::deserialize(&password_file_bytes[..]).unwrap(), - &PrivateKey::from_bytes(¶meters.server_private_key[..]).unwrap(), + &server_setup, + Some(ServerRegistration::deserialize(&password_file_bytes[..]).unwrap()), CredentialRequest::::deserialize(¶meters.KE1[..]) .unwrap(), + ¶meters.credential_identifier, if parameters.envelope_mode == EnvelopeMode::CustomIdentifier { ServerLoginStartParameters::WithInfoAndIdentifiers( parameters.server_info.to_vec(),