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