General cleanups and reorganizing code (#236)
This commit is contained in:
+431
-413
@@ -28,13 +28,20 @@ use hkdf::Hkdf;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
///////////////
|
||||
// Constants //
|
||||
// ========= //
|
||||
///////////////
|
||||
|
||||
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
|
||||
// ============
|
||||
////////////////////////////
|
||||
// High-level API Structs //
|
||||
// ====================== //
|
||||
////////////////////////////
|
||||
|
||||
/// The state elements the server holds upon setup
|
||||
#[cfg_attr(
|
||||
@@ -54,6 +61,86 @@ pub struct ServerSetup<
|
||||
pub(crate) fake_keypair: KeyPair<CS::KeGroup>,
|
||||
}
|
||||
|
||||
// Cannot be derived because it would require for CS to be bound.
|
||||
impl_clone_for!(
|
||||
struct ServerSetup<CS: CipherSuite>,
|
||||
[oprf_seed, keypair, fake_keypair],
|
||||
);
|
||||
impl_debug_eq_hash_for!(
|
||||
struct ServerSetup<CS: CipherSuite>,
|
||||
[oprf_seed, oprf_seed, fake_keypair],
|
||||
);
|
||||
|
||||
/// The state elements the client holds to register itself
|
||||
pub struct ClientRegistration<CS: CipherSuite> {
|
||||
alpha: CS::OprfGroup,
|
||||
/// token containing the client's password and the blinding factor
|
||||
pub(crate) token: oprf::Token<CS::OprfGroup>,
|
||||
}
|
||||
|
||||
impl_clone_for!(struct ClientRegistration<CS: CipherSuite>, [token, alpha]);
|
||||
impl_debug_eq_hash_for!(
|
||||
struct ClientRegistration<CS: CipherSuite>,
|
||||
[token],
|
||||
[oprf::Token<CS::OprfGroup>],
|
||||
);
|
||||
impl_serialize_and_deserialize_for!(ClientRegistration);
|
||||
|
||||
/// The state elements the server holds to record a registration
|
||||
pub struct ServerRegistration<CS: CipherSuite>(RegistrationUpload<CS>);
|
||||
|
||||
impl_clone_for!(tuple ServerRegistration<CS: CipherSuite>, [0]);
|
||||
impl_debug_eq_hash_for!(
|
||||
tuple ServerRegistration<CS: CipherSuite>,
|
||||
[0],
|
||||
);
|
||||
impl_serialize_and_deserialize_for!(ServerRegistration);
|
||||
|
||||
/// The state elements the client holds to perform a login
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(
|
||||
feature = "serialize",
|
||||
serde(bound(
|
||||
deserialize = "oprf::Token<CS::OprfGroup>: serde::Deserialize<'de>, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State: serde::Deserialize<'de>",
|
||||
serialize = "oprf::Token<CS::OprfGroup>: serde::Serialize, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State: serde::Serialize"
|
||||
))
|
||||
)]
|
||||
pub struct ClientLogin<CS: CipherSuite> {
|
||||
/// token containing the client's password and the blinding factor
|
||||
token: oprf::Token<CS::OprfGroup>,
|
||||
ke1_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State,
|
||||
serialized_credential_request: Vec<u8>,
|
||||
}
|
||||
|
||||
impl_clone_for!(struct ClientLogin<CS: CipherSuite>, [token, ke1_state, serialized_credential_request]);
|
||||
impl_debug_eq_hash_for!(
|
||||
struct ClientLogin<CS: CipherSuite>,
|
||||
[token, ke1_state, serialized_credential_request],
|
||||
[oprf::Token<CS::OprfGroup>, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State],
|
||||
);
|
||||
|
||||
/// The state elements the server holds to record a login
|
||||
pub struct ServerLogin<CS: CipherSuite> {
|
||||
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2State,
|
||||
_cs: PhantomData<CS>,
|
||||
}
|
||||
|
||||
impl_clone_for!(struct ServerLogin<CS: CipherSuite>, [ke2_state, _cs]);
|
||||
impl_debug_eq_hash_for!(
|
||||
struct ServerLogin<CS: CipherSuite>,
|
||||
[ke2_state, _cs],
|
||||
[<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2State],
|
||||
);
|
||||
impl_serialize_and_deserialize_for!(ServerLogin);
|
||||
|
||||
////////////////////////////////
|
||||
// High-level Implementations //
|
||||
// ========================== //
|
||||
////////////////////////////////
|
||||
|
||||
// Server Setup
|
||||
// ============
|
||||
|
||||
impl<CS: CipherSuite> ServerSetup<CS, PrivateKey<CS::KeGroup>> {
|
||||
/// Generate a new instance of server setup
|
||||
pub fn new<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
|
||||
@@ -108,33 +195,9 @@ impl<CS: CipherSuite, S: SecretKey<CS::KeGroup>> ServerSetup<CS, S> {
|
||||
}
|
||||
}
|
||||
|
||||
// Cannot be derived because it would require for CS to be bound.
|
||||
impl_clone_for!(
|
||||
struct ServerSetup<CS: CipherSuite>,
|
||||
[oprf_seed, keypair, fake_keypair],
|
||||
);
|
||||
impl_debug_eq_hash_for!(
|
||||
struct ServerSetup<CS: CipherSuite>,
|
||||
[oprf_seed, oprf_seed, fake_keypair],
|
||||
);
|
||||
|
||||
// Registration
|
||||
// ============
|
||||
|
||||
/// The state elements the client holds to register itself
|
||||
pub struct ClientRegistration<CS: CipherSuite> {
|
||||
alpha: CS::OprfGroup,
|
||||
/// token containing the client's password and the blinding factor
|
||||
pub(crate) token: oprf::Token<CS::OprfGroup>,
|
||||
}
|
||||
|
||||
impl_clone_for!(struct ClientRegistration<CS: CipherSuite>, [token, alpha]);
|
||||
impl_debug_eq_hash_for!(
|
||||
struct ClientRegistration<CS: CipherSuite>,
|
||||
[token],
|
||||
[oprf::Token<CS::OprfGroup>],
|
||||
);
|
||||
|
||||
impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
@@ -188,85 +251,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||
/* cannot provide raw pointer to self.token.blind until this is exposed in curve25519_dalek::scalar::Scalar */
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl_serialize_and_deserialize_for!(ClientRegistration);
|
||||
|
||||
/// Options for specifying custom identifiers
|
||||
#[derive(Clone)]
|
||||
pub enum Identifiers {
|
||||
/// Supply only a client identifier
|
||||
ClientIdentifier(Vec<u8>),
|
||||
/// Supply only a server identifier
|
||||
ServerIdentifier(Vec<u8>),
|
||||
/// Supply a client and server identifier
|
||||
ClientAndServerIdentifiers(Vec<u8>, Vec<u8>),
|
||||
}
|
||||
|
||||
pub(crate) fn bytestrings_from_identifiers(
|
||||
ids: &Option<Identifiers>,
|
||||
client_s_pk: &[u8],
|
||||
server_s_pk: &[u8],
|
||||
) -> Result<(Vec<u8>, Vec<u8>), ProtocolError> {
|
||||
let (client_identity, server_identity): (Vec<u8>, Vec<u8>) = match ids {
|
||||
None => (client_s_pk.to_vec(), server_s_pk.to_vec()),
|
||||
Some(Identifiers::ClientIdentifier(id_u)) => (id_u.clone(), server_s_pk.to_vec()),
|
||||
Some(Identifiers::ServerIdentifier(id_s)) => (client_s_pk.to_vec(), id_s.clone()),
|
||||
Some(Identifiers::ClientAndServerIdentifiers(id_u, id_s)) => (id_u.clone(), id_s.clone()),
|
||||
};
|
||||
Ok((
|
||||
serialize(&client_identity, 2)?,
|
||||
serialize(&server_identity, 2)?,
|
||||
))
|
||||
}
|
||||
|
||||
/// Optional parameters for client registration finish
|
||||
#[derive(Clone)]
|
||||
pub struct ClientRegistrationFinishParameters<'h, CS: CipherSuite> {
|
||||
/// Specifying the identifiers idU and idS
|
||||
pub identifiers: Option<Identifiers>,
|
||||
/// Specifying a configuration for the slow hash
|
||||
pub slow_hash: Option<&'h CS::SlowHash>,
|
||||
}
|
||||
|
||||
impl<'h, CS: CipherSuite> Default for ClientRegistrationFinishParameters<'h, CS> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
identifiers: None,
|
||||
slow_hash: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'h, CS: CipherSuite> ClientRegistrationFinishParameters<'h, CS> {
|
||||
/// Create a new [`ClientRegistrationFinishParameters`]
|
||||
pub fn new(identifiers: Option<Identifiers>, slow_hash: Option<&'h CS::SlowHash>) -> Self {
|
||||
Self {
|
||||
identifiers,
|
||||
slow_hash,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains the fields that are returned by a client registration start
|
||||
pub struct ClientRegistrationStartResult<CS: CipherSuite> {
|
||||
/// The registration request message to be sent to the server
|
||||
pub message: RegistrationRequest<CS>,
|
||||
/// The client state that must be persisted in order to complete registration
|
||||
pub state: ClientRegistration<CS>,
|
||||
}
|
||||
|
||||
// Cannot be derived because it would require for CS to be Clone.
|
||||
impl<CS: CipherSuite> Clone for ClientRegistrationStartResult<CS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
message: self.message.clone(),
|
||||
state: self.state.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||
/// Returns an initial "blinded" request to send to the server, as well as a ClientRegistration
|
||||
pub fn start<R: RngCore + CryptoRng>(
|
||||
blinding_factor_rng: &mut R,
|
||||
@@ -280,45 +265,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||
state: Self { alpha, token },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains the fields that are returned by a client registration finish
|
||||
pub struct ClientRegistrationFinishResult<CS: CipherSuite> {
|
||||
/// The registration upload message to be sent to the server
|
||||
pub message: RegistrationUpload<CS>,
|
||||
/// The export key output by client registration
|
||||
pub export_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
||||
/// The server's static public key
|
||||
pub server_s_pk: PublicKey<CS::KeGroup>,
|
||||
/// Instance of the ClientRegistration, only used in tests for checking zeroize
|
||||
#[cfg(test)]
|
||||
pub state: ClientRegistration<CS>,
|
||||
/// AuthKey, only used in tests
|
||||
#[cfg(test)]
|
||||
pub auth_key: Vec<u8>,
|
||||
/// Password derived key, only used in tests
|
||||
#[cfg(test)]
|
||||
pub randomized_pwd: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
||||
}
|
||||
|
||||
// Cannot be derived because it would require for CS to be Clone.
|
||||
impl<CS: CipherSuite> Clone for ClientRegistrationFinishResult<CS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
message: self.message.clone(),
|
||||
export_key: self.export_key.clone(),
|
||||
server_s_pk: self.server_s_pk.clone(),
|
||||
#[cfg(test)]
|
||||
state: self.state.clone(),
|
||||
#[cfg(test)]
|
||||
auth_key: self.auth_key.clone(),
|
||||
#[cfg(test)]
|
||||
randomized_pwd: self.randomized_pwd.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||
/// "Unblinds" the server's answer and returns a final message containing
|
||||
/// cryptographic identifiers, to be sent to the server on setup finalization
|
||||
pub fn finish<R: CryptoRng + RngCore>(
|
||||
@@ -366,36 +313,6 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains the fields that are returned by a server registration start.
|
||||
/// Note that there is no state output in this step
|
||||
pub struct ServerRegistrationStartResult<CS: CipherSuite> {
|
||||
/// The registration resposne message to send to the client
|
||||
pub message: RegistrationResponse<CS>,
|
||||
/// OPRF key, only used in tests
|
||||
#[cfg(test)]
|
||||
pub oprf_key: GenericArray<u8, <CS::OprfGroup as Group>::ScalarLen>,
|
||||
}
|
||||
|
||||
// Cannot be derived because it would require for CS to be Clone.
|
||||
impl<CS: CipherSuite> Clone for ServerRegistrationStartResult<CS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
message: self.message.clone(),
|
||||
#[cfg(test)]
|
||||
oprf_key: self.oprf_key.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The state elements the server holds to record a registration
|
||||
pub struct ServerRegistration<CS: CipherSuite>(RegistrationUpload<CS>);
|
||||
|
||||
impl_clone_for!(tuple ServerRegistration<CS: CipherSuite>, [0]);
|
||||
impl_debug_eq_hash_for!(
|
||||
tuple ServerRegistration<CS: CipherSuite>,
|
||||
[0],
|
||||
);
|
||||
|
||||
impl<CS: CipherSuite> ServerRegistration<CS> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
@@ -456,34 +373,9 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
|
||||
}
|
||||
}
|
||||
|
||||
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<CS::OprfGroup>: serde::Deserialize<'de>, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State: serde::Deserialize<'de>",
|
||||
serialize = "oprf::Token<CS::OprfGroup>: serde::Serialize, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State: serde::Serialize"
|
||||
))
|
||||
)]
|
||||
pub struct ClientLogin<CS: CipherSuite> {
|
||||
/// token containing the client's password and the blinding factor
|
||||
token: oprf::Token<CS::OprfGroup>,
|
||||
ke1_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State,
|
||||
serialized_credential_request: Vec<u8>,
|
||||
}
|
||||
|
||||
impl_clone_for!(struct ClientLogin<CS: CipherSuite>, [token, ke1_state, serialized_credential_request]);
|
||||
impl_debug_eq_hash_for!(
|
||||
struct ClientLogin<CS: CipherSuite>,
|
||||
[token, ke1_state, serialized_credential_request],
|
||||
[oprf::Token<CS::OprfGroup>, <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE1State],
|
||||
);
|
||||
|
||||
impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
|
||||
@@ -543,99 +435,6 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains the fields that are returned by a client login start
|
||||
pub struct ClientLoginStartResult<CS: CipherSuite> {
|
||||
/// The message to send to the server to begin the login protocol
|
||||
pub message: CredentialRequest<CS>,
|
||||
/// The state that the client must keep in order to complete the protocol
|
||||
pub state: ClientLogin<CS>,
|
||||
}
|
||||
|
||||
// Cannot be derived because it would require for CS to be Clone.
|
||||
impl<CS: CipherSuite> Clone for ClientLoginStartResult<CS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
message: self.message.clone(),
|
||||
state: self.state.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Optional parameters for client login finish
|
||||
#[derive(Clone)]
|
||||
pub struct ClientLoginFinishParameters<'h, CS: CipherSuite> {
|
||||
/// Specifying a context field that the server must agree on
|
||||
pub context: Option<Vec<u8>>,
|
||||
/// Specifying a user identifier and server identifier that will be matched against the server
|
||||
pub identifiers: Option<Identifiers>,
|
||||
/// Specifying a configuration for the slow hash
|
||||
pub slow_hash: Option<&'h CS::SlowHash>,
|
||||
}
|
||||
|
||||
impl<'h, CS: CipherSuite> Default for ClientLoginFinishParameters<'h, CS> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
context: None,
|
||||
identifiers: None,
|
||||
slow_hash: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'h, CS: CipherSuite> ClientLoginFinishParameters<'h, CS> {
|
||||
/// Create a new [`ClientLoginFinishParameters`]
|
||||
pub fn new(
|
||||
context: Option<Vec<u8>>,
|
||||
identifiers: Option<Identifiers>,
|
||||
slow_hash: Option<&'h CS::SlowHash>,
|
||||
) -> Self {
|
||||
Self {
|
||||
context,
|
||||
identifiers,
|
||||
slow_hash,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains the fields that are returned by a client login finish
|
||||
pub struct ClientLoginFinishResult<CS: CipherSuite> {
|
||||
/// The message to send to the server to complete the protocol
|
||||
pub message: CredentialFinalization<CS>,
|
||||
/// The session key
|
||||
pub session_key: Vec<u8>,
|
||||
/// The client-side export key
|
||||
pub export_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
||||
/// The server's static public key
|
||||
pub server_s_pk: PublicKey<CS::KeGroup>,
|
||||
/// Instance of the ClientLogin, only used in tests for checking zeroize
|
||||
#[cfg(test)]
|
||||
pub state: ClientLogin<CS>,
|
||||
/// Handshake secret, only used in tests
|
||||
#[cfg(test)]
|
||||
pub handshake_secret: Vec<u8>,
|
||||
/// Client MAC key, only used in tests
|
||||
#[cfg(test)]
|
||||
pub client_mac_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
||||
}
|
||||
|
||||
// Cannot be derived because it would require for CS to be Clone.
|
||||
impl<CS: CipherSuite> Clone for ClientLoginFinishResult<CS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
message: self.message.clone(),
|
||||
session_key: self.session_key.clone(),
|
||||
export_key: self.export_key.clone(),
|
||||
server_s_pk: self.server_s_pk.clone(),
|
||||
#[cfg(test)]
|
||||
state: self.state.clone(),
|
||||
#[cfg(test)]
|
||||
handshake_secret: self.handshake_secret.clone(),
|
||||
#[cfg(test)]
|
||||
client_mac_key: self.client_mac_key.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
/// Returns an initial "blinded" password request to send to the server, as well as a ClientLogin
|
||||
pub fn start<R: RngCore + CryptoRng>(
|
||||
@@ -743,93 +542,6 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The state elements the server holds to record a login
|
||||
pub struct ServerLogin<CS: CipherSuite> {
|
||||
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2State,
|
||||
_cs: PhantomData<CS>,
|
||||
}
|
||||
|
||||
impl_clone_for!(struct ServerLogin<CS: CipherSuite>, [ke2_state, _cs]);
|
||||
impl_debug_eq_hash_for!(
|
||||
struct ServerLogin<CS: CipherSuite>,
|
||||
[ke2_state, _cs],
|
||||
[<CS::KeyExchange as KeyExchange<CS::Hash, CS::KeGroup>>::KE2State],
|
||||
);
|
||||
|
||||
/// Optional parameters for server login start
|
||||
#[derive(Clone)]
|
||||
pub enum ServerLoginStartParameters {
|
||||
/// Specifying a context field that the client must agree on
|
||||
WithContext(Vec<u8>),
|
||||
/// Specifying a user identifier and server identifier that will be matched against the client
|
||||
WithIdentifiers(Identifiers),
|
||||
/// Specifying a context field that the client must agree on,
|
||||
/// along with a user identifier and and server identifier that will be matched against the client
|
||||
/// (in that order)
|
||||
WithContextAndIdentifiers(Vec<u8>, Identifiers),
|
||||
}
|
||||
|
||||
impl Default for ServerLoginStartParameters {
|
||||
fn default() -> Self {
|
||||
Self::WithContext(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains the fields that are returned by a server login start
|
||||
pub struct ServerLoginStartResult<CS: CipherSuite> {
|
||||
/// The message to send back to the client
|
||||
pub message: CredentialResponse<CS>,
|
||||
/// The state that the server must keep in order to finish the protocl
|
||||
pub state: ServerLogin<CS>,
|
||||
/// Handshake secret, only used in tests
|
||||
#[cfg(test)]
|
||||
pub handshake_secret: Vec<u8>,
|
||||
/// Server MAC key, only used in tests
|
||||
#[cfg(test)]
|
||||
pub server_mac_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
||||
/// OPRF key, only used in tests
|
||||
#[cfg(test)]
|
||||
pub oprf_key: GenericArray<u8, <CS::OprfGroup as Group>::ScalarLen>,
|
||||
}
|
||||
|
||||
// Cannot be derived because it would require for CS to be Clone.
|
||||
impl<CS: CipherSuite> Clone for ServerLoginStartResult<CS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
message: self.message.clone(),
|
||||
state: self.state.clone(),
|
||||
#[cfg(test)]
|
||||
handshake_secret: self.handshake_secret.clone(),
|
||||
#[cfg(test)]
|
||||
server_mac_key: self.server_mac_key.clone(),
|
||||
#[cfg(test)]
|
||||
oprf_key: self.oprf_key.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains the fields that are returned by a server login finish
|
||||
pub struct ServerLoginFinishResult<CS: CipherSuite> {
|
||||
/// The session key between client and server
|
||||
pub session_key: Vec<u8>,
|
||||
_cs: PhantomData<CS>,
|
||||
/// Instance of the ClientRegistration, only used in tests for checking zeroize
|
||||
#[cfg(test)]
|
||||
pub state: ServerLogin<CS>,
|
||||
}
|
||||
|
||||
// Cannot be derived because it would require for CS to be Clone.
|
||||
impl<CS: CipherSuite> Clone for ServerLoginFinishResult<CS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
session_key: self.session_key.clone(),
|
||||
_cs: PhantomData,
|
||||
#[cfg(test)]
|
||||
state: self.state.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> ServerLogin<CS> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
@@ -964,68 +676,297 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
||||
}
|
||||
}
|
||||
|
||||
impl_serialize_and_deserialize_for!(ServerLogin);
|
||||
/////////////////////////
|
||||
// Convenience Structs //
|
||||
//==================== //
|
||||
/////////////////////////
|
||||
|
||||
// Zeroize on drop implementations
|
||||
/// Options for specifying custom identifiers
|
||||
#[derive(Clone)]
|
||||
pub enum Identifiers {
|
||||
/// Supply only a client identifier
|
||||
ClientIdentifier(Vec<u8>),
|
||||
/// Supply only a server identifier
|
||||
ServerIdentifier(Vec<u8>),
|
||||
/// Supply a client and server identifier
|
||||
ClientAndServerIdentifiers(Vec<u8>, Vec<u8>),
|
||||
}
|
||||
|
||||
// This can't be derived because of the use of a phantom parameter
|
||||
impl<CS: CipherSuite> Zeroize for ClientRegistration<CS> {
|
||||
fn zeroize(&mut self) {
|
||||
self.token.data.zeroize();
|
||||
self.token.blind.zeroize();
|
||||
/// Optional parameters for client registration finish
|
||||
#[derive(Clone)]
|
||||
pub struct ClientRegistrationFinishParameters<'h, CS: CipherSuite> {
|
||||
/// Specifying the identifiers idU and idS
|
||||
pub identifiers: Option<Identifiers>,
|
||||
/// Specifying a configuration for the slow hash
|
||||
pub slow_hash: Option<&'h CS::SlowHash>,
|
||||
}
|
||||
|
||||
impl<'h, CS: CipherSuite> Default for ClientRegistrationFinishParameters<'h, CS> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
identifiers: None,
|
||||
slow_hash: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> Drop for ClientRegistration<CS> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
impl<'h, CS: CipherSuite> ClientRegistrationFinishParameters<'h, CS> {
|
||||
/// Create a new [`ClientRegistrationFinishParameters`]
|
||||
pub fn new(identifiers: Option<Identifiers>, slow_hash: Option<&'h CS::SlowHash>) -> Self {
|
||||
Self {
|
||||
identifiers,
|
||||
slow_hash,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This can't be derived because of the use of a phantom parameter
|
||||
impl<CS: CipherSuite> Zeroize for ServerRegistration<CS> {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.envelope.zeroize();
|
||||
self.0.masking_key.zeroize();
|
||||
self.0.client_s_pk.zeroize();
|
||||
/// Contains the fields that are returned by a client registration start
|
||||
pub struct ClientRegistrationStartResult<CS: CipherSuite> {
|
||||
/// The registration request message to be sent to the server
|
||||
pub message: RegistrationRequest<CS>,
|
||||
/// The client state that must be persisted in order to complete registration
|
||||
pub state: ClientRegistration<CS>,
|
||||
}
|
||||
|
||||
// Cannot be derived because it would require for CS to be Clone.
|
||||
impl<CS: CipherSuite> Clone for ClientRegistrationStartResult<CS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
message: self.message.clone(),
|
||||
state: self.state.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> Drop for ServerRegistration<CS> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
/// Contains the fields that are returned by a client registration finish
|
||||
pub struct ClientRegistrationFinishResult<CS: CipherSuite> {
|
||||
/// The registration upload message to be sent to the server
|
||||
pub message: RegistrationUpload<CS>,
|
||||
/// The export key output by client registration
|
||||
pub export_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
||||
/// The server's static public key
|
||||
pub server_s_pk: PublicKey<CS::KeGroup>,
|
||||
/// Instance of the ClientRegistration, only used in tests for checking zeroize
|
||||
#[cfg(test)]
|
||||
pub state: ClientRegistration<CS>,
|
||||
/// AuthKey, only used in tests
|
||||
#[cfg(test)]
|
||||
pub auth_key: Vec<u8>,
|
||||
/// Password derived key, only used in tests
|
||||
#[cfg(test)]
|
||||
pub randomized_pwd: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
||||
}
|
||||
|
||||
// Cannot be derived because it would require for CS to be Clone.
|
||||
impl<CS: CipherSuite> Clone for ClientRegistrationFinishResult<CS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
message: self.message.clone(),
|
||||
export_key: self.export_key.clone(),
|
||||
server_s_pk: self.server_s_pk.clone(),
|
||||
#[cfg(test)]
|
||||
state: self.state.clone(),
|
||||
#[cfg(test)]
|
||||
auth_key: self.auth_key.clone(),
|
||||
#[cfg(test)]
|
||||
randomized_pwd: self.randomized_pwd.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This can't be derived because of the use of a phantom parameter
|
||||
impl<CS: CipherSuite> Zeroize for ClientLogin<CS> {
|
||||
fn zeroize(&mut self) {
|
||||
self.token.data.zeroize();
|
||||
self.token.blind.zeroize();
|
||||
self.ke1_state.zeroize();
|
||||
self.serialized_credential_request.zeroize();
|
||||
/// Contains the fields that are returned by a server registration start.
|
||||
/// Note that there is no state output in this step
|
||||
pub struct ServerRegistrationStartResult<CS: CipherSuite> {
|
||||
/// The registration resposne message to send to the client
|
||||
pub message: RegistrationResponse<CS>,
|
||||
/// OPRF key, only used in tests
|
||||
#[cfg(test)]
|
||||
pub oprf_key: GenericArray<u8, <CS::OprfGroup as Group>::ScalarLen>,
|
||||
}
|
||||
|
||||
// Cannot be derived because it would require for CS to be Clone.
|
||||
impl<CS: CipherSuite> Clone for ServerRegistrationStartResult<CS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
message: self.message.clone(),
|
||||
#[cfg(test)]
|
||||
oprf_key: self.oprf_key.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> Drop for ClientLogin<CS> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
/// Contains the fields that are returned by a client login start
|
||||
pub struct ClientLoginStartResult<CS: CipherSuite> {
|
||||
/// The message to send to the server to begin the login protocol
|
||||
pub message: CredentialRequest<CS>,
|
||||
/// The state that the client must keep in order to complete the protocol
|
||||
pub state: ClientLogin<CS>,
|
||||
}
|
||||
|
||||
// Cannot be derived because it would require for CS to be Clone.
|
||||
impl<CS: CipherSuite> Clone for ClientLoginStartResult<CS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
message: self.message.clone(),
|
||||
state: self.state.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This can't be derived because of the use of a phantom parameter
|
||||
impl<CS: CipherSuite> Zeroize for ServerLogin<CS> {
|
||||
fn zeroize(&mut self) {
|
||||
self.ke2_state.zeroize();
|
||||
/// Optional parameters for client login finish
|
||||
#[derive(Clone)]
|
||||
pub struct ClientLoginFinishParameters<'h, CS: CipherSuite> {
|
||||
/// Specifying a context field that the server must agree on
|
||||
pub context: Option<Vec<u8>>,
|
||||
/// Specifying a user identifier and server identifier that will be matched against the server
|
||||
pub identifiers: Option<Identifiers>,
|
||||
/// Specifying a configuration for the slow hash
|
||||
pub slow_hash: Option<&'h CS::SlowHash>,
|
||||
}
|
||||
|
||||
impl<'h, CS: CipherSuite> Default for ClientLoginFinishParameters<'h, CS> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
context: None,
|
||||
identifiers: None,
|
||||
slow_hash: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> Drop for ServerLogin<CS> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
impl<'h, CS: CipherSuite> ClientLoginFinishParameters<'h, CS> {
|
||||
/// Create a new [`ClientLoginFinishParameters`]
|
||||
pub fn new(
|
||||
context: Option<Vec<u8>>,
|
||||
identifiers: Option<Identifiers>,
|
||||
slow_hash: Option<&'h CS::SlowHash>,
|
||||
) -> Self {
|
||||
Self {
|
||||
context,
|
||||
identifiers,
|
||||
slow_hash,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains the fields that are returned by a client login finish
|
||||
pub struct ClientLoginFinishResult<CS: CipherSuite> {
|
||||
/// The message to send to the server to complete the protocol
|
||||
pub message: CredentialFinalization<CS>,
|
||||
/// The session key
|
||||
pub session_key: Vec<u8>,
|
||||
/// The client-side export key
|
||||
pub export_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
||||
/// The server's static public key
|
||||
pub server_s_pk: PublicKey<CS::KeGroup>,
|
||||
/// Instance of the ClientLogin, only used in tests for checking zeroize
|
||||
#[cfg(test)]
|
||||
pub state: ClientLogin<CS>,
|
||||
/// Handshake secret, only used in tests
|
||||
#[cfg(test)]
|
||||
pub handshake_secret: Vec<u8>,
|
||||
/// Client MAC key, only used in tests
|
||||
#[cfg(test)]
|
||||
pub client_mac_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
||||
}
|
||||
|
||||
// Cannot be derived because it would require for CS to be Clone.
|
||||
impl<CS: CipherSuite> Clone for ClientLoginFinishResult<CS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
message: self.message.clone(),
|
||||
session_key: self.session_key.clone(),
|
||||
export_key: self.export_key.clone(),
|
||||
server_s_pk: self.server_s_pk.clone(),
|
||||
#[cfg(test)]
|
||||
state: self.state.clone(),
|
||||
#[cfg(test)]
|
||||
handshake_secret: self.handshake_secret.clone(),
|
||||
#[cfg(test)]
|
||||
client_mac_key: self.client_mac_key.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains the fields that are returned by a server login finish
|
||||
pub struct ServerLoginFinishResult<CS: CipherSuite> {
|
||||
/// The session key between client and server
|
||||
pub session_key: Vec<u8>,
|
||||
_cs: PhantomData<CS>,
|
||||
/// Instance of the ClientRegistration, only used in tests for checking zeroize
|
||||
#[cfg(test)]
|
||||
pub state: ServerLogin<CS>,
|
||||
}
|
||||
|
||||
// Cannot be derived because it would require for CS to be Clone.
|
||||
impl<CS: CipherSuite> Clone for ServerLoginFinishResult<CS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
session_key: self.session_key.clone(),
|
||||
_cs: PhantomData,
|
||||
#[cfg(test)]
|
||||
state: self.state.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Optional parameters for server login start
|
||||
#[derive(Clone)]
|
||||
pub enum ServerLoginStartParameters {
|
||||
/// Specifying a context field that the client must agree on
|
||||
WithContext(Vec<u8>),
|
||||
/// Specifying a user identifier and server identifier that will be matched against the client
|
||||
WithIdentifiers(Identifiers),
|
||||
/// Specifying a context field that the client must agree on,
|
||||
/// along with a user identifier and and server identifier that will be matched against the client
|
||||
/// (in that order)
|
||||
WithContextAndIdentifiers(Vec<u8>, Identifiers),
|
||||
}
|
||||
|
||||
impl Default for ServerLoginStartParameters {
|
||||
fn default() -> Self {
|
||||
Self::WithContext(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains the fields that are returned by a server login start
|
||||
pub struct ServerLoginStartResult<CS: CipherSuite> {
|
||||
/// The message to send back to the client
|
||||
pub message: CredentialResponse<CS>,
|
||||
/// The state that the server must keep in order to finish the protocl
|
||||
pub state: ServerLogin<CS>,
|
||||
/// Handshake secret, only used in tests
|
||||
#[cfg(test)]
|
||||
pub handshake_secret: Vec<u8>,
|
||||
/// Server MAC key, only used in tests
|
||||
#[cfg(test)]
|
||||
pub server_mac_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
|
||||
/// OPRF key, only used in tests
|
||||
#[cfg(test)]
|
||||
pub oprf_key: GenericArray<u8, <CS::OprfGroup as Group>::ScalarLen>,
|
||||
}
|
||||
|
||||
// Cannot be derived because it would require for CS to be Clone.
|
||||
impl<CS: CipherSuite> Clone for ServerLoginStartResult<CS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
message: self.message.clone(),
|
||||
state: self.state.clone(),
|
||||
#[cfg(test)]
|
||||
handshake_secret: self.handshake_secret.clone(),
|
||||
#[cfg(test)]
|
||||
server_mac_key: self.server_mac_key.clone(),
|
||||
#[cfg(test)]
|
||||
oprf_key: self.oprf_key.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// Helper functions and Trait Implementations //
|
||||
// ========================================== //
|
||||
////////////////////////////////////////////////
|
||||
|
||||
// Helper functions
|
||||
|
||||
fn get_password_derived_key<CS: CipherSuite>(
|
||||
@@ -1107,3 +1048,80 @@ fn unmask_response<CS: CipherSuite>(
|
||||
|
||||
Ok((server_s_pk, envelope))
|
||||
}
|
||||
|
||||
pub(crate) fn bytestrings_from_identifiers(
|
||||
ids: &Option<Identifiers>,
|
||||
client_s_pk: &[u8],
|
||||
server_s_pk: &[u8],
|
||||
) -> Result<(Vec<u8>, Vec<u8>), ProtocolError> {
|
||||
let (client_identity, server_identity): (Vec<u8>, Vec<u8>) = match ids {
|
||||
None => (client_s_pk.to_vec(), server_s_pk.to_vec()),
|
||||
Some(Identifiers::ClientIdentifier(id_u)) => (id_u.clone(), server_s_pk.to_vec()),
|
||||
Some(Identifiers::ServerIdentifier(id_s)) => (client_s_pk.to_vec(), id_s.clone()),
|
||||
Some(Identifiers::ClientAndServerIdentifiers(id_u, id_s)) => (id_u.clone(), id_s.clone()),
|
||||
};
|
||||
Ok((
|
||||
serialize(&client_identity, 2)?,
|
||||
serialize(&server_identity, 2)?,
|
||||
))
|
||||
}
|
||||
|
||||
// Zeroize on drop implementations
|
||||
|
||||
// This can't be derived because of the use of a phantom parameter
|
||||
impl<CS: CipherSuite> Zeroize for ClientRegistration<CS> {
|
||||
fn zeroize(&mut self) {
|
||||
self.token.data.zeroize();
|
||||
self.token.blind.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> Drop for ClientRegistration<CS> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
// This can't be derived because of the use of a phantom parameter
|
||||
impl<CS: CipherSuite> Zeroize for ServerRegistration<CS> {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.envelope.zeroize();
|
||||
self.0.masking_key.zeroize();
|
||||
self.0.client_s_pk.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> Drop for ServerRegistration<CS> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
// This can't be derived because of the use of a phantom parameter
|
||||
impl<CS: CipherSuite> Zeroize for ClientLogin<CS> {
|
||||
fn zeroize(&mut self) {
|
||||
self.token.data.zeroize();
|
||||
self.token.blind.zeroize();
|
||||
self.ke1_state.zeroize();
|
||||
self.serialized_credential_request.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> Drop for ClientLogin<CS> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
// This can't be derived because of the use of a phantom parameter
|
||||
impl<CS: CipherSuite> Zeroize for ServerLogin<CS> {
|
||||
fn zeroize(&mut self) {
|
||||
self.ke2_state.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> Drop for ServerLogin<CS> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user