//! The OPAQUE profile used for login, setup and password change (docs/02 "Authentication to the server"). //! //! Client and server MUST both use [`OpaqueSuite`] with these conventions, or login fails: //! * password input: the 32-byte `AUTH_INPUT`, which the client has already stretched with Argon2id, //! so the suite's key-stretching function is the identity; //! * credential identifier (server side only): the 16 raw bytes of the account UUID; //! * default parameters everywhere (`ClientRegistrationFinishParameters::default()`, //! `ClientLoginFinishParameters::default()`, `ServerLoginStartParameters::default()`): //! no custom identifiers and no context string. //! //! Messages travel base64url-encoded (no padding) inside the JSON bodies in this crate, using //! opaque-ke's own `serialize()` / `deserialize()`. pub use opaque_ke; /// Ristretto255 OPRF, Ristretto255 + TripleDH key exchange, SHA-512, identity KSF. pub struct OpaqueSuite; impl opaque_ke::CipherSuite for OpaqueSuite { type OprfCs = opaque_ke::Ristretto255; type KeGroup = opaque_ke::Ristretto255; type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh; type Ksf = opaque_ke::ksf::Identity; } #[cfg(test)] mod tests { use opaque_ke::{ ClientLogin, ClientLoginFinishParameters, ClientRegistration, ClientRegistrationFinishParameters, CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin, ServerLoginStartParameters, ServerRegistration, ServerSetup, }; use rand_core::OsRng; use super::OpaqueSuite; /// Registration then login across serialized messages, as the client and server exchange them. #[test] fn registration_and_login_roundtrip() { let mut rng = OsRng; let setup = ServerSetup::::new(&mut rng); let cred_id = uuid::Uuid::new_v4(); let auth_input = [7u8; 32]; let c = ClientRegistration::::start(&mut rng, &auth_input).unwrap(); let req = RegistrationRequest::deserialize(&c.message.serialize()).unwrap(); let s = ServerRegistration::::start(&setup, req, cred_id.as_bytes()).unwrap(); let resp = RegistrationResponse::deserialize(&s.message.serialize()).unwrap(); let fin = c.state.finish(&mut rng, &auth_input, resp, ClientRegistrationFinishParameters::default()).unwrap(); let upload = RegistrationUpload::::deserialize(&fin.message.serialize()).unwrap(); let record = ServerRegistration::finish(upload).serialize(); let login = |password: &[u8]| { let c = ClientLogin::::start(&mut OsRng, password).unwrap(); let ke1 = CredentialRequest::deserialize(&c.message.serialize()).unwrap(); let record = ServerRegistration::deserialize(&record).unwrap(); let s = ServerLogin::start( &mut OsRng, &setup, Some(record), ke1, cred_id.as_bytes(), ServerLoginStartParameters::default(), ) .unwrap(); let ke2 = CredentialResponse::deserialize(&s.message.serialize()).unwrap(); let Ok(done) = c.state.finish(password, ke2, ClientLoginFinishParameters::default()) else { return false; }; let ke3 = CredentialFinalization::deserialize(&done.message.serialize()).unwrap(); let server = s.state.finish(ke3).unwrap(); server.session_key == done.session_key }; assert!(login(&auth_input)); assert!(!login(&[8u8; 32])); } }