Skip to main content

opaque_vx/
lib.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (c) VexaHub and contributors.
3// Copyright (c) Meta Platforms, Inc. and affiliates.
4
5//! An implementation of the OPAQUE augmented password authentication key
6//! exchange protocol
7//!
8//! ### Minimum Supported Rust Version
9//!
10//! Rust **1.90** or higher.
11//!
12//! # Overview
13//!
14//! OPAQUE is a protocol between a client and a server. They must first agree on
15//! a collection of primitives to be kept consistent throughout protocol
16//! execution. These include:
17//! * a finite cyclic group along with a point representation
18//!   * for the OPRF and
19//!   * for the key exchange
20//! * a key exchange protocol,
21//! * a hashing function, and
22//! * a key stretching function.
23//!
24//! We will use the following choices in this example:
25//! ```ignore
26//! use opaque_vx::CipherSuite;
27//!
28//! struct Default;
29//!
30//! impl CipherSuite for Default {
31//!     type OprfCs = opaque_vx::Ristretto255;
32//!     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
33//!     type Ksf = opaque_vx::ksf::Identity;
34//! }
35//! ```
36//! See [examples/simple_login.rs](https://github.com/facebook/opaque-ke/blob/main/examples/simple_login.rs)
37//! for a working example of a simple password-based login using OPAQUE.
38//!
39//! Note that our choice of key stretching function in this example, `Identity`,
40//! is selected only to ensure that the tests execute quickly. A real
41//! application should use an actual key stretching function, such as `Argon2`,
42//! which can be enabled through the `argon2` feature. See more details in
43//! the [features](#features) section.
44//!
45//! ## Setup
46//! To set up the protocol, the server begins by creating a `ServerSetup`
47//! object:
48//! ```
49//! # use opaque_vx::errors::ProtocolError;
50//! # use opaque_vx::CipherSuite;
51//! # use opaque_vx::ServerSetup;
52//! # struct Default;
53//! # #[cfg(feature = "ristretto255")]
54//! # impl CipherSuite for Default {
55//! #     type OprfCs = opaque_vx::Ristretto255;
56//! #     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
57//! #     type Ksf = opaque_vx::ksf::Identity;
58//! # }
59//! # #[cfg(not(feature = "ristretto255"))]
60//! # impl CipherSuite for Default {
61//! #     type OprfCs = p256::NistP256;
62//! #     type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
63//! #     type Ksf = opaque_vx::ksf::Identity;
64//! # }
65//! use rand::Rng;
66//! use rand::rngs::SysRng;
67//! use rand::rand_core::UnwrapErr;
68//!
69//! let mut rng = UnwrapErr(SysRng);
70//! let server_setup = ServerSetup::<Default>::new(&mut rng);
71//! # Ok::<(), ProtocolError>(())
72//! ```
73//! The server must persist an instance of [`ServerSetup`] for the registration
74//! and login steps, and can use [`ServerSetup::serialize`] and
75//! [`ServerSetup::deserialize`] to save and restore the instance.
76//!
77//! ## Registration
78//! The registration protocol between the client and server consists of four
79//! steps along with three messages: [`RegistrationRequest`],
80//! [`RegistrationResponse`], and [`RegistrationUpload`]. A successful execution
81//! of the registration protocol results in the server producing a password file
82//! corresponding to a server-side identifier for the client, along with the
83//! password provided by the client. This password file is typically stored in a
84//! key-value database, where the keys consist of these server-side identifiers
85//! for each client, and the values consist of their corresponding password
86//! files, to be retrieved upon future login attempts made by the client.
87//! It is your responsibility to ensure that the identifier used to form the
88//! initial [`RegistrationRequest`], typically supplied by the client, matches
89//! the database key used in the final [`RegistrationUpload`] step.
90//!
91//! Note that the [`RegistrationUpload`] message contains sensitive information
92//! (about as sensitive as a hash of the password), and hence should be
93//! protected with confidentiality guarantees by the consumer of this library.
94//!
95//! ### Client Registration Start
96//! In the first step of registration, the client chooses as input a
97//! registration password. The client runs [`ClientRegistration::start`] to
98//! produce a [`ClientRegistrationStartResult`], which consists of a
99//! [`RegistrationRequest`] to be sent to the server and a
100//! [`ClientRegistration`] which must be persisted on the client for the final
101//! step of client registration.
102//! ```
103//! # use opaque_vx::{
104//! #   errors::ProtocolError,
105//! #   ServerRegistration,
106//! #   ksf::Identity,
107//! # };
108//! # use opaque_vx::CipherSuite;
109//! # struct Default;
110//! # #[cfg(feature = "ristretto255")]
111//! # impl CipherSuite for Default {
112//! #     type OprfCs = opaque_vx::Ristretto255;
113//! #     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
114//! #     type Ksf = opaque_vx::ksf::Identity;
115//! # }
116//! # #[cfg(not(feature = "ristretto255"))]
117//! # impl CipherSuite for Default {
118//! #     type OprfCs = p256::NistP256;
119//! #     type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
120//! #     type Ksf = opaque_vx::ksf::Identity;
121//! # }
122//! use opaque_vx::ClientRegistration;
123//! use rand::Rng;
124//! use rand::rngs::SysRng;
125//! use rand::rand_core::UnwrapErr;
126//!
127//! let mut client_rng = UnwrapErr(SysRng);
128//! let client_registration_start_result =
129//!     ClientRegistration::<Default>::start(&mut client_rng, b"password")?;
130//! # Ok::<(), ProtocolError>(())
131//! ```
132//!
133//! ### Server Registration Start
134//! In the second step of registration, the server takes as input a persisted
135//! instance of [`ServerSetup`], a [`RegistrationRequest`] from the client, and
136//! a server-side identifier for the client. The server runs
137//! [`ServerRegistration::start`] to produce a
138//! [`ServerRegistrationStartResult`], which consists of a
139//! [`RegistrationResponse`] to be returned to the client.
140//! ```
141//! # use opaque_vx::{
142//! #   errors::ProtocolError,
143//! #   ClientRegistration,
144//! #   ServerSetup,
145//! #   ksf::Identity,
146//! # };
147//! # use opaque_vx::CipherSuite;
148//! # struct Default;
149//! # #[cfg(feature = "ristretto255")]
150//! # impl CipherSuite for Default {
151//! #     type OprfCs = opaque_vx::Ristretto255;
152//! #     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
153//! #     type Ksf = opaque_vx::ksf::Identity;
154//! # }
155//! # #[cfg(not(feature = "ristretto255"))]
156//! # impl CipherSuite for Default {
157//! #     type OprfCs = p256::NistP256;
158//! #     type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
159//! #     type Ksf = opaque_vx::ksf::Identity;
160//! # }
161//! # use rand::{rngs::SysRng, Rng};
162//! # use rand::rand_core::UnwrapErr;
163//! # let mut client_rng = UnwrapErr(SysRng);
164//! # let client_registration_start_result = ClientRegistration::<Default>::start(
165//! #     &mut client_rng,
166//! #     b"password",
167//! # )?;
168//! use opaque_vx::ServerRegistration;
169//!
170//! # let mut server_rng = UnwrapErr(SysRng);
171//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
172//! let server_registration_start_result = ServerRegistration::<Default>::start(
173//!     &server_setup,
174//!     client_registration_start_result.message,
175//!     b"alice@example.com",
176//! )?;
177//! # Ok::<(), ProtocolError>(())
178//! ```
179//!
180//! ### Client Registration Finish
181//! In the third step of registration, the client takes as input a
182//! [`RegistrationResponse`] from the server, and a [`ClientRegistration`] from
183//! the first step of registration. The client runs
184//! [`ClientRegistration::finish`] to
185//! produce a [`ClientRegistrationFinishResult`], which consists of a
186//! [`RegistrationUpload`] to be sent to the server and an `export_key` field
187//! which can be used optionally as described in the [Export Key](#export-key)
188//! section.
189//! ```
190//! # use opaque_vx::{
191//! #   errors::ProtocolError,
192//! #   ClientRegistration, ServerRegistration, ServerSetup,
193//! #   ksf::Identity,
194//! # };
195//! # use opaque_vx::CipherSuite;
196//! # struct Default;
197//! # #[cfg(feature = "ristretto255")]
198//! # impl CipherSuite for Default {
199//! #     type OprfCs = opaque_vx::Ristretto255;
200//! #     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
201//! #     type Ksf = opaque_vx::ksf::Identity;
202//! # }
203//! # #[cfg(not(feature = "ristretto255"))]
204//! # impl CipherSuite for Default {
205//! #     type OprfCs = p256::NistP256;
206//! #     type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
207//! #     type Ksf = opaque_vx::ksf::Identity;
208//! # }
209//! # use rand::{rngs::SysRng, Rng};
210//! # use rand::rand_core::UnwrapErr;
211//! # let mut client_rng = UnwrapErr(SysRng);
212//! # let client_registration_start_result = ClientRegistration::<Default>::start(
213//! #     &mut client_rng,
214//! #     b"password",
215//! # )?;
216//! # let mut server_rng = UnwrapErr(SysRng);
217//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
218//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?;
219//! use opaque_vx::ClientRegistrationFinishParameters;
220//!
221//! let client_registration_finish_result = client_registration_start_result.state.finish(
222//!     &mut client_rng,
223//!     b"password",
224//!     server_registration_start_result.message,
225//!     ClientRegistrationFinishParameters::default(),
226//! )?;
227//! # Ok::<(), ProtocolError>(())
228//! ```
229//!
230//! ### Server Registration Finish
231//! In the fourth step of registration, the server takes as input a
232//! [`RegistrationUpload`] from the client, and a [`ServerRegistration`] from
233//! the second step. The server runs [`ServerRegistration::finish`] to produce a
234//! finalized [`ServerRegistration`]. At this point, the client can be
235//! considered as successfully registered, and the server can invoke
236//! [`ServerRegistration::serialize`] to store the password file for use during
237//! the login protocol.
238//! ```
239//! # use opaque_vx::{
240//! #   errors::ProtocolError,
241//! #   ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ServerSetup,
242//! #   ksf::Identity,
243//! # };
244//! # use opaque_vx::CipherSuite;
245//! # struct Default;
246//! # #[cfg(feature = "ristretto255")]
247//! # impl CipherSuite for Default {
248//! #     type OprfCs = opaque_vx::Ristretto255;
249//! #     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
250//! #     type Ksf = opaque_vx::ksf::Identity;
251//! # }
252//! # #[cfg(not(feature = "ristretto255"))]
253//! # impl CipherSuite for Default {
254//! #     type OprfCs = p256::NistP256;
255//! #     type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
256//! #     type Ksf = opaque_vx::ksf::Identity;
257//! # }
258//! # use rand::{rngs::SysRng, Rng};
259//! # use rand::rand_core::UnwrapErr;
260//! # let mut client_rng = UnwrapErr(SysRng);
261//! # let client_registration_start_result = ClientRegistration::<Default>::start(
262//! #     &mut client_rng,
263//! #     b"password",
264//! # )?;
265//! # let mut server_rng = UnwrapErr(SysRng);
266//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
267//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?;
268//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
269//! let password_file = ServerRegistration::<Default>::finish(
270//!     client_registration_finish_result.message,
271//! );
272//! # Ok::<(), ProtocolError>(())
273//! ```
274//!
275//! ## Login
276//! The login protocol between a client and server also consists of four steps
277//! along with three messages: [`CredentialRequest`], [`CredentialResponse`],
278//! [`CredentialFinalization`]. The server is expected to have access to the
279//! password file corresponding to an output of the registration phase (see
280//! [Dummy Server Login](#dummy-server-login) for handling the scenario where no
281//! password file is available). The login protocol will execute successfully
282//! only if the same password was used in the registration phase that produced
283//! the password file that the server is testing against.
284//!
285//! ### Client Login Start
286//! In the first step of login, the client chooses as input a login password.
287//! The client runs [`ClientLogin::start`] to produce an output consisting of a
288//! [`CredentialRequest`] to be sent to the server, and a [`ClientLogin`] which
289//! must be persisted on the client for the final step of client login.
290//! ```
291//! # use opaque_vx::{
292//! #   errors::ProtocolError,
293//! #   ClientRegistration, ServerRegistration, ServerLogin, CredentialFinalization,
294//! #   ksf::Identity,
295//! # };
296//! # use opaque_vx::CipherSuite;
297//! # struct Default;
298//! # #[cfg(feature = "ristretto255")]
299//! # impl CipherSuite for Default {
300//! #     type OprfCs = opaque_vx::Ristretto255;
301//! #     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
302//! #     type Ksf = opaque_vx::ksf::Identity;
303//! # }
304//! # #[cfg(not(feature = "ristretto255"))]
305//! # impl CipherSuite for Default {
306//! #     type OprfCs = p256::NistP256;
307//! #     type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
308//! #     type Ksf = opaque_vx::ksf::Identity;
309//! # }
310//! # use rand::{rngs::SysRng, Rng};
311//! # use rand::rand_core::UnwrapErr;
312//! use opaque_vx::ClientLogin;
313//!
314//! let mut client_rng = UnwrapErr(SysRng);
315//! let client_login_start_result = ClientLogin::<Default>::start(&mut client_rng, b"password")?;
316//! # Ok::<(), ProtocolError>(())
317//! ```
318//!
319//! ### Server Login Start
320//! In the second step of login, the server takes as input a persisted instance
321//! of [`ServerSetup`], the password file output from registration, a
322//! [`CredentialRequest`] from the client, and a server-side identifier for the
323//! client. The server runs [`ServerLogin::start`] to produce an output
324//! consisting of a [`CredentialResponse`] which is returned to the client, and
325//! a [`ServerLogin`] which must be persisted on the server for the final step
326//! of login.
327//! ```
328//! # use opaque_vx::{
329//! #   errors::ProtocolError,
330//! #   ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, CredentialFinalization, ServerSetup,
331//! #   ksf::Identity,
332//! # };
333//! # use opaque_vx::CipherSuite;
334//! # struct Default;
335//! # #[cfg(feature = "ristretto255")]
336//! # impl CipherSuite for Default {
337//! #     type OprfCs = opaque_vx::Ristretto255;
338//! #     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
339//! #     type Ksf = opaque_vx::ksf::Identity;
340//! # }
341//! # #[cfg(not(feature = "ristretto255"))]
342//! # impl CipherSuite for Default {
343//! #     type OprfCs = p256::NistP256;
344//! #     type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
345//! #     type Ksf = opaque_vx::ksf::Identity;
346//! # }
347//! # use rand::{rngs::SysRng, Rng};
348//! # use rand::rand_core::UnwrapErr;
349//! # let mut client_rng = UnwrapErr(SysRng);
350//! # let client_registration_start_result = ClientRegistration::<Default>::start(
351//! #     &mut client_rng,
352//! #     b"password",
353//! # )?;
354//! # let mut server_rng = UnwrapErr(SysRng);
355//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
356//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?;
357//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
358//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
359//! # let client_login_start_result = ClientLogin::<Default>::start(
360//! #   &mut client_rng,
361//! #   b"password",
362//! # )?;
363//! use opaque_vx::{ServerLogin, ServerLoginParameters};
364//!
365//! let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes)?;
366//! let mut server_rng = UnwrapErr(SysRng);
367//! let server_login_start_result = ServerLogin::start(
368//!     &mut server_rng,
369//!     &server_setup,
370//!     Some(password_file),
371//!     client_login_start_result.message,
372//!     b"alice@example.com",
373//!     ServerLoginParameters::default(),
374//! )?;
375//! # Ok::<(), ProtocolError>(())
376//! ```
377//! Note that if there is no corresponding password file found for the user, the
378//! server can use `None` in place of `Some(password_file)` in order to generate
379//! a [`CredentialResponse`] that is indistinguishable from a valid
380//! [`CredentialResponse`] returned for a registered client. This allows the
381//! server to prevent leaking information about whether or not a client has
382//! previously registered with the server.
383//!
384//! ### Client Login Finish
385//! In the third step of login, the client takes as input a
386//! [`CredentialResponse`] from the server and runs [`ClientLogin::finish`]
387//! on it.
388//! If the authentication is successful, then the client obtains a
389//! [`ClientLoginFinishResult`]. Otherwise, on failure, the
390//! algorithm outputs an
391//! [`InvalidLoginError`](errors::ProtocolError::InvalidLoginError) error.
392//!
393//! The resulting [`ClientLoginFinishResult`] obtained by client in this step
394//! contains, among other things, a [`CredentialFinalization`] to be sent to the
395//! server to complete the protocol, and a
396//! [`session_key`](struct.ClientLoginFinishResult.html#structfield.session_key)
397//! which will match the server's session key upon a successful login.
398//! ```
399//! # use opaque_vx::{
400//! #   errors::ProtocolError,
401//! #   ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ServerLogin, ServerLoginParameters, CredentialFinalization, ServerSetup,
402//! #   ksf::Identity,
403//! # };
404//! # use opaque_vx::CipherSuite;
405//! # struct Default;
406//! # #[cfg(feature = "ristretto255")]
407//! # impl CipherSuite for Default {
408//! #     type OprfCs = opaque_vx::Ristretto255;
409//! #     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
410//! #     type Ksf = opaque_vx::ksf::Identity;
411//! # }
412//! # #[cfg(not(feature = "ristretto255"))]
413//! # impl CipherSuite for Default {
414//! #     type OprfCs = p256::NistP256;
415//! #     type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
416//! #     type Ksf = opaque_vx::ksf::Identity;
417//! # }
418//! # use rand::{rngs::SysRng, Rng};
419//! # use rand::rand_core::UnwrapErr;
420//! # let mut client_rng = UnwrapErr(SysRng);
421//! # let client_registration_start_result = ClientRegistration::<Default>::start(
422//! #     &mut client_rng,
423//! #     b"password",
424//! # )?;
425//! # let mut server_rng = UnwrapErr(SysRng);
426//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
427//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?;
428//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
429//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
430//! # let client_login_start_result = ClientLogin::<Default>::start(
431//! #     &mut client_rng,
432//! #     b"password",
433//! # )?;
434//! # let password_file =
435//! #   ServerRegistration::<Default>::deserialize(
436//! #     &password_file_bytes,
437//! #   )?;
438//! # let server_login_start_result =
439//! #     ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"alice@example.com", ServerLoginParameters::default())?;
440//! use opaque_vx::ClientLoginFinishParameters;
441//!
442//! let client_login_finish_result = client_login_start_result.state.finish(
443//!     &mut client_rng,
444//!     b"password",
445//!     server_login_start_result.message,
446//!     ClientLoginFinishParameters::default(),
447//! )?;
448//! # Ok::<(), ProtocolError>(())
449//! ```
450//!
451//! ### Server Login Finish
452//! In the fourth step of login, the server takes as input a
453//! [`CredentialFinalization`] from the client and runs [`ServerLogin::finish`]
454//! to produce an output consisting of the `session_key` sequence of bytes which
455//! will match the client's session key upon a successful login.
456//! ```
457//! # use opaque_vx::{
458//! #   errors::ProtocolError,
459//! #   ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, ServerLogin, ServerLoginParameters, CredentialFinalization, ServerSetup,
460//! #   ksf::Identity,
461//! # };
462//! # use opaque_vx::CipherSuite;
463//! # struct Default;
464//! # #[cfg(feature = "ristretto255")]
465//! # impl CipherSuite for Default {
466//! #     type OprfCs = opaque_vx::Ristretto255;
467//! #     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
468//! #     type Ksf = opaque_vx::ksf::Identity;
469//! # }
470//! # #[cfg(not(feature = "ristretto255"))]
471//! # impl CipherSuite for Default {
472//! #     type OprfCs = p256::NistP256;
473//! #     type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
474//! #     type Ksf = opaque_vx::ksf::Identity;
475//! # }
476//! # use rand::{rngs::SysRng, Rng};
477//! # use rand::rand_core::UnwrapErr;
478//! # let mut client_rng = UnwrapErr(SysRng);
479//! # let client_registration_start_result = ClientRegistration::<Default>::start(
480//! #     &mut client_rng,
481//! #     b"password",
482//! # )?;
483//! # let mut server_rng = UnwrapErr(SysRng);
484//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
485//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?;
486//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
487//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
488//! # let client_login_start_result = ClientLogin::<Default>::start(
489//! #   &mut client_rng,
490//! #   b"password",
491//! # )?;
492//! # let password_file =
493//! #   ServerRegistration::<Default>::deserialize(
494//! #     &password_file_bytes,
495//! #   )?;
496//! # let server_login_start_result =
497//! #     ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"alice@example.com", ServerLoginParameters::default())?;
498//! # let client_login_finish_result = client_login_start_result.state.finish(
499//! #   &mut client_rng,
500//! #   b"password",
501//! #   server_login_start_result.message,
502//! #   ClientLoginFinishParameters::default(),
503//! # )?;
504//! let server_login_finish_result = server_login_start_result.state.finish(
505//!     client_login_finish_result.message,
506//!     ServerLoginParameters::default(),
507//! )?;
508//!
509//! assert_eq!(
510//!    client_login_finish_result.session_key,
511//!    server_login_finish_result.session_key,
512//! );
513//! # Ok::<(), ProtocolError>(())
514//! ```
515//! If the protocol completes successfully, then the server obtains a
516//! `server_login_finish_result.session_key` which is guaranteed to match
517//! `client_login_finish_result.session_key` (see the [Session
518//! Key](#session-key) section). Otherwise, on failure, the
519//! [`ServerLogin::finish`] algorithm outputs the error
520//! [`InvalidLoginError`](errors::ProtocolError::InvalidLoginError).
521//!
522//! # Advanced Usage
523//!
524//! This implementation offers support for several optional features of OPAQUE,
525//! described below. They are not critical to the execution of the main
526//! protocol, but can provide additional security benefits which can be suitable
527//! for various applications that rely on OPAQUE for authentication.
528//!
529//! ## Session Key
530//!
531//! Upon a successful completion of the OPAQUE protocol (the client runs login
532//! with the same password used during registration), the client and server have
533//! access to a session key, which is a pseudorandomly distributed byte
534//! string (of length equal to the output size of [`voprf::CipherSuite::Hash`])
535//! which only the client and server know. Multiple login runs using the
536//! same password for the same client will produce different session keys,
537//! distributed as uniformly random strings. Thus, the session key can be used
538//! to establish a secure channel between the client and server.
539//!
540//! The session key can be accessed from the `session_key` field of
541//! [`ClientLoginFinishResult`] and [`ServerLoginFinishResult`]. See the
542//! combination of [Client Login Finish](#client-login-finish) and [Server Login
543//! Finish](#server-login-finish) for example usage.
544//!
545//! ## Checking Server Consistency
546//!
547//! A [`ClientLoginFinishResult`] contains the `server_s_pk` field, which is
548//! represents the static public key of the server that is established during
549//! the setup phase. This can be used by the client to verify the authenticity
550//! of the server it engages with during the login phase. In particular, the
551//! client can check that the static public key of the server supplied during
552//! registration (with the `server_s_pk` field of
553//! [`ClientRegistrationFinishResult`]) matches this field during login.
554//! ```
555//! # use opaque_vx::{
556//! #   errors::ProtocolError,
557//! #   ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, ServerLogin, ServerLoginParameters, CredentialFinalization, ServerSetup,
558//! #   ksf::Identity,
559//! # };
560//! # use opaque_vx::CipherSuite;
561//! # struct Default;
562//! # #[cfg(feature = "ristretto255")]
563//! # impl CipherSuite for Default {
564//! #     type OprfCs = opaque_vx::Ristretto255;
565//! #     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
566//! #     type Ksf = opaque_vx::ksf::Identity;
567//! # }
568//! # #[cfg(not(feature = "ristretto255"))]
569//! # impl CipherSuite for Default {
570//! #     type OprfCs = p256::NistP256;
571//! #     type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
572//! #     type Ksf = opaque_vx::ksf::Identity;
573//! # }
574//! # use rand::{rngs::SysRng, Rng};
575//! # use rand::rand_core::UnwrapErr;
576//! # let mut client_rng = UnwrapErr(SysRng);
577//! # let client_registration_start_result = ClientRegistration::<Default>::start(
578//! #     &mut client_rng,
579//! #     b"password",
580//! # )?;
581//! # let mut server_rng = UnwrapErr(SysRng);
582//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
583//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?;
584//! // During registration, the client obtains a ClientRegistrationFinishResult with
585//! // a server_s_pk field
586//! let client_registration_finish_result = client_registration_start_result.state.finish(
587//!     &mut client_rng,
588//!     b"password",
589//!     server_registration_start_result.message,
590//!     ClientRegistrationFinishParameters::default(),
591//! )?;
592//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
593//! # let client_login_start_result = ClientLogin::<Default>::start(
594//! #     &mut client_rng,
595//! #     b"password",
596//! # )?;
597//! # let password_file =
598//! #   ServerRegistration::<Default>::deserialize(
599//! #     &password_file_bytes,
600//! #   )?;
601//! # let server_login_start_result =
602//! #     ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"alice@example.com", ServerLoginParameters::default())?;
603//!
604//! // And then later, during login...
605//! let client_login_finish_result = client_login_start_result.state.finish(
606//!     &mut client_rng,
607//!     b"password",
608//!     server_login_start_result.message,
609//!     ClientLoginFinishParameters::default(),
610//! )?;
611//!
612//! // Check that the server's static public key obtained from login matches what
613//! // was obtained during registration
614//! assert_eq!(
615//!     &client_registration_finish_result.server_s_pk,
616//!     &client_login_finish_result.server_s_pk,
617//! );
618//! # Ok::<(), ProtocolError>(())
619//! ```
620//!
621//! Note that without this check over the consistency of the server's static
622//! public key, a malicious actor could impersonate the registration server if
623//! it were able to copy the password file output during registration!
624//! Therefore, it is recommended to perform the following check in the
625//! application layer if the client can obtain a copy of the server's static
626//! public key beforehand.
627//!
628//!
629//! ## Export Key
630//!
631//! The export key is a pseudorandomly distributed byte string
632//! (of length equal to the output size of [`voprf::CipherSuite::Hash`]) output
633//! by both the [Client Registration Finish](#client-registration-finish) and
634//! [Client Login Finish](#client-login-finish) steps. The same export key
635//! string will be output by both functions only if the exact same password is
636//! passed to [`ClientRegistration::start`] and [`ClientLogin::start`].
637//!
638//! The export key retains as much secrecy as the password itself, and is
639//! similarly derived through an evaluation of the key stretching function.
640//! Hence, only the parties which know the password the client uses during
641//! registration and login can recover this secret, as it is never exposed to
642//! the server. As a result, the export key can be used (separately from the
643//! OPAQUE protocol) to provide confidentiality and integrity to other data
644//! which only the client should be able to process. For instance, if the server
645//! is expected to maintain any client-side secrets which require a password to
646//! access, then this export key can be used to encrypt these secrets so that
647//! they remain hidden from the server (see [examples/digital_locker.rs](https://github.com/facebook/opaque-ke/blob/main/examples/digital_locker.rs)
648//! for a working example).
649//!
650//! You can access the export key from the `export_key` field of
651//! [`ClientRegistrationFinishResult`] and [`ClientLoginFinishResult`].
652//! ```
653//! # use opaque_vx::{
654//! #   errors::ProtocolError,
655//! #   ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, ServerLogin, ServerLoginParameters, CredentialFinalization, ServerSetup,
656//! #   ksf::Identity,
657//! # };
658//! # use opaque_vx::CipherSuite;
659//! # struct Default;
660//! # #[cfg(feature = "ristretto255")]
661//! # impl CipherSuite for Default {
662//! #     type OprfCs = opaque_vx::Ristretto255;
663//! #     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
664//! #     type Ksf = opaque_vx::ksf::Identity;
665//! # }
666//! # #[cfg(not(feature = "ristretto255"))]
667//! # impl CipherSuite for Default {
668//! #     type OprfCs = p256::NistP256;
669//! #     type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
670//! #     type Ksf = opaque_vx::ksf::Identity;
671//! # }
672//! # use rand::{rngs::SysRng, Rng};
673//! # use rand::rand_core::UnwrapErr;
674//! # let mut client_rng = UnwrapErr(SysRng);
675//! # let client_registration_start_result = ClientRegistration::<Default>::start(
676//! #     &mut client_rng,
677//! #     b"password",
678//! # )?;
679//! # let mut server_rng = UnwrapErr(SysRng);
680//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
681//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?;
682//! // During registration...
683//! let client_registration_finish_result = client_registration_start_result.state.finish(
684//!     &mut client_rng,
685//!     b"password",
686//!     server_registration_start_result.message,
687//!     ClientRegistrationFinishParameters::default()
688//! )?;
689//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
690//! # let client_login_start_result = ClientLogin::<Default>::start(
691//! #     &mut client_rng,
692//! #     b"password",
693//! # )?;
694//! # let password_file =
695//! #   ServerRegistration::<Default>::deserialize(
696//! #     &password_file_bytes,
697//! #   )?;
698//! # let server_login_start_result =
699//! #     ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"alice@example.com", ServerLoginParameters::default())?;
700//!
701//! // And then later, during login...
702//! let client_login_finish_result = client_login_start_result.state.finish(
703//!     &mut client_rng,
704//!     b"password",
705//!     server_login_start_result.message,
706//!     ClientLoginFinishParameters::default(),
707//! )?;
708//!
709//! assert_eq!(
710//!     client_registration_finish_result.export_key,
711//!     client_login_finish_result.export_key,
712//! );
713//! # Ok::<(), ProtocolError>(())
714//! ```
715//!
716//! ## `TripleDhKem` Key Exchange
717//!
718//! `TripleDhKem` extends the default [`TripleDh`] handshake by mixing a
719//! KEM shared secret into the transcript alongside the three Diffie-Hellman
720//! products. This hybrid exchange offers a post-quantum (PQ) upgrade path while
721//! preserving backwards-compatible session keys and transcript bindings.
722//!
723//! Note that this key exchange does not provide full PQ security
724//! for OPAQUE, as the OPRF used in the protocol is still not PQ-secure.
725//! Morever, we only use ephemeral KEM keys to provide confidentiality,
726//! implicitly delegating the authentication guarantees to the static (and
727//! classical) Diffie-Hellman keys. This is intended as a stopgap measure to
728//! provide some level of confidentiality against passive quantum attackers (as
729//! opposed to active ones). See [RFC 9807, Appendix B](https://www.rfc-editor.org/rfc/rfc9807.html#appendix-B)
730//! for a more detailed discussion of the security guarantees.
731//!
732//! This can be enabled with the `kem` feature to compile the integration with
733//! the [`ml-kem`](https://docs.rs/ml-kem/latest/ml_kem/) crate and instantiating
734//! the ciphersuite as follows:
735//!
736//! ```ignore
737//! use opaque_vx::CipherSuite;
738//!
739//! struct KemSuite;
740//!
741//! impl CipherSuite for KemSuite {
742//!     type OprfCs = opaque_vx::Ristretto255;
743//!     type KeyExchange = opaque_vx::TripleDhKem<opaque_vx::Ristretto255, sha2::Sha512, opaque_vx::ml_kem::MlKem768>;
744//!     type Ksf = opaque_vx::ksf::Identity;
745//! }
746//! ```
747//!
748//! ## Custom Identifiers
749//!
750//! Typically, when applications use OPAQUE to authenticate a client to a server,
751//! the client has a registered username which is sent to the server to identify
752//! the corresponding password file established during registration. This
753//! username may or may not coincide with the server-side identifier; however,
754//! this username must be known to both the client and the server (whereas the
755//! server-side identifier does not need to be exposed to the client). The
756//! server may also have an identifier corresponding to an entity (e.g.
757//! Facebook). By default, neither of these public identifiers need to be
758//! supplied to the OPAQUE protocol.
759//!
760//! But, for applications that wish to cryptographically bind these identities
761//! to the registered password file as well as the session key output by the
762//! login phase, these custom identifiers can be specified through
763//! [`ClientRegistrationFinishParameters`] in [Client Registration
764//! Finish](#client-registration-finish):
765//! ```
766//! # use opaque_vx::{
767//! #   errors::ProtocolError,
768//! #   ClientRegistration, ClientRegistrationFinishParameters, Identifiers, ServerRegistration, ServerSetup,
769//! #   ksf::Identity,
770//! # };
771//! # use opaque_vx::CipherSuite;
772//! # struct Default;
773//! # #[cfg(feature = "ristretto255")]
774//! # impl CipherSuite for Default {
775//! #     type OprfCs = opaque_vx::Ristretto255;
776//! #     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
777//! #     type Ksf = opaque_vx::ksf::Identity;
778//! # }
779//! # #[cfg(not(feature = "ristretto255"))]
780//! # impl CipherSuite for Default {
781//! #     type OprfCs = p256::NistP256;
782//! #     type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
783//! #     type Ksf = opaque_vx::ksf::Identity;
784//! # }
785//! # use rand::{rngs::SysRng, Rng};
786//! # use rand::rand_core::UnwrapErr;
787//! # let mut client_rng = UnwrapErr(SysRng);
788//! # let client_registration_start_result = ClientRegistration::<Default>::start(
789//! #     &mut client_rng,
790//! #     b"password",
791//! # )?;
792//! # let mut server_rng = UnwrapErr(SysRng);
793//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
794//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?;
795//! let client_registration_finish_result = client_registration_start_result.state.finish(
796//!     &mut client_rng,
797//!     b"password",
798//!     server_registration_start_result.message,
799//!     ClientRegistrationFinishParameters::new(
800//!         Identifiers {
801//!             client: Some(b"Alice_the_Cryptographer"),
802//!             server: Some(b"Facebook"),
803//!         },
804//!         None,
805//!     ),
806//! )?;
807//! # Ok::<(), ProtocolError>(())
808//! ```
809//!
810//! The same identifiers must also be supplied using [`ServerLoginParameters`]
811//! in [Server Login Start](#server-login-start):
812//! ```
813//! # use opaque_vx::{
814//! #   errors::ProtocolError,
815//! #   ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, CredentialFinalization, Identifiers, ServerSetup,
816//! #   ksf::Identity,
817//! # };
818//! # use opaque_vx::CipherSuite;
819//! # struct Default;
820//! # #[cfg(feature = "ristretto255")]
821//! # impl CipherSuite for Default {
822//! #     type OprfCs = opaque_vx::Ristretto255;
823//! #     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
824//! #     type Ksf = opaque_vx::ksf::Identity;
825//! # }
826//! # #[cfg(not(feature = "ristretto255"))]
827//! # impl CipherSuite for Default {
828//! #     type OprfCs = p256::NistP256;
829//! #     type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
830//! #     type Ksf = opaque_vx::ksf::Identity;
831//! # }
832//! # use rand::{rngs::SysRng, Rng};
833//! # use rand::rand_core::UnwrapErr;
834//! # let mut client_rng = UnwrapErr(SysRng);
835//! # let client_registration_start_result = ClientRegistration::<Default>::start(
836//! #     &mut client_rng,
837//! #     b"password",
838//! # )?;
839//! # let mut server_rng = UnwrapErr(SysRng);
840//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
841//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?;
842//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::new(Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") }, None))?;
843//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
844//! # let client_login_start_result = ClientLogin::<Default>::start(
845//! #   &mut client_rng,
846//! #   b"password",
847//! # )?;
848//! # use opaque_vx::{ServerLogin, ServerLoginParameters};
849//! # let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes)?;
850//! # let mut server_rng = UnwrapErr(SysRng);
851//! let server_login_start_result = ServerLogin::start(
852//!     &mut server_rng,
853//!     &server_setup,
854//!     Some(password_file),
855//!     client_login_start_result.message,
856//!     b"alice@example.com",
857//!     ServerLoginParameters {
858//!         context: None,
859//!         identifiers: Identifiers {
860//!             client: Some(b"Alice_the_Cryptographer"),
861//!             server: Some(b"Facebook"),
862//!         },
863//!     },
864//! )?;
865//! # Ok::<(), ProtocolError>(())
866//! ```
867//!
868//! as well as [`ClientLoginFinishParameters`] in [Client Login
869//! Finish](#client-login-finish):
870//! ```
871//! # use opaque_vx::{
872//! #   errors::ProtocolError,
873//! #   ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, Identifiers, ServerLogin, ServerLoginParameters, CredentialFinalization, ServerSetup,
874//! #   ksf::Identity,
875//! # };
876//! # use opaque_vx::CipherSuite;
877//! # struct Default;
878//! # #[cfg(feature = "ristretto255")]
879//! # impl CipherSuite for Default {
880//! #     type OprfCs = opaque_vx::Ristretto255;
881//! #     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
882//! #     type Ksf = opaque_vx::ksf::Identity;
883//! # }
884//! # #[cfg(not(feature = "ristretto255"))]
885//! # impl CipherSuite for Default {
886//! #     type OprfCs = p256::NistP256;
887//! #     type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
888//! #     type Ksf = opaque_vx::ksf::Identity;
889//! # }
890//! # use rand::{rngs::SysRng, Rng};
891//! # use rand::rand_core::UnwrapErr;
892//! # let mut client_rng = UnwrapErr(SysRng);
893//! # let client_registration_start_result = ClientRegistration::<Default>::start(
894//! #     &mut client_rng,
895//! #     b"password",
896//! # )?;
897//! # let mut server_rng = UnwrapErr(SysRng);
898//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
899//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?;
900//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::new(Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") }, None))?;
901//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
902//! # let client_login_start_result = ClientLogin::<Default>::start(
903//! #     &mut client_rng,
904//! #     b"password",
905//! # )?;
906//! # let password_file =
907//! #   ServerRegistration::<Default>::deserialize(
908//! #     &password_file_bytes,
909//! #   )?;
910//! # let server_login_start_result =
911//! #     ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"alice@example.com", ServerLoginParameters { context: None, identifiers: Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") } })?;
912//! let client_login_finish_result = client_login_start_result.state.finish(
913//!     &mut client_rng,
914//!     b"password",
915//!     server_login_start_result.message,
916//!     ClientLoginFinishParameters::new(
917//!         None,
918//!         Identifiers {
919//!             client: Some(b"Alice_the_Cryptographer"),
920//!             server: Some(b"Facebook"),
921//!         },
922//!         None,
923//!     ),
924//! )?;
925//!
926//! # Ok::<(), ProtocolError>(())
927//! ```
928//! and in [`ServerLoginParameters`] in [Server Login
929//! Finish](#server-login-finish):
930//! ```
931//! # use opaque_vx::{
932//! #   errors::ProtocolError,
933//! #   ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, Identifiers, ServerLogin, ServerLoginParameters, CredentialFinalization, ServerSetup,
934//! #   ksf::Identity,
935//! # };
936//! # use opaque_vx::CipherSuite;
937//! # struct Default;
938//! # #[cfg(feature = "ristretto255")]
939//! # impl CipherSuite for Default {
940//! #     type OprfCs = opaque_vx::Ristretto255;
941//! #     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
942//! #     type Ksf = opaque_vx::ksf::Identity;
943//! # }
944//! # #[cfg(not(feature = "ristretto255"))]
945//! # impl CipherSuite for Default {
946//! #     type OprfCs = p256::NistP256;
947//! #     type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
948//! #     type Ksf = opaque_vx::ksf::Identity;
949//! # }
950//! # use rand::{rngs::SysRng, Rng};
951//! # use rand::rand_core::UnwrapErr;
952//! # let mut client_rng = UnwrapErr(SysRng);
953//! # let client_registration_start_result = ClientRegistration::<Default>::start(
954//! #     &mut client_rng,
955//! #     b"password",
956//! # )?;
957//! # let mut server_rng = UnwrapErr(SysRng);
958//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
959//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?;
960//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::new(Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") }, None))?;
961//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
962//! # let client_login_start_result = ClientLogin::<Default>::start(
963//! #     &mut client_rng,
964//! #     b"password",
965//! # )?;
966//! # let password_file =
967//! #   ServerRegistration::<Default>::deserialize(
968//! #     &password_file_bytes,
969//! #   )?;
970//! # let server_login_start_result =
971//! #     ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"alice@example.com", ServerLoginParameters { context: None, identifiers: Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") } })?;
972//! # let client_login_finish_result = client_login_start_result.state.finish(
973//! #   &mut client_rng,
974//! #   b"password",
975//! #   server_login_start_result.message,
976//! #   ClientLoginFinishParameters::new(None, Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") }, None),
977//! # )?;
978//! let server_login_finish_result = server_login_start_result.state.finish(
979//!     client_login_finish_result.message,
980//!     ServerLoginParameters { context: None, identifiers: Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") } },
981//! )?;
982//!
983//! # Ok::<(), ProtocolError>(())
984//! ```
985//! Failing to supply the same pair of custom identifiers in any of the three
986//! steps above will result in an error in attempting to complete the protocol!
987//!
988//! Note that if only one of the client and server identifiers are present, then
989//! [Identifiers] can be used to specify them individually.
990//!
991//! ## Key Exchange Context
992//!
993//! A key exchange protocol typically allows for the specifying of shared
994//! "context" information between the two parties before the exchange is
995//! complete, to bind the integrity of application-specific data or
996//! configuration parameters to the security of the key exchange. During the
997//! login phase, the client and server can specify this context using:
998//! - In [Server Login Start](#server-login-start), where the server can
999//!   populate [`ServerLoginParameters::context`].
1000//! - In [Client Login Finish](#client-login-finish), where the client can
1001//!   populate [`ClientLoginFinishParameters::context`].
1002//! - In [Server Login Finish](#server-login-finish), where the server can
1003//!   populate [`ServerLoginParameters::context`].
1004//!
1005//! ## Dummy Server Login
1006//!
1007//! For applications in which the server does not wish to reveal to the client
1008//! whether an existing password file has been registered, the server can return
1009//! a "dummy" credential response message to the client for an unregistered
1010//! client, which is indistinguishable from the normal credential response
1011//! message that the server would return for a registered client. The dummy
1012//! message is created by passing a `None` to the `password_file` parameter for
1013//! [`ServerLogin::start`].
1014//!
1015//! ## Remote Private Keys
1016//!
1017//! Servers that want to store their private key in an external location (e.g.
1018//! in an HSM or vault) can do so with [`ServerLogin::builder()`] without
1019//! exposing the bytes of the private key to this library.
1020//! ```
1021//! # use generic_array::{GenericArray, typenum::U0};
1022//! # use opaque_vx::{CipherSuite, ClientLogin, ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, keypair::{PrivateKey, PublicKey}, key_exchange::{KeyExchange, group::Group, tripledh::DiffieHellman}};
1023//! # use rand::rngs::SysRng;
1024//! # use rand::rand_core::UnwrapErr;
1025//!
1026//! type Ristretto255 = <<Default as CipherSuite>::KeyExchange as KeyExchange>::Group;
1027//! # struct Default;
1028//! # #[cfg(feature = "ristretto255")]
1029//! # impl CipherSuite for Default {
1030//! #     type OprfCs = opaque_vx::Ristretto255;
1031//! #     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
1032//! #     type Ksf = opaque_vx::ksf::Identity;
1033//! # }
1034//! # #[cfg(not(feature = "ristretto255"))]
1035//! # impl CipherSuite for Default {
1036//! #     type OprfCs = p256::NistP256;
1037//! #     type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
1038//! #     type Ksf = opaque_vx::ksf::Identity;
1039//! # }
1040//! # #[derive(Debug, thiserror::Error)]
1041//! # #[error("test error")]
1042//! # struct YourRemoteKeyError;
1043//! # #[derive(Clone)]
1044//! # struct YourRemoteKey(<Ristretto255 as Group>::Sk);
1045//! # impl YourRemoteKey {
1046//! #     fn diffie_hellman(&self, pk: &PublicKey<Ristretto255>) -> Result<GenericArray<u8, <Ristretto255 as Group>::PkLen>, YourRemoteKeyError> {
1047//! #         Ok(<<Ristretto255 as Group>::Sk as DiffieHellman<Ristretto255>>::diffie_hellman(&self.0, pk.to_group_type()))
1048//! #     }
1049//! # }
1050//! use opaque_vx::{ServerLogin, ServerLoginParameters, ServerSetup};
1051//! use opaque_vx::keypair::{KeyPair, PrivateKeySerialization};
1052//! use opaque_vx::errors::ProtocolError;
1053//!
1054//! // Implement if you intend to use `ServerSetup::de/serialize` instead of `serde`.
1055//! impl PrivateKeySerialization<Ristretto255> for YourRemoteKey {
1056//!     type Error = YourRemoteKeyError;
1057//!     type Len = U0;
1058//!
1059//!     fn serialize_key_pair(_: &KeyPair<Ristretto255, Self>) -> GenericArray<u8, Self::Len> {
1060//!         unimplemented!()
1061//!     }
1062//!
1063//!     fn deserialize_take_key_pair(input: &mut &[u8]) -> Result<KeyPair<Ristretto255, Self>, ProtocolError<Self::Error>> {
1064//!         unimplemented!()
1065//!     }
1066//! }
1067//!
1068//! # let sk = Ristretto255::random_sk(&mut UnwrapErr(SysRng));
1069//! # let pk = Ristretto255::public_key(&sk);
1070//! # let pk = Ristretto255::serialize_pk(&pk);
1071//! # let public_key = PublicKey::deserialize(&pk).unwrap();
1072//! # let remote_key = YourRemoteKey(sk);
1073//! # let mut server_rng = UnwrapErr(SysRng);
1074//! let keypair = KeyPair::new(remote_key, public_key);
1075//! let server_setup = ServerSetup::<Default, YourRemoteKey>::new_with_key_pair(&mut server_rng, keypair);
1076//!
1077//! # let client_registration_start_result = ClientRegistration::<Default>::start(
1078//! #     &mut UnwrapErr(SysRng),
1079//! #     b"password",
1080//! # )?;
1081//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"alice@example.com")?;
1082//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut UnwrapErr(SysRng), b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
1083//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
1084//! # let client_login_start_result = ClientLogin::<Default>::start(
1085//! #   &mut UnwrapErr(SysRng),
1086//! #   b"password",
1087//! # )?;
1088//! # let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes)?;
1089//! // Use `ServerLogin::builder()` instead of `ServerLogin::start()`.
1090//! let server_login_builder = ServerLogin::builder(
1091//!     &mut server_rng,
1092//!     &server_setup,
1093//!     Some(password_file),
1094//!     client_login_start_result.message,
1095//!     b"alice@example.com",
1096//!     ServerLoginParameters::default(),
1097//! )?;
1098//!
1099//! // Run Diffie-Hellman on your remote key.
1100//! let client_e_public_key = server_login_builder.data();
1101//! let shared_secret = server_login_builder.private_key().diffie_hellman(&client_e_public_key)?;
1102//!
1103//! // Use the shared secret to build `ServerLogin`.
1104//! let server_login_start_result = server_login_builder.build(shared_secret)?;
1105//! # Ok::<(), anyhow::Error>(())
1106//! ```
1107//!
1108//! ## Remote OPRF Seeds
1109//!
1110//! In addition, the OPRF seed can be stored in an external location as well, by
1111//! using [`ServerRegistration::start_with_key_material()`] and
1112//! [`ServerLogin::builder_with_key_material()`] in combination with
1113//! [`ServerSetup::key_material_info()`].
1114//! ```
1115//! # use digest::Output;
1116//! # use generic_array::{GenericArray, typenum::U0};
1117//! # use hkdf::Hkdf;
1118//! # use opaque_vx::{CipherSuite, ClientLogin, ClientRegistration, ClientRegistrationFinishParameters, keypair::{PrivateKey, PublicKey}, key_exchange::{KeyExchange, group::Group, tripledh::DiffieHellman}};
1119//! # use rand::rngs::SysRng;
1120//! # use rand::Rng;
1121//! # use rand::rand_core::UnwrapErr;
1122//!
1123//! type Ristretto255 = <<Default as CipherSuite>::KeyExchange as KeyExchange>::Group;
1124//! # type Hash = <<Default as CipherSuite>::KeyExchange as KeyExchange>::Hash;
1125//! # type OprfGroup = <<Default as CipherSuite>::OprfCs as voprf::CipherSuite>::Group;
1126//! # struct Default;
1127//! # #[cfg(feature = "ristretto255")]
1128//! # impl CipherSuite for Default {
1129//! #     type OprfCs = opaque_vx::Ristretto255;
1130//! #     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
1131//! #     type Ksf = opaque_vx::ksf::Identity;
1132//! # }
1133//! # #[cfg(not(feature = "ristretto255"))]
1134//! # impl CipherSuite for Default {
1135//! #     type OprfCs = p256::NistP256;
1136//! #     type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
1137//! #     type Ksf = opaque_vx::ksf::Identity;
1138//! # }
1139//! # #[derive(Debug, thiserror::Error)]
1140//! # #[error("test error")]
1141//! # struct YourRemoteSecretsError;
1142//! # #[derive(Clone)]
1143//! # struct YourRemoteSeed(Output<Hash>);
1144//! # impl YourRemoteSeed {
1145//! #     fn hkdf(&self, info: &[&[u8]]) -> GenericArray<u8, <OprfGroup as voprf::Group>::ScalarLen> {
1146//! #         let mut ikm = GenericArray::default();
1147//! #         Hkdf::<Hash>::from_prk(&self.0)
1148//! #             .unwrap()
1149//! #             .expand_multi_info(info, &mut ikm)
1150//! #             .unwrap();
1151//! #         ikm
1152//! #     }
1153//! # }
1154//! # #[derive(Clone)]
1155//! # struct YourRemoteKey(<Ristretto255 as Group>::Sk);
1156//! # impl YourRemoteKey {
1157//! #     fn diffie_hellman(&self, pk: &PublicKey<Ristretto255>) -> Result<GenericArray<u8, <Ristretto255 as Group>::PkLen>, YourRemoteSecretsError> {
1158//! #         Ok(<<Ristretto255 as Group>::Sk as DiffieHellman<Ristretto255>>::diffie_hellman(&self.0, pk.to_group_type()))
1159//! #     }
1160//! # }
1161//! use opaque_vx::{ServerLogin, ServerLoginParameters, ServerRegistration, ServerSetup};
1162//! use opaque_vx::keypair::{KeyPair, OprfSeedSerialization};
1163//! use opaque_vx::errors::ProtocolError;
1164//!
1165//! // Implement if you intend to use `ServerSetup::de/serialize` instead of `serde`.
1166//! impl OprfSeedSerialization<sha2::Sha512, YourRemoteSecretsError> for YourRemoteSeed {
1167//!     type Len = U0;
1168//!
1169//!     fn serialize(&self) -> GenericArray<u8, Self::Len> {
1170//!         unimplemented!()
1171//!     }
1172//!
1173//!     fn deserialize_take(input: &mut &[u8]) -> Result<YourRemoteSeed, ProtocolError<YourRemoteSecretsError>> {
1174//!         unimplemented!()
1175//!     }
1176//! }
1177//!
1178//! # let mut oprf_seed = YourRemoteSeed(GenericArray::default().into_ha0_4());
1179//! # UnwrapErr(SysRng).fill_bytes(&mut oprf_seed.0);
1180//! # let sk = Ristretto255::random_sk(&mut UnwrapErr(SysRng));
1181//! # let pk = Ristretto255::public_key(&sk);
1182//! # let pk = Ristretto255::serialize_pk(&pk);
1183//! # let public_key = PublicKey::deserialize(&pk).unwrap();
1184//! # let remote_key = YourRemoteKey(sk);
1185//! # let mut server_rng = UnwrapErr(SysRng);
1186//! let keypair = KeyPair::new(remote_key, public_key);
1187//! let server_setup = ServerSetup::<Default, YourRemoteKey, YourRemoteSeed>::new_with_key_pair_and_seed(&mut server_rng, keypair, oprf_seed);
1188//!
1189//! // Incoming registration ...
1190//! # let client_registration_start_result = ClientRegistration::<Default>::start(
1191//! #     &mut UnwrapErr(SysRng),
1192//! #     b"password",
1193//! # )?;
1194//!
1195//! // Run HKDF on your remote OPRF seed.
1196//! let info = server_setup.key_material_info(b"alice@example.com");
1197//! let key_material = info.ikm.hkdf(&info.info);
1198//!
1199//! // Use `ServerRegistration::start_with_key_material()` instead of `ServerRegistration::start()`.
1200//! let server_registration_start_result = ServerRegistration::<Default>::start_with_key_material(
1201//!     &server_setup,
1202//!     key_material,
1203//!     client_registration_start_result.message,
1204//! )?;
1205//!
1206//! // Finish registration ...
1207//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut UnwrapErr(SysRng), b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
1208//!
1209//! // Incoming login ...
1210//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
1211//! # let client_login_start_result = ClientLogin::<Default>::start(
1212//! #   &mut UnwrapErr(SysRng),
1213//! #   b"password",
1214//! # )?;
1215//! # let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes)?;
1216//!
1217//! // Run HKDF on your remote OPRF seed.
1218//! let info = server_setup.key_material_info(b"alice@example.com");
1219//! let key_material = info.ikm.hkdf(&info.info);
1220//!
1221//! // Use `ServerLogin::builder_with_key_material()` instead of `ServerLogin::start()`.
1222//! let server_login_builder = ServerLogin::builder_with_key_material(
1223//!     &mut server_rng,
1224//!     &server_setup,
1225//!     key_material,
1226//!     Some(password_file),
1227//!     client_login_start_result.message,
1228//!     ServerLoginParameters::default(),
1229//! )?;
1230//!
1231//! // Run Diffie-Hellman on your remote key.
1232//! let client_e_public_key = server_login_builder.data();
1233//! let shared_secret = server_login_builder.private_key().diffie_hellman(&client_e_public_key)?;
1234//!
1235//! // Use the shared secret to build `ServerLogin`.
1236//! let server_login_start_result = server_login_builder.build(shared_secret)?;
1237//! # Ok::<(), anyhow::Error>(())
1238//! ```
1239//!
1240//! ## Custom KSF and Parameters
1241//!
1242//! An application might want to use a custom KSF (Key Stretching Function)
1243//! that's not supported directly by this crate. The maintainer of the said KSF
1244//! or of the application itself can implement the [`Ksf`](ksf::Ksf) trait to
1245//! use it with `opaque-ke`. `scrypt` is used for this example, but any KSF
1246//! can be used.
1247//! ```
1248//! # use generic_array::GenericArray;
1249//! use opaque_vx::ksf::Ksf;
1250//!
1251//! #[derive(Default)]
1252//! struct CustomKsf(scrypt::Params);
1253//!
1254//! // The Ksf trait must be implemented to be used in the ciphersuite.
1255//! impl Ksf for CustomKsf {
1256//!     fn hash<L: generic_array::ArrayLength>(
1257//!         &self,
1258//!         input: GenericArray<u8, L>,
1259//!     ) -> Result<GenericArray<u8, L>, opaque_vx::errors::InternalError> {
1260//!         let mut output = GenericArray::<u8, L>::default();
1261//!         scrypt::scrypt(&input, &[], &self.0, &mut output)
1262//!             .map_err(|_| opaque_vx::errors::InternalError::KsfError)?;
1263//!
1264//!         Ok(output)
1265//!     }
1266//! }
1267//! ```
1268//!
1269//! It is also possible to override the default derivation parameters that are
1270//! used by the KSF during registration and login. This can be especially
1271//! helpful if the `Ksf` trait is already implemented.
1272//! ```
1273//! # use opaque_vx::CipherSuite;
1274//! # use opaque_vx::ClientRegistration;
1275//! # use opaque_vx::ClientRegistrationFinishParameters;
1276//! # use opaque_vx::ServerSetup;
1277//! # use opaque_vx::errors::ProtocolError;
1278//! # use rand::rngs::SysRng;
1279//! # use rand::Rng;
1280//! # use rand::rand_core::UnwrapErr;
1281//! # use std::default::Default;
1282//! # #[cfg(feature = "argon2")]
1283//! # {
1284//! # struct DefaultCipherSuite;
1285//! # #[cfg(feature = "ristretto255")]
1286//! # impl CipherSuite for DefaultCipherSuite {
1287//! #     type OprfCs = opaque_vx::Ristretto255;
1288//! #     type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
1289//! #     type Ksf = argon2::Argon2<'static>;
1290//! # }
1291//! # #[cfg(not(feature = "ristretto255"))]
1292//! # impl CipherSuite for DefaultCipherSuite {
1293//! #     type OprfCs = p256::NistP256;
1294//! #     type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
1295//! #     type Ksf = argon2::Argon2<'static>;
1296//! # }
1297//! #
1298//! # let password = b"password";
1299//! # let mut rng = UnwrapErr(SysRng);
1300//! # let server_setup = ServerSetup::<DefaultCipherSuite>::new(&mut rng);
1301//! # let mut client_rng = UnwrapErr(SysRng);
1302//! # let client_registration_start_result =
1303//! #     ClientRegistration::<DefaultCipherSuite>::start(&mut client_rng, password)?;
1304//! # use opaque_vx::ServerRegistration;
1305//! # let server_registration_start_result = ServerRegistration::<DefaultCipherSuite>::start(
1306//! #     &server_setup,
1307//! #     client_registration_start_result.message,
1308//! #     b"alice@example.com",
1309//! # )?;
1310//! #
1311//! // Create an Argon2 instance with the specified parameters
1312//! let argon2_params = argon2::Params::new(131072, 2, 4, None).unwrap();
1313//! let argon2_params = argon2::Argon2::new(
1314//!     argon2::Algorithm::Argon2id,
1315//!     argon2::Version::V0x13,
1316//!     argon2_params,
1317//! );
1318//!
1319//! // Override the default parameters with the custom ones
1320//! let hash_params = ClientRegistrationFinishParameters {
1321//!     ksf: Some(&argon2_params),
1322//!     ..Default::default()
1323//! };
1324//!
1325//! let client_registration_finish_result = client_registration_start_result
1326//!     .state
1327//!     .finish(
1328//!         &mut rng,
1329//!         password,
1330//!         server_registration_start_result.message,
1331//!         hash_params,
1332//!     )
1333//!     .unwrap();
1334//! # }
1335//! # Ok::<(), ProtocolError>(())
1336//! ```
1337//!
1338//! # Features
1339//!
1340//! - The `argon2` feature, when enabled, introduces a dependency on `argon2`
1341//!   and implements the `Ksf` trait for `Argon2` with a set of default parameters.
1342//!   In general, secure instantiations should choose to invoke a memory-hard password
1343//!   hashing function when the client's password is expected to have low entropy,
1344//!   instead of relying on [`ksf::Identity`] as done in the above example. The
1345//!   more computationally intensive the `Ksf` function is, the more resistant
1346//!   the server's password file records will be against offline dictionary and precomputation
1347//!   attacks; see [the OPAQUE paper](https://eprint.iacr.org/2018/163.pdf) for
1348//!   more details. The `argon2` feature requires [`alloc`].
1349//!
1350//! - The `serde` feature, enabled by default, provides convenience functions for serializing and deserializing with [serde](https://serde.rs/).
1351//!
1352//! - The `ristretto255` feature enables using [`Ristretto255`] as a `KeGroup`
1353//!   and `OprfCs`. To select a specific backend see the [curve25519-dalek]
1354//!   documentation.
1355//!
1356//! - The `curve25519` feature enables Curve25519 as a `KeGroup`. To select a
1357//!   specific backend see the [curve25519-dalek] documentation.
1358//!
1359//! - The `ecdsa` feature enables using [`elliptic_curve`]s with [`Ecdsa`] for
1360//!   [`SigmaI`]s signature algorithm.
1361//!
1362//! - The `ed25519` feature enables using [`Ed25519`]s with [`PureEddsa`] and
1363//!   [`HashEddsa`] for [`SigmaI`]s signature algorithm.
1364//!
1365//! - The `kem` feature enables the [`TripleDhKem`] key exchange, adding support
1366//!   for KEM-based handshakes backed by the `ml-kem` crate. Disabling the
1367//!   feature removes those types and their associated tests from the build.
1368//!
1369//! [`alloc`]: https://doc.rust-lang.org/alloc
1370//! [curve25519-dalek]: https://docs.rs/curve25519-dalek/4/curve25519_dalek/index.html#backends
1371
1372#![no_std]
1373#![cfg_attr(docsrs, feature(doc_cfg))]
1374#![cfg_attr(not(test), deny(unsafe_code))]
1375#![warn(clippy::cargo, clippy::doc_markdown, missing_docs, rustdoc::all)]
1376#![cfg_attr(not(test), warn(unused_crate_dependencies))]
1377#![allow(type_alias_bounds)]
1378
1379#[cfg(any(feature = "std", test))]
1380extern crate std;
1381
1382// Error types
1383pub mod errors;
1384
1385pub mod ciphersuite;
1386mod envelope;
1387pub mod hash;
1388pub mod key_exchange;
1389pub mod keypair;
1390pub mod ksf;
1391mod messages;
1392mod opaque;
1393mod serialization;
1394
1395#[cfg(test)]
1396mod tests;
1397
1398// Exports
1399
1400#[cfg(feature = "argon2")]
1401pub use argon2;
1402pub use generic_array;
1403pub use hybrid_array;
1404#[cfg(feature = "kem")]
1405pub use ml_kem;
1406pub use rand;
1407
1408pub use crate::ciphersuite::CipherSuite;
1409#[cfg(feature = "curve25519")]
1410pub use crate::key_exchange::group::curve25519::Curve25519;
1411#[cfg(feature = "ed25519")]
1412pub use crate::key_exchange::group::ed25519::Ed25519;
1413#[cfg(feature = "ristretto255")]
1414pub use crate::key_exchange::group::ristretto255::Ristretto255;
1415pub use crate::key_exchange::sigma_i::SigmaI;
1416#[cfg(feature = "ecdsa")]
1417pub use crate::key_exchange::sigma_i::ecdsa::Ecdsa;
1418pub use crate::key_exchange::sigma_i::hash_eddsa::HashEddsa;
1419pub use crate::key_exchange::sigma_i::pure_eddsa::PureEddsa;
1420pub use crate::key_exchange::tripledh::TripleDh;
1421#[cfg(feature = "kem")]
1422pub use crate::key_exchange::tripledh_kem::TripleDhKem;
1423pub use crate::messages::{
1424    CredentialFinalization, CredentialFinalizationLen, CredentialRequest, CredentialRequestLen,
1425    CredentialResponse, CredentialResponseLen, RegistrationRequest, RegistrationRequestLen,
1426    RegistrationResponse, RegistrationResponseLen, RegistrationUpload, RegistrationUploadLen,
1427    ServerLoginBuilder,
1428};
1429pub use crate::opaque::{
1430    ClientLogin, ClientLoginFinishParameters, ClientLoginFinishResult, ClientLoginStartResult,
1431    ClientRegistration, ClientRegistrationFinishParameters, ClientRegistrationFinishResult,
1432    ClientRegistrationStartResult, Identifiers, KeyMaterialInfo, ServerLogin,
1433    ServerLoginFinishResult, ServerLoginParameters, ServerLoginStartResult, ServerRegistration,
1434    ServerRegistrationLen, ServerRegistrationStartResult, ServerSetup,
1435};