use opaque_ke::ciphersuite::CipherSuite; use rand::{CryptoRng, RngCore}; #[derive(thiserror::Error, Debug)] pub enum AuthenticationError { #[error("Protocol error: `{0}`")] ProtocolError(#[from] opaque_ke::errors::ProtocolError), } pub type AuthenticationResult = std::result::Result; pub use opaque_ke::keypair::{PrivateKey, PublicKey}; pub type KeyPair = opaque_ke::keypair::KeyPair<::Group>; /// A wrapper around argon2 to provide the [`opaque_ke::slow_hash::SlowHash`] trait. pub struct ArgonHasher; /// The Argon hasher used for bruteforce protection. /// /// Note that it isn't used to "hash the passwords", so it doesn't need a variable salt. Instead, /// it's used as part of the OPAQUE protocol to add a slow hashing method, making bruteforce /// attacks prohibitively more expensive. impl ArgonHasher { /// Fixed salt, doesn't affect the security. It is only used to make attacks more /// computationally intensive, it doesn't serve any security purpose. const SALT: &'static [u8] = b"lldap_opaque_salt"; /// Config for the argon hasher. Security enthusiasts may want to tweak this for their system. const CONFIG: &'static argon2::Config<'static> = &argon2::Config { ad: &[], hash_length: 128, lanes: 1, mem_cost: 50 * 1024, // 50 MB, in KB secret: &[], thread_mode: argon2::ThreadMode::Sequential, time_cost: 1, variant: argon2::Variant::Argon2id, version: argon2::Version::Version13, }; } impl opaque_ke::slow_hash::SlowHash for ArgonHasher { fn hash( input: generic_array::GenericArray::OutputSize>, ) -> Result, opaque_ke::errors::InternalPakeError> { argon2::hash_raw(&input, Self::SALT, Self::CONFIG) .map_err(|_| opaque_ke::errors::InternalPakeError::HashingFailure) } } /// The ciphersuite trait allows to specify the underlying primitives /// that will be used in the OPAQUE protocol #[allow(dead_code)] pub struct DefaultSuite; impl CipherSuite for DefaultSuite { type Group = curve25519_dalek::ristretto::RistrettoPoint; type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDH; type Hash = sha2::Sha512; /// Use argon2 as the slow hashing algorithm for our CipherSuite. type SlowHash = ArgonHasher; } /// Client-side code for OPAQUE protocol handling, to register a new user and login. All methods' /// results must be sent to the server using the serialized `.message`. Incoming messages can be /// deserialized using the type's `deserialize` method. #[cfg(feature = "opaque_client")] pub mod client { pub use super::*; /// Methods to register a new user, from the client side. pub mod registration { pub use super::*; pub type ClientRegistration = opaque_ke::ClientRegistration; pub type ClientRegistrationStartResult = opaque_ke::ClientRegistrationStartResult; pub type ClientRegistrationFinishResult = opaque_ke::ClientRegistrationFinishResult; pub type RegistrationResponse = opaque_ke::RegistrationResponse; pub use opaque_ke::ClientRegistrationFinishParameters; /// Initiate the registration negotiation. pub fn start_registration( password: &str, rng: &mut R, ) -> AuthenticationResult { Ok(ClientRegistration::start(rng, password.as_bytes())?) } /// Finalize the registration negotiation. pub fn finish_registration( registration_start: ClientRegistration, registration_response: RegistrationResponse, rng: &mut R, ) -> AuthenticationResult { Ok(registration_start.finish( rng, registration_response, ClientRegistrationFinishParameters::default(), )?) } } /// Methods to login, from the client side. pub mod login { pub use super::*; pub type ClientLogin = opaque_ke::ClientLogin; pub type ClientLoginFinishResult = opaque_ke::ClientLoginFinishResult; pub type ClientLoginStartResult = opaque_ke::ClientLoginStartResult; pub type CredentialResponse = opaque_ke::CredentialResponse; pub type CredentialFinalization = opaque_ke::CredentialFinalization; pub use opaque_ke::ClientLoginFinishParameters; /// Initiate the login negotiation. pub fn start_login( password: &str, rng: &mut R, ) -> AuthenticationResult { Ok(ClientLogin::start(rng, password.as_bytes())?) } /// Finalize the client login negotiation. pub fn finish_login( login_start: ClientLogin, login_response: CredentialResponse, ) -> AuthenticationResult { Ok(login_start.finish(login_response, ClientLoginFinishParameters::default())?) } } } /// Server-side code for OPAQUE protocol handling, to register a new user and login. The /// intermediate results must be sent to the client using the serialized `.message`. #[cfg(feature = "opaque_server")] pub mod server { pub use super::*; pub type ServerRegistration = opaque_ke::ServerRegistration; pub type ServerSetup = opaque_ke::ServerSetup; /// Methods to register a new user, from the server side. pub mod registration { pub use super::*; pub type RegistrationRequest = opaque_ke::RegistrationRequest; pub type RegistrationUpload = opaque_ke::RegistrationUpload; pub type ServerRegistrationStartResult = opaque_ke::ServerRegistrationStartResult; /// Start a registration process, from a request sent by the client. /// /// The result must be kept for the next step. pub fn start_registration( server_setup: &ServerSetup, registration_request: RegistrationRequest, username: &str, ) -> AuthenticationResult { Ok(ServerRegistration::start( server_setup, registration_request, username.as_bytes(), )?) } /// Finish to register a new user, and get the data to store in the database. pub fn get_password_file(registration_upload: RegistrationUpload) -> ServerRegistration { ServerRegistration::finish(registration_upload) } } /// Methods to handle user login, from the server-side. pub mod login { pub use super::*; pub type CredentialFinalization = opaque_ke::CredentialFinalization; pub type CredentialRequest = opaque_ke::CredentialRequest; pub type ServerLogin = opaque_ke::ServerLogin; pub type ServerLoginStartResult = opaque_ke::ServerLoginStartResult; pub type ServerLoginFinishResult = opaque_ke::ServerLoginFinishResult; pub use opaque_ke::ServerLoginStartParameters; /// Start a login process, from a request sent by the client. /// /// The result must be kept for the next step. pub fn start_login( rng: &mut R, server_setup: &ServerSetup, password_file: Option, credential_request: CredentialRequest, username: &str, ) -> AuthenticationResult { Ok(ServerLogin::start( rng, server_setup, password_file, credential_request, username.as_bytes(), ServerLoginStartParameters::default(), )?) } /// Finish to authorize a new user, and get the session key to decrypt associated data. pub fn finish_login( login_start: ServerLogin, credential_finalization: CredentialFinalization, ) -> AuthenticationResult { Ok(login_start.finish(credential_finalization)?) } } }