Skip to main content

voprf_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 a verifiable oblivious pseudorandom function (VOPRF)
6//!
7//! Note: This implementation is in sync with
8//! [RFC 9497](https://www.rfc-editor.org/rfc/rfc9497).
9//!
10//! # Overview
11//!
12//! A verifiable oblivious pseudorandom function is a protocol that is evaluated
13//! between a client and a server. They must first agree on a finite cyclic
14//! group along with a point representation.
15//!
16//! We will use the following choice in this example:
17//!
18//! ```ignore
19//! type CipherSuite = voprf_vx::Ristretto255;
20//! ```
21//!
22//! ## Modes of Operation
23//!
24//! VOPRF can be used in three modes:
25//! - [Base Mode](#base-mode), which corresponds to a normal OPRF evaluation
26//!   with no support for the verification of the OPRF outputs
27//! - [Verifiable Mode](#verifiable-mode), which corresponds to an OPRF
28//!   evaluation where the outputs can be verified against a server public key
29//!   (VOPRF)
30//! - [Partially Oblivious Verifiable Mode](#metadata), which corresponds to a
31//!   VOPRF, where a public input can be supplied to the PRF computation
32//!
33//! In all of these modes, the protocol begins with a client blinding, followed
34//! by a server evaluation, and finishes with a client finalization and server
35//! evaluation.
36//!
37//! ## Base Mode
38//!
39//! In base mode, an [OprfClient] interacts with an [OprfServer] to compute the
40//! output of the OPRF.
41//!
42//! ### Server Setup
43//!
44//! The protocol begins with a setup phase, in which the server must run
45//! [OprfServer::new()] to produce an instance of itself. This instance must be
46//! persisted on the server and used for online client evaluations.
47//!
48//! ```
49//! # #[cfg(feature = "ristretto255")]
50//! # type CipherSuite = voprf_vx::Ristretto255;
51//! # #[cfg(not(feature = "ristretto255"))]
52//! # type CipherSuite = p256::NistP256;
53//! use rand::rngs::SysRng;
54//! use rand::Rng;
55//! use voprf_vx::OprfServer;
56//!
57//! let mut server_rng = SysRng;
58//! let server = OprfServer::<CipherSuite>::new(&mut server_rng);
59//! ```
60//!
61//! ### Client Blinding
62//!
63//! In the first step, the client chooses an input, and runs [OprfClient::blind]
64//! to produce an [OprfClientBlindResult], which consists of a [BlindedElement]
65//! to be sent to the server and an [OprfClient] which must be persisted on the
66//! client for the final step of the VOPRF protocol.
67//!
68//! ```
69//! # #[cfg(feature = "ristretto255")]
70//! # type CipherSuite = voprf_vx::Ristretto255;
71//! # #[cfg(not(feature = "ristretto255"))]
72//! # type CipherSuite = p256::NistP256;
73//! use rand::rngs::SysRng;
74//! use rand::Rng;
75//! use voprf_vx::OprfClient;
76//!
77//! let mut client_rng = SysRng;
78//! let client_blind_result = OprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
79//!     .expect("Unable to construct client");
80//! ```
81//!
82//! ### Server Blind Evaluation
83//!
84//! In the second step, the server takes as input the message from
85//! [OprfClient::blind] (a [BlindedElement]), and runs
86//! [OprfServer::blind_evaluate] to produce [EvaluationElement] to be sent to
87//! the client.
88//!
89//! ```
90//! # #[cfg(feature = "ristretto255")]
91//! # type CipherSuite = voprf_vx::Ristretto255;
92//! # #[cfg(not(feature = "ristretto255"))]
93//! # type CipherSuite = p256::NistP256;
94//! # use voprf_vx::OprfClient;
95//! # use rand::{rngs::SysRng, Rng};
96//! #
97//! # let mut client_rng = SysRng;
98//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
99//! #     b"input",
100//! #     &mut client_rng,
101//! # ).expect("Unable to construct client");
102//! # use voprf_vx::OprfServer;
103//! # let mut server_rng = SysRng;
104//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
105//! let server_evaluate_result = server.blind_evaluate(&client_blind_result.message);
106//! ```
107//!
108//! ### Client Finalization
109//!
110//! In the final step on the client side, the client takes as input the message
111//! from [OprfServer::evaluate] (an [EvaluationElement]), and runs
112//! [OprfClient::finalize] to produce an output for the protocol.
113//!
114//! ```
115//! # #[cfg(feature = "ristretto255")]
116//! # type CipherSuite = voprf_vx::Ristretto255;
117//! # #[cfg(not(feature = "ristretto255"))]
118//! # type CipherSuite = p256::NistP256;
119//! # use voprf_vx::OprfClient;
120//! # use rand::{rngs::SysRng, Rng};
121//! #
122//! # let mut client_rng = SysRng;
123//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
124//! #     b"input",
125//! #     &mut client_rng,
126//! # ).expect("Unable to construct client");
127//! # use voprf_vx::OprfServer;
128//! # let mut server_rng = SysRng;
129//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
130//! # let message = server.blind_evaluate(&client_blind_result.message);
131//! let client_finalize_result = client_blind_result
132//!     .state
133//!     .finalize(b"input", &message)
134//!     .expect("Unable to perform client finalization");
135//!
136//! println!("VOPRF output: {:?}", client_finalize_result.to_vec());
137//! ```
138//!
139//! ### Server Evaluation
140//!
141//! Optionally, if the server has direct access to the PRF input, then it need
142//! not perform the oblivious computation and can simply run
143//! [OprfServer::evaluate] to generate an output which matches the output
144//! produced by an execution of the oblivious protocol on the same input and
145//! key.
146//!
147//! ```
148//! # #[cfg(feature = "ristretto255")]
149//! # type CipherSuite = voprf_vx::Ristretto255;
150//! # #[cfg(not(feature = "ristretto255"))]
151//! # type CipherSuite = p256::NistP256;
152//! # use voprf_vx::OprfClient;
153//! # use rand::{rngs::SysRng, Rng};
154//! #
155//! # let mut client_rng = SysRng;
156//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
157//! #     b"input",
158//! #     &mut client_rng,
159//! # ).expect("Unable to construct client");
160//! # use voprf_vx::OprfServer;
161//! # let mut server_rng = SysRng;
162//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
163//! # let message = server.blind_evaluate(&client_blind_result.message);
164//! let client_finalize_result = client_blind_result
165//!     .state
166//!     .finalize(b"input", &message)
167//!     .expect("Unable to perform client finalization");
168//!
169//! let server_evaluate_result = server
170//!     .evaluate(b"input")
171//!     .expect("Unable to perform the server evaluation");
172//!
173//! assert_eq!(client_finalize_result, server_evaluate_result);
174//! ```
175//!
176//! ## Verifiable Mode
177//!
178//! In verifiable mode, a [VoprfClient] interacts with a [VoprfServer] to
179//! compute the output of the VOPRF. In order to verify the server's
180//! computation, the client checks a server-generated proof against the server's
181//! public key. If the proof fails to verify, then the client does not receive
182//! an output.
183//!
184//! In batch mode, a single proof can be used for multiple VOPRF evaluations.
185//! See [the batching section](#batching) for more details on how to perform
186//! batch evaluations.
187//!
188//! ### Server Setup
189//!
190//! The protocol begins with a setup phase, in which the server must run
191//! [VoprfServer::new()] to produce an instance of itself. This instance must be
192//! persisted on the server and used for online client evaluations.
193//!
194//! ```
195//! # #[cfg(feature = "ristretto255")]
196//! # type CipherSuite = voprf_vx::Ristretto255;
197//! # #[cfg(not(feature = "ristretto255"))]
198//! # type CipherSuite = p256::NistP256;
199//! use rand::rngs::SysRng;
200//! use rand::Rng;
201//! use voprf_vx::VoprfServer;
202//!
203//! let mut server_rng = SysRng;
204//! let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
205//!
206//! // To be sent to the client
207//! println!("Server public key: {:?}", server.get_public_key());
208//! ```
209//!
210//! The public key should be sent to the client, since the client will need it
211//! in the final step of the protocol in order to complete the evaluation of the
212//! VOPRF.
213//!
214//! ### Client Blinding
215//!
216//! In the first step, the client chooses an input, and runs
217//! [VoprfClient::blind] to produce a [VoprfClientBlindResult], which consists
218//! of a [BlindedElement] to be sent to the server and a [VoprfClient] which
219//! must be persisted on the client for the final step of the VOPRF protocol.
220//!
221//! ```
222//! # #[cfg(feature = "ristretto255")]
223//! # type CipherSuite = voprf_vx::Ristretto255;
224//! # #[cfg(not(feature = "ristretto255"))]
225//! # type CipherSuite = p256::NistP256;
226//! use rand::rngs::SysRng;
227//! use rand::Rng;
228//! use voprf_vx::VoprfClient;
229//!
230//! let mut client_rng = SysRng;
231//! let client_blind_result = VoprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
232//!     .expect("Unable to construct client");
233//! ```
234//!
235//! ### Server Blind Evaluation
236//!
237//! In the second step, the server takes as input the message from
238//! [VoprfClient::blind] (a [BlindedElement]), and runs
239//! [VoprfServer::blind_evaluate] to produce a [VoprfServerEvaluateResult],
240//! which consists of an [EvaluationElement] to be sent to the client along with
241//! a proof.
242//!
243//! ```
244//! # #[cfg(feature = "ristretto255")]
245//! # type CipherSuite = voprf_vx::Ristretto255;
246//! # #[cfg(not(feature = "ristretto255"))]
247//! # type CipherSuite = p256::NistP256;
248//! # use voprf_vx::{VoprfServerEvaluateResult, VoprfClient};
249//! # use rand::{rngs::SysRng, Rng};
250//! #
251//! # let mut client_rng = SysRng;
252//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
253//! #     b"input",
254//! #     &mut client_rng,
255//! # ).expect("Unable to construct client");
256//! # use voprf_vx::VoprfServer;
257//! # let mut server_rng = SysRng;
258//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
259//! let VoprfServerEvaluateResult { message, proof } =
260//!     server.blind_evaluate(&mut server_rng, &client_blind_result.message);
261//! ```
262//!
263//! ### Client Finalization
264//!
265//! In the final step, the client takes as input the message from
266//! [VoprfServer::blind_evaluate] (an [EvaluationElement]), the proof, and the
267//! server's public key, and runs [VoprfClient::finalize] to produce an output
268//! for the protocol.
269//!
270//! ```
271//! # #[cfg(feature = "ristretto255")]
272//! # type CipherSuite = voprf_vx::Ristretto255;
273//! # #[cfg(not(feature = "ristretto255"))]
274//! # type CipherSuite = p256::NistP256;
275//! # use voprf_vx::VoprfClient;
276//! # use rand::{rngs::SysRng, Rng};
277//! #
278//! # let mut client_rng = SysRng;
279//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
280//! #     b"input",
281//! #     &mut client_rng,
282//! # ).expect("Unable to construct client");
283//! # use voprf_vx::VoprfServer;
284//! # let mut server_rng = SysRng;
285//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
286//! # let server_evaluate_result = server.blind_evaluate(
287//! #     &mut server_rng,
288//! #     &client_blind_result.message,
289//! # );
290//! let client_finalize_result = client_blind_result
291//!     .state
292//!     .finalize(
293//!         b"input",
294//!         &server_evaluate_result.message,
295//!         &server_evaluate_result.proof,
296//!         server.get_public_key(),
297//!     )
298//!     .expect("Unable to perform client finalization");
299//!
300//! println!("VOPRF output: {:?}", client_finalize_result.to_vec());
301//! ```
302//!
303//! ### Server Evaluation
304//!
305//! Optionally, if the server has direct access to the PRF input, then it need
306//! not perform the oblivious computation and can simply run
307//! [VoprfServer::evaluate] to generate an output which matches the output
308//! produced by an execution of the oblivious protocol on the same input and
309//! key.
310//!
311//! ```
312//! # #[cfg(feature = "ristretto255")]
313//! # type CipherSuite = voprf_vx::Ristretto255;
314//! # #[cfg(not(feature = "ristretto255"))]
315//! # type CipherSuite = p256::NistP256;
316//! # use voprf_vx::VoprfClient;
317//! # use rand::{rngs::SysRng, Rng};
318//! #
319//! # let mut client_rng = SysRng;
320//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
321//! #     b"input",
322//! #     &mut client_rng,
323//! # ).expect("Unable to construct client");
324//! # use voprf_vx::VoprfServer;
325//! # let mut server_rng = SysRng;
326//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
327//! # let server_evaluate_result = server.blind_evaluate(
328//! #     &mut server_rng,
329//! #     &client_blind_result.message,
330//! # );
331//! let client_finalize_result = client_blind_result
332//!     .state
333//!     .finalize(
334//!         b"input",
335//!         &server_evaluate_result.message,
336//!         &server_evaluate_result.proof,
337//!         server.get_public_key(),
338//!     )
339//!     .expect("Unable to perform client finalization");
340//!
341//! let server_evaluate_result = server
342//!     .evaluate(b"input")
343//!     .expect("Unable to perform the server evaluation");
344//!
345//! assert_eq!(client_finalize_result, server_evaluate_result);
346//! ```
347//!
348//! # Advanced Usage
349//!
350//! There are two additional (and optional) extensions to the core VOPRF
351//! protocol: support for batching of evaluations, and support for public
352//! metadata.
353//!
354//! ## Batching
355//!
356//! It is sometimes desirable to generate only a single, constant-size proof for
357//! an unbounded number of VOPRF evaluations (on arbitrary inputs).
358//! [VoprfClient] and [VoprfServer] support a batch API for handling this case.
359//! In the following example, we show how to use the batch API to produce a
360//! single proof for 10 parallel VOPRF evaluations.
361//!
362//! First, the client produces 10 blindings, storing their resulting states and
363//! messages:
364//!
365//! ```
366//! # #[cfg(feature = "ristretto255")]
367//! # type CipherSuite = voprf_vx::Ristretto255;
368//! # #[cfg(not(feature = "ristretto255"))]
369//! # type CipherSuite = p256::NistP256;
370//! # use voprf_vx::VoprfClient;
371//! # use rand::{rngs::SysRng, Rng};
372//! #
373//! let mut client_rng = SysRng;
374//! let mut client_states = vec![];
375//! let mut client_messages = vec![];
376//! for _ in 0..10 {
377//!     let client_blind_result = VoprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
378//!         .expect("Unable to construct client");
379//!     client_states.push(client_blind_result.state);
380//!     client_messages.push(client_blind_result.message);
381//! }
382//! ```
383//!
384//! Next, the server calls the [VoprfServer::batch_blind_evaluate_prepare] and
385//! [VoprfServer::batch_blind_evaluate_finish] function on a set of client
386//! messages, to produce a corresponding set of messages to be returned to the
387//! client (returned in the same order), along with a single proof:
388//!
389//! ```
390//! # #[cfg(feature = "ristretto255")]
391//! # type CipherSuite = voprf_vx::Ristretto255;
392//! # #[cfg(not(feature = "ristretto255"))]
393//! # type CipherSuite = p256::NistP256;
394//! # use voprf_vx::{VoprfServerBatchEvaluateFinishResult, VoprfClient};
395//! # use rand::{rngs::SysRng, Rng};
396//! #
397//! # let mut client_rng = SysRng;
398//! # let mut client_states = vec![];
399//! # let mut client_messages = vec![];
400//! # for _ in 0..10 {
401//! #     let client_blind_result = VoprfClient::<CipherSuite>::blind(
402//! #         b"input",
403//! #        &mut client_rng,
404//! #     ).expect("Unable to construct client");
405//! #     client_states.push(client_blind_result.state);
406//! #     client_messages.push(client_blind_result.message);
407//! # }
408//! # use voprf_vx::VoprfServer;
409//! let mut server_rng = SysRng;
410//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
411//! let prepared_evaluation_elements = server.batch_blind_evaluate_prepare(client_messages.iter());
412//! let prepared_elements: Vec<_> = prepared_evaluation_elements.collect();
413//! let VoprfServerBatchEvaluateFinishResult { messages, proof } = server
414//!     .batch_blind_evaluate_finish(&mut server_rng, client_messages.iter(), &prepared_elements)
415//!     .expect("Unable to perform server batch evaluate");
416//! let messages: Vec<_> = messages.collect();
417//! ```
418//!
419//! If `alloc` is available, `VoprfServer::batch_blind_evaluate` can be called
420//! to avoid having to collect output manually:
421//!
422//! ```
423//! # #[cfg(feature = "alloc")] {
424//! # #[cfg(feature = "ristretto255")]
425//! # type CipherSuite = voprf_vx::Ristretto255;
426//! # #[cfg(not(feature = "ristretto255"))]
427//! # type CipherSuite = p256::NistP256;
428//! # use voprf_vx::{VoprfServerBatchEvaluateResult, VoprfClient};
429//! # use rand::{rngs::SysRng, Rng};
430//! #
431//! # let mut client_rng = SysRng;
432//! # let mut client_states = vec![];
433//! # let mut client_messages = vec![];
434//! # for _ in 0..10 {
435//! #     let client_blind_result = VoprfClient::<CipherSuite>::blind(
436//! #         b"input",
437//! #        &mut client_rng,
438//! #     ).expect("Unable to construct client");
439//! #     client_states.push(client_blind_result.state);
440//! #     client_messages.push(client_blind_result.message);
441//! # }
442//! # use voprf_vx::VoprfServer;
443//! let mut server_rng = SysRng;
444//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
445//! let VoprfServerBatchEvaluateResult { messages, proof } = server
446//!     .batch_blind_evaluate(&mut server_rng, &client_messages)
447//!     .expect("Unable to perform server batch evaluate");
448//! # }
449//! ```
450//!
451//! Then, the client calls [VoprfClient::batch_finalize] on the client states
452//! saved from the first step, along with the messages returned by the server,
453//! along with the server's proof, in order to produce a vector of outputs if
454//! the proof verifies correctly.
455//!
456//! ```
457//! # #[cfg(feature = "alloc")] {
458//! # #[cfg(feature = "ristretto255")]
459//! # type CipherSuite = voprf_vx::Ristretto255;
460//! # #[cfg(not(feature = "ristretto255"))]
461//! # type CipherSuite = p256::NistP256;
462//! # use voprf_vx::{VoprfServerBatchEvaluateResult, VoprfClient};
463//! # use rand::{rngs::SysRng, Rng};
464//! #
465//! # let mut client_rng = SysRng;
466//! # let mut client_states = vec![];
467//! # let mut client_messages = vec![];
468//! # for _ in 0..10 {
469//! #     let client_blind_result = VoprfClient::<CipherSuite>::blind(
470//! #         b"input",
471//! #        &mut client_rng,
472//! #     ).expect("Unable to construct client");
473//! #     client_states.push(client_blind_result.state);
474//! #     client_messages.push(client_blind_result.message);
475//! # }
476//! # use voprf_vx::VoprfServer;
477//! # let mut server_rng = SysRng;
478//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
479//! # let VoprfServerBatchEvaluateResult { messages, proof } = server
480//! #     .batch_blind_evaluate(&mut server_rng, &client_messages)
481//! #     .expect("Unable to perform server batch evaluate");
482//! let client_batch_finalize_result = VoprfClient::batch_finalize(
483//!     &[b"input"; 10],
484//!     &client_states,
485//!     &messages,
486//!     &proof,
487//!     server.get_public_key(),
488//! )
489//! .expect("Unable to perform client batch finalization")
490//! .collect::<Vec<_>>();
491//!
492//! println!("VOPRF batch outputs: {:?}", client_batch_finalize_result);
493//! # }
494//! ```
495//!
496//! ## Metadata
497//!
498//! The optional metadata parameter included in the POPRF mode allows clients
499//! and servers to cryptographically bind additional data to the VOPRF output.
500//! This metadata is known to both parties at the start of the protocol, and is
501//! inserted under the server's blind evaluate step and the client's finalize
502//! step. This metadata can be constructed with some type of higher-level domain
503//! separation to avoid cross-protocol attacks or related issues.
504//!
505//! The API for POPRF mode is similar to VOPRF mode, except that a [PoprfServer]
506//! and [PoprfClient] are used, and that each of the functions accept an
507//! additional (and optional) info parameter which represents the public input.
508//! See
509//! <https://www.rfc-editor.org/rfc/rfc9497#name-poprf-public-input>
510//! for more detailed information on how this public input should be used.
511//!
512//! # Features
513//!
514//! - The `alloc` feature requires Rust's `alloc` crate and enables batching
515//!   VOPRF evaluations.
516//!
517//! - The `serde` feature, enabled by default, provides convenience functions
518//!   for serializing and deserializing with [serde](https://serde.rs/).
519//!
520//! - The `danger` feature, disabled by default, exposes functions for setting
521//!   and getting internal values not available in the default API. These
522//!   functions are intended for use in by higher-level cryptographic protocols
523//!   that need access to these raw values and are able to perform the necessary
524//!   validations on them (such as being valid group elements).
525//!
526//! - The `ristretto255-ciphersuite` features enables using [`Ristretto255`] as
527//!   a [`CipherSuite`].
528//!
529//! - The `ristretto255` feature enables using [`Ristretto255`] as the
530//!   underlying group for the [Group] choice. To select a specific backend see
531//!   the [curve25519-dalek] documentation.
532//!
533//! [curve25519-dalek]:
534//!     (https://docs.rs/curve25519-dalek/4.0.0-pre.5/curve25519_dalek/index.html#backends)
535
536#![no_std]
537#![cfg_attr(docsrs, feature(doc_cfg))]
538#![cfg_attr(not(test), deny(unsafe_code))]
539#![warn(
540    clippy::cargo,
541    clippy::missing_errors_doc,
542    missing_debug_implementations,
543    missing_docs
544)]
545#![allow(clippy::multiple_crate_versions)]
546
547#[cfg(any(feature = "alloc", test))]
548extern crate alloc;
549
550#[cfg(feature = "std")]
551extern crate std;
552
553mod ciphersuite;
554mod common;
555mod error;
556mod group;
557mod oprf;
558mod poprf;
559mod serialization;
560mod voprf;
561
562#[cfg(test)]
563mod tests;
564
565// Exports
566
567pub use crate::ciphersuite::CipherSuite;
568#[cfg(feature = "danger")]
569pub use crate::common::derive_key;
570pub use crate::common::{
571    BlindedElement, EvaluationElement, Mode, PreparedEvaluationElement, Proof,
572};
573pub use crate::error::{Error, InternalError, Result};
574pub use crate::group::Group;
575#[cfg(feature = "ristretto255")]
576pub use crate::group::Ristretto255;
577pub use crate::oprf::{OprfClient, OprfClientBlindResult, OprfServer};
578#[cfg(feature = "alloc")]
579pub use crate::poprf::PoprfServerBatchEvaluateResult;
580pub use crate::poprf::{
581    PoprfClient, PoprfClientBatchFinalizeResult, PoprfPreparedTweak, PoprfServer,
582    PoprfServerBatchEvaluateFinishResult, PoprfServerBatchEvaluateFinishedMessages,
583    PoprfServerBatchEvaluatePrepareResult, PoprfServerBatchEvaluatePreparedEvaluationElements,
584};
585pub use crate::serialization::{
586    BlindedElementLen, EvaluationElementLen, OprfClientLen, OprfServerLen, PoprfClientLen,
587    PoprfServerLen, ProofLen, VoprfClientLen, VoprfServerLen,
588};
589#[cfg(feature = "alloc")]
590pub use crate::voprf::VoprfServerBatchEvaluateResult;
591pub use crate::voprf::{
592    VoprfClient, VoprfClientBatchFinalizeResult, VoprfClientBlindResult, VoprfServer,
593    VoprfServerBatchEvaluateFinishResult, VoprfServerBatchEvaluateFinishedMessages,
594    VoprfServerBatchEvaluatePreparedEvaluationElements, VoprfServerEvaluateResult,
595};