// Copyright (c) Facebook, Inc. and its affiliates. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree. //! Provides the main OPAQUE API use crate::{ ciphersuite::CipherSuite, envelope::Envelope, errors::{utils::check_slice_size, InternalPakeError, PakeError, ProtocolError}, group::Group, hash::Hash, key_exchange::traits::{FromBytes, KeyExchange, ToBytesWithPointers}, keypair::{KeyPair, PrivateKey, PublicKey, SecretKey}, oprf, serialization::{serialize, tokenize}, slow_hash::SlowHash, CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, }; use alloc::vec; use alloc::vec::Vec; use core::marker::PhantomData; use digest::Digest; use generic_array::{typenum::Unsigned, GenericArray}; use hkdf::Hkdf; use rand::{CryptoRng, RngCore}; 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"; const STR_OPAQUE_DERIVE_KEY_PAIR: &[u8] = b"OPAQUE-DeriveKeyPair"; // Server Setup // ============ /// The state elements the server holds upon setup #[cfg_attr( feature = "serialize", derive(serde::Deserialize, serde::Serialize), serde(bound( deserialize = "KeyPair: serde::Deserialize<'de>", serialize = "KeyPair: serde::Serialize" )) )] pub struct ServerSetup< CS: CipherSuite, S: SecretKey = PrivateKey<::KeGroup>, > { oprf_seed: GenericArray::OutputSize>, keypair: KeyPair, pub(crate) fake_keypair: KeyPair, } impl ServerSetup> { /// Generate a new instance of server setup pub fn new(rng: &mut R) -> Self { let keypair = KeyPair::::generate_random(rng); Self::new_with_key(rng, keypair) } } impl> ServerSetup { /// Create [`ServerSetup`] with the given keypair pub fn new_with_key( rng: &mut R, keypair: KeyPair, ) -> Self { let mut seed = vec![0u8; ::OutputSize::USIZE]; rng.fill_bytes(&mut seed); Self { oprf_seed: GenericArray::clone_from_slice(&seed[..]), keypair, fake_keypair: KeyPair::::generate_random(rng), } } /// Serialization into bytes pub fn serialize(&self) -> Vec { [ self.oprf_seed.to_vec(), self.keypair.private().serialize(), self.fake_keypair.private().serialize(), ] .concat() } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result> { let seed_len = ::OutputSize::USIZE; let key_len = ::ScalarLen::USIZE; let checked_slice = check_slice_size(input, seed_len + key_len + key_len, "server_setup")?; Ok(Self { oprf_seed: GenericArray::clone_from_slice(&checked_slice[..seed_len]), keypair: KeyPair::from_private_key_slice(&checked_slice[seed_len..seed_len + key_len])?, fake_keypair: KeyPair::from_private_key_slice(&checked_slice[seed_len + key_len..]) .map_err(ProtocolError::into_custom)?, }) } /// Returns the keypair pub fn keypair(&self) -> &KeyPair { &self.keypair } } // Cannot be derived because it would require for CS to be bound. impl_clone_for!( struct ServerSetup, [oprf_seed, keypair, fake_keypair], ); impl_debug_eq_hash_for!( struct ServerSetup, [oprf_seed, oprf_seed, fake_keypair], ); // Registration // ============ /// The state elements the client holds to register itself pub struct ClientRegistration { alpha: CS::OprfGroup, /// token containing the client's password and the blinding factor pub(crate) token: oprf::Token, } impl_clone_for!(struct ClientRegistration, [token, alpha]); impl_debug_eq_hash_for!( struct ClientRegistration, [token], [oprf::Token], ); impl ClientRegistration { /// Serialization into bytes pub fn serialize(&self) -> Vec { [ &self.alpha.to_arr().to_vec(), &CS::OprfGroup::scalar_as_bytes(self.token.blind)[..], &self.token.data, ] .concat() } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let elem_len = ::ElemLen::USIZE; let scalar_len = ::ScalarLen::USIZE; let min_expected_len = elem_len + scalar_len; let checked_slice = (if input.len() <= min_expected_len { Err(InternalPakeError::SizeError { name: "client_registration_bytes", len: min_expected_len, actual_len: input.len(), }) } else { Ok(input) })?; let alpha = CS::OprfGroup::from_element_slice(GenericArray::from_slice( &checked_slice[..elem_len], ))?; // Check that the message is actually containing an element of the // correct subgroup let blinding_factor_bytes = GenericArray::from_slice(&checked_slice[elem_len..elem_len + scalar_len]); let blinding_factor = CS::OprfGroup::from_scalar_slice(blinding_factor_bytes)?; let password = checked_slice[elem_len + scalar_len..].to_vec(); Ok(Self { alpha, token: oprf::Token { data: password, blind: blinding_factor, }, }) } #[cfg(test)] pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> { vec![ (self.token.data.as_ptr(), self.token.data.len()), /* cannot provide raw pointer to self.token.blind until this is exposed in curve25519_dalek::scalar::Scalar */ ] } } impl_serialize_and_deserialize_for!(ClientRegistration); /// Options for specifying custom identifiers #[derive(Clone)] pub enum Identifiers { /// Supply only a client identifier ClientIdentifier(Vec), /// Supply only a server identifier ServerIdentifier(Vec), /// Supply a client and server identifier ClientAndServerIdentifiers(Vec, Vec), } pub(crate) fn bytestrings_from_identifiers( ids: &Option, client_s_pk: &[u8], server_s_pk: &[u8], ) -> Result<(Vec, Vec), ProtocolError> { let (client_identity, server_identity): (Vec, Vec) = match ids { None => (client_s_pk.to_vec(), server_s_pk.to_vec()), Some(Identifiers::ClientIdentifier(id_u)) => (id_u.clone(), server_s_pk.to_vec()), Some(Identifiers::ServerIdentifier(id_s)) => (client_s_pk.to_vec(), id_s.clone()), Some(Identifiers::ClientAndServerIdentifiers(id_u, id_s)) => (id_u.clone(), id_s.clone()), }; Ok(( serialize(&client_identity, 2)?, serialize(&server_identity, 2)?, )) } /// Optional parameters for client registration finish #[derive(Clone)] pub enum ClientRegistrationFinishParameters { /// Specifying the identifiers idU and idS WithIdentifiers(Identifiers), /// No identifiers or private key specified Default, } impl Default for ClientRegistrationFinishParameters { fn default() -> Self { Self::Default } } /// Contains the fields that are returned by a client registration start pub struct ClientRegistrationStartResult { /// The registration request message to be sent to the server pub message: RegistrationRequest, /// The client state that must be persisted in order to complete registration pub state: ClientRegistration, } // Cannot be derived because it would require for CS to be Clone. impl Clone for ClientRegistrationStartResult { fn clone(&self) -> Self { Self { message: self.message.clone(), state: self.state.clone(), } } } impl ClientRegistration { /// Returns an initial "blinded" request to send to the server, as well as a ClientRegistration pub fn start( blinding_factor_rng: &mut R, password: &[u8], ) -> Result, ProtocolError> { let (token, alpha) = oprf::blind::(password, blinding_factor_rng)?; Ok(ClientRegistrationStartResult { message: RegistrationRequest:: { alpha }, state: Self { alpha, token }, }) } } /// Contains the fields that are returned by a client registration finish pub struct ClientRegistrationFinishResult { /// The registration upload message to be sent to the server pub message: RegistrationUpload, /// The export key output by client registration pub export_key: GenericArray::OutputSize>, /// The server's static public key pub server_s_pk: PublicKey, /// Instance of the ClientRegistration, only used in tests for checking zeroize #[cfg(test)] pub state: ClientRegistration, /// AuthKey, only used in tests #[cfg(test)] pub auth_key: Vec, /// Password derived key, only used in tests #[cfg(test)] pub randomized_pwd: GenericArray::OutputSize>, } // Cannot be derived because it would require for CS to be Clone. impl Clone for ClientRegistrationFinishResult { fn clone(&self) -> Self { Self { message: self.message.clone(), export_key: self.export_key.clone(), server_s_pk: self.server_s_pk.clone(), #[cfg(test)] state: self.state.clone(), #[cfg(test)] auth_key: self.auth_key.clone(), #[cfg(test)] randomized_pwd: self.randomized_pwd.clone(), } } } impl ClientRegistration { /// "Unblinds" the server's answer and returns a final message containing /// cryptographic identifiers, to be sent to the server on setup finalization pub fn finish( self, rng: &mut R, r2: RegistrationResponse, params: ClientRegistrationFinishParameters, ) -> Result, ProtocolError> { let optional_ids = match params { ClientRegistrationFinishParameters::WithIdentifiers(ids) => Some(ids), ClientRegistrationFinishParameters::Default => None, }; // Check for reflected value from server and halt if detected if self.alpha.ct_equal(&r2.beta) { return Err(ProtocolError::ReflectedValueError); } let password_derived_key = get_password_derived_key::( &self.token, r2.beta, )?; #[cfg_attr(not(test), allow(unused_variables))] let (randomized_pwd, h) = Hkdf::::extract(None, &password_derived_key); let mut masking_key = vec![0u8; ::OutputSize::USIZE]; h.expand(STR_MASKING_KEY, &mut masking_key) .map_err(|_| InternalPakeError::HkdfError)?; let result = Envelope::::seal(rng, &password_derived_key, &r2.server_s_pk, optional_ids)?; Ok(ClientRegistrationFinishResult { message: RegistrationUpload { envelope: result.0, masking_key: GenericArray::clone_from_slice(&masking_key[..]), client_s_pk: result.1, }, export_key: result.2, server_s_pk: r2.server_s_pk, #[cfg(test)] state: self, #[cfg(test)] auth_key: result.3, #[cfg(test)] randomized_pwd, }) } } /// 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, /// OPRF key, only used in tests #[cfg(test)] pub oprf_key: GenericArray::ScalarLen>, } // Cannot be derived because it would require for CS to be Clone. impl Clone for ServerRegistrationStartResult { fn clone(&self) -> Self { Self { message: self.message.clone(), #[cfg(test)] oprf_key: self.oprf_key.clone(), } } } /// The state elements the server holds to record a registration pub struct ServerRegistration(RegistrationUpload); impl_clone_for!(tuple ServerRegistration, [0]); impl_debug_eq_hash_for!( tuple ServerRegistration, [0], ); impl ServerRegistration { /// Serialization into bytes pub fn serialize(&self) -> Vec { self.0.serialize() } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { Ok(Self(RegistrationUpload::deserialize(input)?)) } #[cfg(test)] pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> { [ 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 pub fn start>( server_setup: &ServerSetup, message: RegistrationRequest, credential_identifier: &[u8], ) -> Result, ProtocolError> { 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); Ok(ServerRegistrationStartResult { message: RegistrationResponse { beta, server_s_pk: server_setup.keypair.public().clone(), }, #[cfg(test)] oprf_key: CS::OprfGroup::scalar_as_bytes(oprf_key), }) } /// From the client's cryptographic identifiers, fully populates and /// returns a ServerRegistration pub fn finish(message: RegistrationUpload) -> Self { Self(message) } // Creates a dummy instance used for faking a [CredentialResponse] pub(crate) fn dummy>( rng: &mut R, server_setup: &ServerSetup, ) -> Self { Self(RegistrationUpload::dummy(rng, server_setup)) } } impl_serialize_and_deserialize_for!(ServerRegistration); // Login // ===== /// The state elements the client holds to perform a login #[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr( feature = "serialize", serde(bound( deserialize = "oprf::Token: serde::Deserialize<'de>, >::KE1State: serde::Deserialize<'de>", serialize = "oprf::Token: serde::Serialize, >::KE1State: serde::Serialize" )) )] pub struct ClientLogin { /// token containing the client's password and the blinding factor token: oprf::Token, ke1_state: >::KE1State, serialized_credential_request: Vec, } impl_clone_for!(struct ClientLogin, [token, ke1_state, serialized_credential_request]); impl_debug_eq_hash_for!( struct ClientLogin, [token, ke1_state, serialized_credential_request], [oprf::Token, >::KE1State], ); impl ClientLogin { /// Serialization into bytes pub fn serialize(&self) -> Result, ProtocolError> { let output: Vec = [ &CS::OprfGroup::scalar_as_bytes(self.token.blind)[..], &serialize(&self.serialized_credential_request, 2)?, &serialize(&self.ke1_state.to_bytes(), 2)?, &self.token.data, ] .concat(); Ok(output) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let scalar_len = ::ScalarLen::USIZE; let checked_slice = (if input.len() <= scalar_len { Err(InternalPakeError::SizeError { name: "client_login_bytes", len: scalar_len, actual_len: input.len(), }) } else { Ok(input) })?; let blinding_factor_bytes = GenericArray::from_slice(&checked_slice[..scalar_len]); let blinding_factor = CS::OprfGroup::from_scalar_slice(blinding_factor_bytes)?; let (serialized_credential_request, remainder) = tokenize(&checked_slice[scalar_len..], 2)?; let (ke1_state_bytes, password) = tokenize(&remainder, 2)?; let ke1_state = >::KE1State::from_bytes::( &ke1_state_bytes[..], )?; Ok(Self { token: oprf::Token { data: password, blind: blinding_factor, }, ke1_state, serialized_credential_request, }) } #[cfg(test)] pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> { [ vec![ (self.token.data.as_ptr(), self.token.data.len()), /* cannot provide raw pointer to self.token.blind until this is exposed in curve25519_dalek::scalar::Scalar */ ], self.ke1_state.as_byte_ptrs(), vec![ (self.serialized_credential_request.as_ptr(), self.serialized_credential_request.len()) ], ].concat() } } /// Contains the fields that are returned by a client login start pub struct ClientLoginStartResult { /// The message to send to the server to begin the login protocol pub message: CredentialRequest, /// The state that the client must keep in order to complete the protocol pub state: ClientLogin, } // Cannot be derived because it would require for CS to be Clone. impl Clone for ClientLoginStartResult { fn clone(&self) -> Self { Self { message: self.message.clone(), state: self.state.clone(), } } } /// Optional parameters for client login finish #[derive(Clone)] pub enum ClientLoginFinishParameters { /// Specifying a context field that the server must agree on WithContext(Vec), /// Specifying a user identifier and server identifier that will be matched against the server WithIdentifiers(Identifiers), /// Specifying a context field that the server must agree on, /// along with a user identifier and server identifier and context that will be matched against the server WithContextAndIdentifiers(Vec, Identifiers), /// No custom identifiers and no context Default, } impl Default for ClientLoginFinishParameters { fn default() -> Self { Self::Default } } /// Contains the fields that are returned by a client login finish pub struct ClientLoginFinishResult { /// The message to send to the server to complete the protocol pub message: CredentialFinalization, /// The session key pub session_key: Vec, /// The client-side export key pub export_key: GenericArray::OutputSize>, /// The server's static public key pub server_s_pk: PublicKey, /// Instance of the ClientLogin, only used in tests for checking zeroize #[cfg(test)] pub state: ClientLogin, /// Handshake secret, only used in tests #[cfg(test)] pub handshake_secret: Vec, /// Client MAC key, only used in tests #[cfg(test)] pub client_mac_key: GenericArray::OutputSize>, } // Cannot be derived because it would require for CS to be Clone. impl Clone for ClientLoginFinishResult { fn clone(&self) -> Self { Self { message: self.message.clone(), session_key: self.session_key.clone(), export_key: self.export_key.clone(), server_s_pk: self.server_s_pk.clone(), #[cfg(test)] state: self.state.clone(), #[cfg(test)] handshake_secret: self.handshake_secret.clone(), #[cfg(test)] client_mac_key: self.client_mac_key.clone(), } } } impl ClientLogin { /// Returns an initial "blinded" password request to send to the server, as well as a ClientLogin pub fn start( rng: &mut R, password: &[u8], ) -> Result, ProtocolError> { let (token, alpha) = oprf::blind::(password, rng)?; let (ke1_state, ke1_message) = CS::KeyExchange::generate_ke1(rng)?; let credential_request = CredentialRequest { alpha, ke1_message }; let serialized_credential_request = credential_request.serialize(); Ok(ClientLoginStartResult { message: credential_request, state: Self { token, ke1_state, serialized_credential_request, }, }) } /// "Unblinds" the server's answer and returns the opened assets from /// the server pub fn finish( self, credential_response: CredentialResponse, params: ClientLoginFinishParameters, ) -> Result, ProtocolError> { let (context, optional_ids) = match params { ClientLoginFinishParameters::Default => (vec![], None), ClientLoginFinishParameters::WithContext(context) => (context, None), ClientLoginFinishParameters::WithIdentifiers(ids) => (vec![], Some(ids)), // add context ClientLoginFinishParameters::WithContextAndIdentifiers(context, ids) => { (context, Some(ids)) } }; // Check if beta value from server is equal to alpha value from client let credential_request = CredentialRequest::::deserialize(&self.serialized_credential_request[..])?; if credential_request.alpha.ct_equal(&credential_response.beta) { return Err(ProtocolError::ReflectedValueError); } let password_derived_key = get_password_derived_key::( &self.token, credential_response.beta, )?; let h = Hkdf::::new(None, &password_derived_key); let mut masking_key = vec![0u8; ::OutputSize::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(), ProtocolError::VerificationError(PakeError::SerializationError) => { 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 { ProtocolError::VerificationError(PakeError::CryptoError( InternalPakeError::SealOpenHmacError, )) => ProtocolError::VerificationError(PakeError::InvalidLoginError), err => err, })?; let credential_response_component = CredentialResponse::::serialize_without_ke( &credential_response.beta, &credential_response.masking_nonce, &credential_response.masked_response, ); let result = CS::KeyExchange::generate_ke3( credential_response_component, credential_response.ke2_message, &self.ke1_state, &self.serialized_credential_request, server_s_pk.clone(), opened_envelope.client_static_keypair.private().clone(), opened_envelope.id_u.clone(), opened_envelope.id_s.clone(), context, )?; Ok(ClientLoginFinishResult { message: CredentialFinalization { ke3_message: result.1, }, session_key: result.0, export_key: opened_envelope.export_key.clone(), server_s_pk, #[cfg(test)] state: self, #[cfg(test)] handshake_secret: result.2, #[cfg(test)] client_mac_key: result.3, }) } } /// The state elements the server holds to record a login pub struct ServerLogin { ke2_state: >::KE2State, _cs: PhantomData, } impl_clone_for!(struct ServerLogin, [ke2_state, _cs]); impl_debug_eq_hash_for!( struct ServerLogin, [ke2_state, _cs], [>::KE2State], ); /// Optional parameters for server login start #[derive(Clone)] pub enum ServerLoginStartParameters { /// Specifying a context field that the client must agree on WithContext(Vec), /// Specifying a user identifier and server identifier that will be matched against the client WithIdentifiers(Identifiers), /// Specifying a context field that the client must agree on, /// along with a user identifier and and server identifier that will be matched against the client /// (in that order) WithContextAndIdentifiers(Vec, Identifiers), } impl Default for ServerLoginStartParameters { fn default() -> Self { Self::WithContext(Vec::new()) } } /// Contains the fields that are returned by a server login start pub struct ServerLoginStartResult { /// The message to send back to the client pub message: CredentialResponse, /// The state that the server must keep in order to finish the protocl pub state: ServerLogin, /// Handshake secret, only used in tests #[cfg(test)] pub handshake_secret: Vec, /// Server MAC key, only used in tests #[cfg(test)] pub server_mac_key: GenericArray::OutputSize>, /// OPRF key, only used in tests #[cfg(test)] pub oprf_key: GenericArray::ScalarLen>, } // Cannot be derived because it would require for CS to be Clone. impl Clone for ServerLoginStartResult { fn clone(&self) -> Self { Self { message: self.message.clone(), state: self.state.clone(), #[cfg(test)] handshake_secret: self.handshake_secret.clone(), #[cfg(test)] server_mac_key: self.server_mac_key.clone(), #[cfg(test)] oprf_key: self.oprf_key.clone(), } } } /// Contains the fields that are returned by a server login finish pub struct ServerLoginFinishResult { /// The session key between client and server pub session_key: Vec, _cs: PhantomData, /// Instance of the ClientRegistration, only used in tests for checking zeroize #[cfg(test)] pub state: ServerLogin, } // Cannot be derived because it would require for CS to be Clone. impl Clone for ServerLoginFinishResult { fn clone(&self) -> Self { Self { session_key: self.session_key.clone(), _cs: PhantomData, #[cfg(test)] state: self.state.clone(), } } } impl ServerLogin { /// Serialization into bytes pub fn serialize(&self) -> Vec { self.ke2_state.to_bytes() } /// Deserialization from bytes pub fn deserialize(bytes: &[u8]) -> Result { Ok(Self { _cs: PhantomData, ke2_state: >::KE2State::from_bytes::( bytes, )?, }) } /// From the client's "blinded" password, returns a challenge to be /// sent back to the client, as well as a ServerLogin pub fn start>( rng: &mut R, server_setup: &ServerSetup, password_file: Option>, l1: CredentialRequest, credential_identifier: &[u8], params: ServerLoginStartParameters, ) -> Result, ProtocolError> { let record = match password_file { Some(x) => x, None => ServerRegistration::dummy(rng, server_setup), }; let client_s_pk = record.0.client_s_pk.clone(); let (context, optional_ids) = match params { ServerLoginStartParameters::WithContext(context) => (context, None), ServerLoginStartParameters::WithIdentifiers(ids) => (Vec::new(), Some(ids)), ServerLoginStartParameters::WithContextAndIdentifiers(context, ids) => { (context, Some(ids)) } }; let server_s_sk = server_setup.keypair.private(); let server_s_pk = server_s_sk.public_key()?; 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, &record.0.envelope, ) .map_err(ProtocolError::into_custom)?; let (id_u, id_s) = bytestrings_from_identifiers( &optional_ids, &client_s_pk.to_arr(), &server_s_pk.to_arr(), ) .map_err(ProtocolError::into_custom)?; let l1_bytes = &l1.serialize(); let oprf_key = oprf_key_from_seed::( &server_setup.oprf_seed, credential_identifier, ) .map_err(ProtocolError::into_custom)?; let beta = oprf::evaluate(l1.alpha, &oprf_key); let credential_response_component = CredentialResponse::::serialize_without_ke(&beta, &masking_nonce, &masked_response); let result = CS::KeyExchange::generate_ke2( rng, l1_bytes.to_vec(), credential_response_component, l1.ke1_message, client_s_pk, server_s_sk.clone(), id_u, id_s, context, )?; let credential_response = CredentialResponse { beta, masking_nonce, masked_response, ke2_message: result.1, }; Ok(ServerLoginStartResult { message: credential_response, state: Self { _cs: PhantomData, ke2_state: result.0, }, #[cfg(test)] handshake_secret: result.2, #[cfg(test)] server_mac_key: result.3, #[cfg(test)] oprf_key: CS::OprfGroup::scalar_as_bytes(oprf_key), }) } /// From the client's second and final message, check the client's /// authentication and produce a message transport pub fn finish( self, message: CredentialFinalization, ) -> Result, ProtocolError> { let session_key = >::finish_ke( message.ke3_message, &self.ke2_state, ) .map_err(|e| match e { ProtocolError::VerificationError(PakeError::KeyExchangeMacValidationError) => { ProtocolError::VerificationError(PakeError::InvalidLoginError) } err => err, })?; Ok(ServerLoginFinishResult { session_key, _cs: PhantomData, #[cfg(test)] state: self, }) } #[cfg(test)] pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> { self.ke2_state.as_byte_ptrs() } } impl_serialize_and_deserialize_for!(ServerLogin); // Zeroize on drop implementations // This can't be derived because of the use of a phantom parameter impl Zeroize for ClientRegistration { fn zeroize(&mut self) { self.token.data.zeroize(); self.token.blind.zeroize(); } } impl Drop for ClientRegistration { fn drop(&mut self) { self.zeroize(); } } // This can't be derived because of the use of a phantom parameter impl Zeroize for ServerRegistration { fn zeroize(&mut self) { self.0.envelope.zeroize(); self.0.masking_key.zeroize(); self.0.client_s_pk.zeroize(); } } impl Drop for ServerRegistration { fn drop(&mut self) { self.zeroize(); } } // This can't be derived because of the use of a phantom parameter impl Zeroize for ClientLogin { fn zeroize(&mut self) { self.token.data.zeroize(); self.token.blind.zeroize(); self.ke1_state.zeroize(); self.serialized_credential_request.zeroize(); } } impl Drop for ClientLogin { fn drop(&mut self) { self.zeroize(); } } // This can't be derived because of the use of a phantom parameter impl Zeroize for ServerLogin { fn zeroize(&mut self) { self.ke2_state.zeroize(); } } impl Drop for ServerLogin { fn drop(&mut self) { self.zeroize(); } } // Helper functions fn get_password_derived_key, D: Hash>( token: &oprf::Token, beta: G, ) -> Result, ProtocolError> { let oprf_output = oprf::finalize::(&token.data, &token.blind, beta)?; SH::hash(oprf_output).map_err(ProtocolError::from) } fn oprf_key_from_seed( oprf_seed: &GenericArray, credential_identifier: &[u8], ) -> Result { let mut ikm = vec![0u8; G::ScalarLen::USIZE]; Hkdf::::from_prk(oprf_seed) .map_err(|_| InternalPakeError::HkdfError)? .expand(&[credential_identifier, STR_OPRF_KEY].concat(), &mut ikm) .map_err(|_| InternalPakeError::HkdfError)?; G::hash_to_scalar::(&ikm[..], STR_OPAQUE_DERIVE_KEY_PAIR) } fn mask_response( masking_key: &[u8], masking_nonce: &[u8], server_s_pk: &PublicKey, envelope: &Envelope, ) -> Result, ProtocolError> { let mut xor_pad = vec![0u8; ::ElemLen::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; ::ElemLen::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 = ::ElemLen::USIZE; let unchecked_server_s_pk = PublicKey::from_bytes(&plaintext[..key_len])?; let envelope = Envelope::deserialize(&plaintext[key_len..])?; // Ensure that public key is valid let server_s_pk = KeyPair::::check_public_key(unchecked_server_s_pk) .map_err(|_| ProtocolError::VerificationError(PakeError::SerializationError))?; Ok((server_s_pk, envelope)) }