From f6707331655cc092bebfb37d4e0ffac70dee8f0d Mon Sep 17 00:00:00 2001 From: raphaelrobert Date: Sat, 9 Jul 2022 13:30:20 +0200 Subject: [PATCH] Updating to draft 11 (#80) * draft-11 * Fix CI complaints * Address review comments, CHANGELOG entry, minor fixes --- CHANGELOG.md | 8 ++ Cargo.toml | 2 +- README.md | 2 +- src/common.rs | 63 +++++++++-- src/lib.rs | 190 +++++++++++++++++++++++---------- src/oprf.rs | 56 +++++++++- src/poprf.rs | 109 +++++++++++++++---- src/tests/test_cfrg_vectors.rs | 90 +++++++++++++--- src/voprf.rs | 98 +++++++++++++---- 9 files changed, 489 insertions(+), 129 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3b152d..b987c85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.4.0-pre.4 (July 9, 2022) +* Updated to be in sync with draft-irtf-cfrg-voprf-11 +* Adds the evaluate() function to the servers to calculate the output of the OPRF + directly +* Renames the former evaluate() function to blind_evaluate to match the spec +* Fixes the order of parameters for PoprfClient::blind to align it with the + other clients + ## 0.4.0-pre.3 (July 1, 2022) * Updated to be in sync with draft-irtf-cfrg-voprf-10, with the only difference from -09 being a constant string change diff --git a/Cargo.toml b/Cargo.toml index 49f86f1..3130245 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ name = "voprf" readme = "README.md" repository = "https://github.com/novifinancial/voprf/" rust-version = "1.57" -version = "0.4.0-pre.3" +version = "0.4.0-pre.4" [features] alloc = [] diff --git a/README.md b/README.md index b9a86a0..bbadcd9 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Installation Add the following line to the dependencies of your `Cargo.toml`: ``` -voprf = "0.4.0-pre.3" +voprf = "0.4.0-pre.4" ``` ### Minimum Supported Rust Version diff --git a/src/common.rs b/src/common.rs index 214be01..6af1b95 100644 --- a/src/common.rs +++ b/src/common.rs @@ -11,7 +11,7 @@ use core::convert::TryFrom; use derive_where::derive_where; use digest::core_api::BlockSizeUser; -use digest::{Digest, OutputSizeUser}; +use digest::{Digest, Output, OutputSizeUser}; use generic_array::sequence::Concat; use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U11, U2, U256}; use generic_array::{ArrayLength, GenericArray}; @@ -153,7 +153,7 @@ where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { - // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-10.html#section-2.2.1 + // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html#section-2.2.1 let (m, z) = compute_composites::(Some(k), b, cs, ds, mode)?; @@ -216,7 +216,7 @@ where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { - // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-10.html#section-2.2.2 + // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html#section-2.2.2 let (m, z) = compute_composites::(None, b, cs, ds, mode)?; let t2 = (a * &proof.s_scalar) + &(b * &proof.c_scalar); let t3 = (m * &proof.s_scalar) + &(z * &proof.c_scalar); @@ -285,7 +285,7 @@ where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { - // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-10.html#section-2.2.1 + // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html#section-2.2.1 let elem_len = ::ElemLen::U16.to_be_bytes(); @@ -437,12 +437,61 @@ where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { - let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::(mode)); - let hashed_point = - CS::Group::hash_to_curve::(&[input], &dst).map_err(|_| Error::Input)?; + let hashed_point = hash_to_group::(input, mode)?; Ok(hashed_point * blind) } +/// Hashes `input` to a point on the curve +pub(crate) fn hash_to_group( + input: &[u8], + mode: Mode, +) -> Result<::Elem> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::(mode)); + CS::Group::hash_to_curve::(&[input], &dst).map_err(|_| Error::Input) +} + +/// Internal function that finalizes the hash input for OPRF, VOPRF & POPRF. +/// Returned values can only fail with [`Error::Input`]. +pub(crate) fn server_evaluate_hash_input( + input: &[u8], + info: Option<&[u8]>, + issued_element: GenericArray::Group as Group>::ElemLen>, +) -> Result> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + // OPRF & VOPRF + // hashInput = I2OSP(len(input), 2) || input || + // I2OSP(len(issuedElement), 2) || issuedElement || + // "Finalize" + // return Hash(hashInput) + // + // POPRF + // hashInput = I2OSP(len(input), 2) || input || + // I2OSP(len(info), 2) || info || + // I2OSP(len(issuedElement), 2) || issuedElement || + // "Finalize" + + let mut hash = CS::Hash::new() + .chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?) + .chain_update(input.as_ref()); + if let Some(info) = info { + hash = hash + .chain_update(i2osp_2(info.as_ref().len()).map_err(|_| Error::Input)?) + .chain_update(info.as_ref()); + } + Ok(hash + .chain_update(i2osp_2(issued_element.as_ref().len()).map_err(|_| Error::Input)?) + .chain_update(issued_element) + .chain_update(STR_FINALIZE) + .finalize()) +} + /// Generates the contextString parameter as defined in /// pub(crate) fn create_context_string(mode: Mode) -> GenericArray diff --git a/src/lib.rs b/src/lib.rs index 6d53fee..00fbe88 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ //! An implementation of a verifiable oblivious pseudorandom function (VOPRF) //! //! Note: This implementation is in sync with -//! [draft-irtf-cfrg-voprf-10](https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-10.html), +//! [draft-irtf-cfrg-voprf-11](https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html), //! but this specification is subject to change, until the final version //! published by the IETF. //! @@ -36,18 +36,19 @@ //! VOPRF, where a public input can be supplied to the PRF computation //! //! In all of these modes, the protocol begins with a client blinding, followed -//! by a server evaluation, and finishes with a client finalization. +//! by a server evaluation, and finishes with a client finalization and server +//! evaluation. //! //! ## Base Mode //! -//! In base mode, an [OprfClient] interacts with an [OprfServer] -//! to compute the output of the OPRF. +//! In base mode, an [OprfClient] interacts with an [OprfServer] to compute the +//! output of the OPRF. //! //! ### Server Setup //! //! The protocol begins with a setup phase, in which the server must run -//! [OprfServer::new()] to produce an instance of itself. This instance -//! must be persisted on the server and used for online client evaluations. +//! [OprfServer::new()] to produce an instance of itself. This instance must be +//! persisted on the server and used for online client evaluations. //! //! ``` //! # #[cfg(feature = "ristretto255")] @@ -64,11 +65,10 @@ //! //! ### Client Blinding //! -//! In the first step, the client chooses an input, and runs -//! [OprfClient::blind] to produce an [OprfClientBlindResult], -//! which consists of a [BlindedElement] to be sent to the server and an -//! [OprfClient] which must be persisted on the client for the final -//! step of the VOPRF protocol. +//! In the first step, the client chooses an input, and runs [OprfClient::blind] +//! to produce an [OprfClientBlindResult], which consists of a [BlindedElement] +//! to be sent to the server and an [OprfClient] which must be persisted on the +//! client for the final step of the VOPRF protocol. //! //! ``` //! # #[cfg(feature = "ristretto255")] @@ -84,11 +84,11 @@ //! .expect("Unable to construct client"); //! ``` //! -//! ### Server Evaluation +//! ### Server Blind Evaluation //! //! In the second step, the server takes as input the message from //! [OprfClient::blind] (a [BlindedElement]), and runs -//! [OprfServer::evaluate] to produce [EvaluationElement] to be sent to +//! [OprfServer::blind_evaluate] to produce [EvaluationElement] to be sent to //! the client. //! //! ``` @@ -107,13 +107,13 @@ //! # use voprf::OprfServer; //! # let mut server_rng = OsRng; //! # let server = OprfServer::::new(&mut server_rng).unwrap(); -//! let server_evaluate_result = server.evaluate(&client_blind_result.message); +//! let server_evaluate_result = server.blind_evaluate(&client_blind_result.message); //! ``` //! //! ### Client Finalization //! -//! In the final step, the client takes as input the message from -//! [OprfServer::evaluate] (an [EvaluationElement]), and runs +//! In the final step on the client side, the client takes as input the message +//! from [OprfServer::evaluate] (an [EvaluationElement]), and runs //! [OprfClient::finalize] to produce an output for the protocol. //! //! ``` @@ -132,7 +132,7 @@ //! # use voprf::OprfServer; //! # let mut server_rng = OsRng; //! # let server = OprfServer::::new(&mut server_rng).unwrap(); -//! # let message = server.evaluate(&client_blind_result.message); +//! # let message = server.blind_evaluate(&client_blind_result.message); //! let client_finalize_result = client_blind_result //! .state //! .finalize(b"input", &message) @@ -141,10 +141,47 @@ //! println!("VOPRF output: {:?}", client_finalize_result.to_vec()); //! ``` //! +//! ### Server Evaluation +//! +//! Optionally, if the server has direct access to the PRF input, then it need +//! not perform the oblivious computation and can simply run +//! [OprfServer::evaluate] to generate an output which matches the output +//! produced by an execution of the oblivious protocol on the same input and +//! key. +//! +//! ``` +//! # #[cfg(feature = "ristretto255")] +//! # type CipherSuite = voprf::Ristretto255; +//! # #[cfg(not(feature = "ristretto255"))] +//! # type CipherSuite = p256::NistP256; +//! # use voprf::OprfClient; +//! # use rand::{rngs::OsRng, RngCore}; +//! # +//! # let mut client_rng = OsRng; +//! # let client_blind_result = OprfClient::::blind( +//! # b"input", +//! # &mut client_rng, +//! # ).expect("Unable to construct client"); +//! # use voprf::OprfServer; +//! # let mut server_rng = OsRng; +//! # let server = OprfServer::::new(&mut server_rng).unwrap(); +//! # let message = server.blind_evaluate(&client_blind_result.message); +//! let client_finalize_result = client_blind_result +//! .state +//! .finalize(b"input", &message) +//! .expect("Unable to perform client finalization"); +//! +//! let server_evaluate_result = server +//! .evaluate(b"input") +//! .expect("Unable to perform the server evaluation"); +//! +//! assert_eq!(client_finalize_result, server_evaluate_result); +//! ``` +//! //! ## Verifiable Mode //! -//! In verifiable mode, a [VoprfClient] interacts with a [VoprfServer] -//! to compute the output of the VOPRF. In order to verify the server's +//! In verifiable mode, a [VoprfClient] interacts with a [VoprfServer] to +//! compute the output of the VOPRF. In order to verify the server's //! computation, the client checks a server-generated proof against the server's //! public key. If the proof fails to verify, then the client does not receive //! an output. @@ -156,8 +193,8 @@ //! ### Server Setup //! //! The protocol begins with a setup phase, in which the server must run -//! [VoprfServer::new()] to produce an instance of itself. This instance -//! must be persisted on the server and used for online client evaluations. +//! [VoprfServer::new()] to produce an instance of itself. This instance must be +//! persisted on the server and used for online client evaluations. //! //! ``` //! # #[cfg(feature = "ristretto255")] @@ -182,10 +219,9 @@ //! ### Client Blinding //! //! In the first step, the client chooses an input, and runs -//! [VoprfClient::blind] to produce a [VoprfClientBlindResult], which -//! consists of a [BlindedElement] to be sent to the server and a -//! [VoprfClient] which must be persisted on the client for the final step -//! of the VOPRF protocol. +//! [VoprfClient::blind] to produce a [VoprfClientBlindResult], which consists +//! of a [BlindedElement] to be sent to the server and a [VoprfClient] which +//! must be persisted on the client for the final step of the VOPRF protocol. //! //! ``` //! # #[cfg(feature = "ristretto255")] @@ -201,11 +237,11 @@ //! .expect("Unable to construct client"); //! ``` //! -//! ### Server Evaluation +//! ### Server Blind Evaluation //! //! In the second step, the server takes as input the message from //! [VoprfClient::blind] (a [BlindedElement]), and runs -//! [VoprfServer::evaluate] to produce a [VoprfServerEvaluateResult], +//! [VoprfServer::blind_evaluate] to produce a [VoprfServerEvaluateResult], //! which consists of an [EvaluationElement] to be sent to the client along with //! a proof. //! @@ -226,15 +262,15 @@ //! # let mut server_rng = OsRng; //! # let server = VoprfServer::::new(&mut server_rng).unwrap(); //! let VoprfServerEvaluateResult { message, proof } = -//! server.evaluate(&mut server_rng, &client_blind_result.message); +//! server.blind_evaluate(&mut server_rng, &client_blind_result.message); //! ``` //! //! ### Client Finalization //! //! In the final step, the client takes as input the message from -//! [VoprfServer::evaluate] (an [EvaluationElement]), the proof, and the -//! server's public key, and runs [VoprfClient::finalize] to produce an -//! output for the protocol. +//! [VoprfServer::blind_evaluate] (an [EvaluationElement]), the proof, and the +//! server's public key, and runs [VoprfClient::finalize] to produce an output +//! for the protocol. //! //! ``` //! # #[cfg(feature = "ristretto255")] @@ -252,7 +288,7 @@ //! # use voprf::VoprfServer; //! # let mut server_rng = OsRng; //! # let server = VoprfServer::::new(&mut server_rng).unwrap(); -//! # let server_evaluate_result = server.evaluate( +//! # let server_evaluate_result = server.blind_evaluate( //! # &mut server_rng, //! # &client_blind_result.message, //! # ); @@ -269,6 +305,51 @@ //! println!("VOPRF output: {:?}", client_finalize_result.to_vec()); //! ``` //! +//! ### Server Evaluation +//! +//! Optionally, if the server has direct access to the PRF input, then it need +//! not perform the oblivious computation and can simply run +//! [VoprfServer::evaluate] to generate an output which matches the output +//! produced by an execution of the oblivious protocol on the same input and +//! key. +//! +//! ``` +//! # #[cfg(feature = "ristretto255")] +//! # type CipherSuite = voprf::Ristretto255; +//! # #[cfg(not(feature = "ristretto255"))] +//! # type CipherSuite = p256::NistP256; +//! # use voprf::VoprfClient; +//! # use rand::{rngs::OsRng, RngCore}; +//! # +//! # let mut client_rng = OsRng; +//! # let client_blind_result = VoprfClient::::blind( +//! # b"input", +//! # &mut client_rng, +//! # ).expect("Unable to construct client"); +//! # use voprf::VoprfServer; +//! # let mut server_rng = OsRng; +//! # let server = VoprfServer::::new(&mut server_rng).unwrap(); +//! # let server_evaluate_result = server.blind_evaluate( +//! # &mut server_rng, +//! # &client_blind_result.message, +//! # ); +//! let client_finalize_result = client_blind_result +//! .state +//! .finalize( +//! b"input", +//! &server_evaluate_result.message, +//! &server_evaluate_result.proof, +//! server.get_public_key(), +//! ) +//! .expect("Unable to perform client finalization"); +//! +//! let server_evaluate_result = server +//! .evaluate(b"input") +//! .expect("Unable to perform the server evaluation"); +//! +//! assert_eq!(client_finalize_result, server_evaluate_result); +//! ``` +//! //! # Advanced Usage //! //! There are two additional (and optional) extensions to the core VOPRF @@ -279,9 +360,9 @@ //! //! It is sometimes desirable to generate only a single, constant-size proof for //! an unbounded number of VOPRF evaluations (on arbitrary inputs). -//! [VoprfClient] and [VoprfServer] support a batch API for handling -//! this case. In the following example, we show how to use the batch API to -//! produce a single proof for 10 parallel VOPRF evaluations. +//! [VoprfClient] and [VoprfServer] support a batch API for handling this case. +//! In the following example, we show how to use the batch API to produce a +//! single proof for 10 parallel VOPRF evaluations. //! //! First, the client produces 10 blindings, storing their resulting states and //! messages: @@ -305,8 +386,8 @@ //! } //! ``` //! -//! Next, the server calls the [VoprfServer::batch_evaluate_prepare] and -//! [VoprfServer::batch_evaluate_finish] function on a set of client +//! Next, the server calls the [VoprfServer::batch_blind_evaluate_prepare] and +//! [VoprfServer::batch_blind_evaluate_finish] function on a set of client //! messages, to produce a corresponding set of messages to be returned to the //! client (returned in the same order), along with a single proof: //! @@ -332,15 +413,15 @@ //! # use voprf::VoprfServer; //! let mut server_rng = OsRng; //! # let server = VoprfServer::::new(&mut server_rng).unwrap(); -//! let prepared_evaluation_elements = server.batch_evaluate_prepare(client_messages.iter()); +//! let prepared_evaluation_elements = server.batch_blind_evaluate_prepare(client_messages.iter()); //! let prepared_elements: Vec<_> = prepared_evaluation_elements.collect(); //! let VoprfServerBatchEvaluateFinishResult { messages, proof } = server -//! .batch_evaluate_finish(&mut server_rng, client_messages.iter(), &prepared_elements) +//! .batch_blind_evaluate_finish(&mut server_rng, client_messages.iter(), &prepared_elements) //! .expect("Unable to perform server batch evaluate"); //! let messages: Vec<_> = messages.collect(); //! ``` //! -//! If `alloc` is available, `VoprfServer::batch_evaluate` can be called +//! If `alloc` is available, `VoprfServer::batch_blind_evaluate` can be called //! to avoid having to collect output manually: //! //! ``` @@ -367,15 +448,15 @@ //! let mut server_rng = OsRng; //! # let server = VoprfServer::::new(&mut server_rng).unwrap(); //! let VoprfServerBatchEvaluateResult { messages, proof } = server -//! .batch_evaluate(&mut server_rng, &client_messages) +//! .batch_blind_evaluate(&mut server_rng, &client_messages) //! .expect("Unable to perform server batch evaluate"); //! # } //! ``` //! -//! Then, the client calls [VoprfClient::batch_finalize] on the client -//! states saved from the first step, along with the messages returned by the -//! server, along with the server's proof, in order to produce a vector of -//! outputs if the proof verifies correctly. +//! Then, the client calls [VoprfClient::batch_finalize] on the client states +//! saved from the first step, along with the messages returned by the server, +//! along with the server's proof, in order to produce a vector of outputs if +//! the proof verifies correctly. //! //! ``` //! # #[cfg(feature = "alloc")] { @@ -401,7 +482,7 @@ //! # let mut server_rng = OsRng; //! # let server = VoprfServer::::new(&mut server_rng).unwrap(); //! # let VoprfServerBatchEvaluateResult { messages, proof } = server -//! # .batch_evaluate(&mut server_rng, &client_messages) +//! # .batch_blind_evaluate(&mut server_rng, &client_messages) //! # .expect("Unable to perform server batch evaluate"); //! let client_batch_finalize_result = VoprfClient::batch_finalize( //! &[b"input"; 10], @@ -420,17 +501,17 @@ //! ## Metadata //! //! The optional metadata parameter included in the POPRF mode allows clients -//! and servers to cryptographically bind additional data to the -//! VOPRF output. This metadata is known to both parties at the start of the -//! protocol, and is inserted under the server's evaluate step and the client's -//! finalize step. This metadata can be constructed with some type of -//! higher-level domain separation to avoid cross-protocol attacks or related -//! issues. +//! and servers to cryptographically bind additional data to the VOPRF output. +//! This metadata is known to both parties at the start of the protocol, and is +//! inserted under the server's blind evaluate step and the client's finalize +//! step. This metadata can be constructed with some type of higher-level domain +//! separation to avoid cross-protocol attacks or related issues. //! //! The API for POPRF mode is similar to VOPRF mode, except that a [PoprfServer] //! and [PoprfClient] are used, and that each of the functions accept an //! additional (and optional) info parameter which represents the public input. -//! See +//! See +//! //! for more detailed information on how this public input should be used. //! //! # Features @@ -464,7 +545,8 @@ //! automatically enable the `ristretto255-u64` feature and requires Rust //! nightly. //! -//! [curve25519-dalek]: (https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) +//! [curve25519-dalek]: +//! (https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) #![cfg_attr(not(test), deny(unsafe_code))] #![no_std] diff --git a/src/oprf.rs b/src/oprf.rs index f6e23d9..1fa73f0 100644 --- a/src/oprf.rs +++ b/src/oprf.rs @@ -17,8 +17,8 @@ use generic_array::GenericArray; use rand_core::{CryptoRng, RngCore}; use crate::common::{ - derive_key_internal, deterministic_blind_unchecked, i2osp_2, BlindedElement, EvaluationElement, - Mode, STR_FINALIZE, + derive_key_internal, deterministic_blind_unchecked, hash_to_group, i2osp_2, + server_evaluate_hash_input, BlindedElement, EvaluationElement, Mode, STR_FINALIZE, }; #[cfg(feature = "serde")] use crate::serialization::serde::Scalar; @@ -202,9 +202,25 @@ where /// Computes the second step for the multiplicative blinding version of /// DH-OPRF. This message is sent from the server (who holds the OPRF key) /// to the client. - pub fn evaluate(&self, blinded_element: &BlindedElement) -> EvaluationElement { + pub fn blind_evaluate(&self, blinded_element: &BlindedElement) -> EvaluationElement { EvaluationElement(blinded_element.0 * &self.sk) } + + /// Computes the output of the OPRF on the server side + /// + /// # Errors + /// [`Error::Input`] if the `input` is longer then [`u16::MAX`]. + pub fn evaluate(&self, input: &[u8]) -> Result::Hash>> { + let input_element = hash_to_group::(input, Mode::Oprf)?; + if CS::Group::is_identity_elem(input_element).into() { + return Err(Error::Input); + }; + let evaluated_element = input_element * &self.sk; + + let issued_element = CS::Group::serialize_elem(evaluated_element); + + server_evaluate_hash_input::(input, None, issued_element) + } } ///////////////////////// @@ -312,7 +328,7 @@ mod tests { let mut rng = OsRng; let client_blind_result = OprfClient::::blind(input, &mut rng).unwrap(); let server = OprfServer::::new(&mut rng).unwrap(); - let message = server.evaluate(&client_blind_result.message); + let message = server.blind_evaluate(&client_blind_result.message); let client_finalize_result = client_blind_result.state.finalize(input, &message).unwrap(); let res2 = prf::(input, server.get_private_key(), &[], Mode::Oprf); assert_eq!(client_finalize_result, res2); @@ -343,6 +359,34 @@ mod tests { assert_eq!(client_finalize_result, res2); } + fn server_evaluate() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { + let input = b"input"; + let mut rng = OsRng; + let client_blind_result = OprfClient::::blind(input, &mut rng).unwrap(); + let server = OprfServer::::new(&mut rng).unwrap(); + let server_result = server.blind_evaluate(&client_blind_result.message); + + let client_finalize = client_blind_result + .state + .finalize(input, &server_result) + .unwrap(); + + // We expect the outputs from client and server to be equal given an identical + // input + let server_evaluate = server.evaluate(input).unwrap(); + assert_eq!(client_finalize, server_evaluate); + + // We expect the outputs from client and server to be different given different + // inputs + let wrong_input = b"wrong input"; + let server_evaluate = server.evaluate(wrong_input).unwrap(); + assert!(client_finalize != server_evaluate); + } + fn zeroize_oprf_client() where ::OutputSize: @@ -370,7 +414,7 @@ mod tests { let mut rng = OsRng; let client_blind_result = OprfClient::::blind(input, &mut rng).unwrap(); let server = OprfServer::::new(&mut rng).unwrap(); - let mut message = server.evaluate(&client_blind_result.message); + let mut message = server.blind_evaluate(&client_blind_result.message); let mut state = server; unsafe { ptr::drop_in_place(&mut state) }; @@ -390,6 +434,7 @@ mod tests { base_retrieval::(); base_inversion_unsalted::(); + server_evaluate::(); zeroize_oprf_client::(); zeroize_oprf_server::(); @@ -397,6 +442,7 @@ mod tests { base_retrieval::(); base_inversion_unsalted::(); + server_evaluate::(); zeroize_oprf_client::(); zeroize_oprf_server::(); diff --git a/src/poprf.rs b/src/poprf.rs index 2191983..1dfabe7 100644 --- a/src/poprf.rs +++ b/src/poprf.rs @@ -20,9 +20,10 @@ use generic_array::GenericArray; use rand_core::{CryptoRng, RngCore}; use crate::common::{ - create_context_string, derive_keypair, deterministic_blind_unchecked, generate_proof, i2osp_2, - verify_proof, BlindedElement, EvaluationElement, Mode, PreparedEvaluationElement, Proof, - STR_FINALIZE, STR_HASH_TO_SCALAR, STR_INFO, + create_context_string, derive_keypair, deterministic_blind_unchecked, generate_proof, + hash_to_group, i2osp_2, server_evaluate_hash_input, verify_proof, BlindedElement, + EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE, STR_HASH_TO_SCALAR, + STR_INFO, }; #[cfg(feature = "serde")] use crate::serialization::serde::{Element, Scalar}; @@ -89,8 +90,8 @@ where /// # Errors /// [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`]. pub fn blind( - blinding_factor_rng: &mut R, input: &[u8], + blinding_factor_rng: &mut R, ) -> Result> { let blind = CS::Group::random_scalar(blinding_factor_rng); Self::deterministic_blind_unchecked_inner(input, blind) @@ -248,7 +249,7 @@ where /// # Errors /// - [`Error::Info`] if the `info` is longer than `u16::MAX`. /// - [`Error::Protocol`] if the protocol fails and can't be completed. - pub fn evaluate( + pub fn blind_evaluate( &self, rng: &mut R, blinded_element: &BlindedElement, @@ -257,7 +258,7 @@ where let PoprfServerBatchEvaluatePrepareResult { mut prepared_evaluation_elements, prepared_tweak, - } = self.batch_evaluate_prepare(iter::once(blinded_element), info)?; + } = self.batch_blind_evaluate_prepare(iter::once(blinded_element), info)?; let prepared_evaluation_element = prepared_evaluation_elements.next().unwrap(); let prepared_evaluation_elements = core::array::from_ref(&prepared_evaluation_element); @@ -265,7 +266,7 @@ where let PoprfServerBatchEvaluateFinishResult { mut messages, proof, - } = Self::batch_evaluate_finish( + } = Self::batch_blind_evaluate_finish( rng, iter::once(blinded_element), prepared_evaluation_elements, @@ -286,7 +287,7 @@ where /// - [`Error::Info`] if the `info` is longer than `u16::MAX`. /// - [`Error::Protocol`] if the protocol fails and can't be completed. #[cfg(feature = "alloc")] - pub fn batch_evaluate<'a, R: RngCore + CryptoRng, IE>( + pub fn batch_blind_evaluate<'a, R: RngCore + CryptoRng, IE>( &self, rng: &mut R, blinded_elements: &'a IE, @@ -300,13 +301,13 @@ where let PoprfServerBatchEvaluatePrepareResult { prepared_evaluation_elements, prepared_tweak, - } = self.batch_evaluate_prepare(blinded_elements.into_iter(), info)?; + } = self.batch_blind_evaluate_prepare(blinded_elements.into_iter(), info)?; let prepared_evaluation_elements: Vec<_> = prepared_evaluation_elements.collect(); // This can't fail because we know the size of the inputs. let PoprfServerBatchEvaluateFinishResult { messages, proof } = - Self::batch_evaluate_finish::<_, _, Vec<_>>( + Self::batch_blind_evaluate_finish::<_, _, Vec<_>>( rng, blinded_elements.into_iter(), &prepared_evaluation_elements, @@ -319,15 +320,15 @@ where Ok(PoprfServerBatchEvaluateResult { messages, proof }) } - /// Alternative version of `batch_evaluate` without + /// Alternative version of `batch_blind_evaluate` without /// memory allocation. Returned [`PreparedEvaluationElement`] have to /// be [`collect`](Iterator::collect)ed and passed into - /// [`batch_evaluate_finish`](Self::batch_evaluate_finish). + /// [`batch_blind_evaluate_finish`](Self::batch_blind_evaluate_finish). /// /// # Errors /// - [`Error::Info`] if the `info` is longer than `u16::MAX`. /// - [`Error::Protocol`] if the protocol fails and can't be completed. - pub fn batch_evaluate_prepare<'a, I: Iterator>>( + pub fn batch_blind_evaluate_prepare<'a, I: Iterator>>( &self, blinded_elements: I, info: Option<&[u8]>, @@ -349,14 +350,14 @@ where }) } - /// See [`batch_evaluate_prepare`](Self::batch_evaluate_prepare) for more - /// details. + /// See [`batch_blind_evaluate_prepare`](Self::batch_blind_evaluate_prepare) + /// for more details. /// /// # Errors /// [`Error::Batch`] if the number of `blinded_elements` and /// `prepared_evaluation_elements` don't match or is longer then /// [`u16::MAX`] - pub fn batch_evaluate_finish< + pub fn batch_blind_evaluate_finish< 'a, 'b, R: RngCore + CryptoRng, @@ -398,6 +399,29 @@ where Ok(PoprfServerBatchEvaluateFinishResult { messages, proof }) } + /// Computes the output of the VOPRF on the server side + /// + /// # Errors + /// [`Error::Input`] if the `input` is longer then [`u16::MAX`]. + pub fn evaluate( + &self, + input: &[u8], + info: Option<&[u8]>, + ) -> Result::Hash>> { + let input_element = hash_to_group::(input, Mode::Poprf)?; + if CS::Group::is_identity_elem(input_element).into() { + return Err(Error::Input); + }; + + let tweak = compute_tweak::(self.sk, info)?; + + let evaluated_element = input_element * &CS::Group::invert_scalar(tweak); + + let issued_element = CS::Group::serialize_elem(evaluated_element); + + server_evaluate_hash_input::(input, info, issued_element) + } + /// Retrieves the server's public key pub fn get_public_key(&self) -> ::Elem { self.pk @@ -808,9 +832,9 @@ mod tests { let info = b"info"; let mut rng = OsRng; let server = PoprfServer::::new(&mut rng).unwrap(); - let client_blind_result = PoprfClient::::blind(&mut rng, input).unwrap(); + let client_blind_result = PoprfClient::::blind(input, &mut rng).unwrap(); let server_result = server - .evaluate(&mut rng, &client_blind_result.message, Some(info)) + .blind_evaluate(&mut rng, &client_blind_result.message, Some(info)) .unwrap(); let client_finalize_result = client_blind_result .state @@ -835,9 +859,9 @@ mod tests { let info = b"info"; let mut rng = OsRng; let server = PoprfServer::::new(&mut rng).unwrap(); - let client_blind_result = PoprfClient::::blind(&mut rng, input).unwrap(); + let client_blind_result = PoprfClient::::blind(input, &mut rng).unwrap(); let server_result = server - .evaluate(&mut rng, &client_blind_result.message, Some(info)) + .blind_evaluate(&mut rng, &client_blind_result.message, Some(info)) .unwrap(); let wrong_pk = { let dst = GenericArray::from(STR_HASH_TO_GROUP) @@ -855,6 +879,43 @@ mod tests { assert!(client_finalize_result.is_err()); } + fn verifiable_server_evaluate() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { + let input = b"input"; + let info = Some(b"info".as_slice()); + let mut rng = OsRng; + let client_blind_result = PoprfClient::::blind(input, &mut rng).unwrap(); + let server = PoprfServer::::new(&mut rng).unwrap(); + let server_result = server + .blind_evaluate(&mut rng, &client_blind_result.message, info) + .unwrap(); + + let client_finalize = client_blind_result + .state + .finalize( + input, + &server_result.message, + &server_result.proof, + server.get_public_key(), + info, + ) + .unwrap(); + + // We expect the outputs from client and server to be equal given an identical + // input + let server_evaluate = server.evaluate(input, info).unwrap(); + assert_eq!(client_finalize, server_evaluate); + + // We expect the outputs from client and server to be different given different + // inputs + let wrong_input = b"wrong input"; + let server_evaluate = server.evaluate(wrong_input, info).unwrap(); + assert!(client_finalize != server_evaluate); + } + fn zeroize_verifiable_client() where ::OutputSize: @@ -864,7 +925,7 @@ mod tests { { let input = b"input"; let mut rng = OsRng; - let client_blind_result = PoprfClient::::blind(&mut rng, input).unwrap(); + let client_blind_result = PoprfClient::::blind(input, &mut rng).unwrap(); let mut state = client_blind_result.state; unsafe { ptr::drop_in_place(&mut state) }; @@ -888,9 +949,9 @@ mod tests { let info = b"info"; let mut rng = OsRng; let server = PoprfServer::::new(&mut rng).unwrap(); - let client_blind_result = PoprfClient::::blind(&mut rng, input).unwrap(); + let client_blind_result = PoprfClient::::blind(input, &mut rng).unwrap(); let server_result = server - .evaluate(&mut rng, &client_blind_result.message, Some(info)) + .blind_evaluate(&mut rng, &client_blind_result.message, Some(info)) .unwrap(); let mut state = server; @@ -916,6 +977,7 @@ mod tests { verifiable_retrieval::(); verifiable_bad_public_key::(); + verifiable_server_evaluate::(); zeroize_verifiable_client::(); zeroize_verifiable_server::(); @@ -923,6 +985,7 @@ mod tests { verifiable_retrieval::(); verifiable_bad_public_key::(); + verifiable_server_evaluate::(); zeroize_verifiable_client::(); zeroize_verifiable_server::(); diff --git a/src/tests/test_cfrg_vectors.rs b/src/tests/test_cfrg_vectors.rs index 191ee69..9fe23bf 100644 --- a/src/tests/test_cfrg_vectors.rs +++ b/src/tests/test_cfrg_vectors.rs @@ -101,8 +101,9 @@ fn test_vectors() -> Result<()> { assert_ne!(ristretto_oprf_tvs.len(), 0); test_oprf_seed_to_key::(&ristretto_oprf_tvs)?; test_oprf_blind::(&ristretto_oprf_tvs)?; - test_oprf_evaluate::(&ristretto_oprf_tvs)?; + test_oprf_blind_evaluate::(&ristretto_oprf_tvs)?; test_oprf_finalize::(&ristretto_oprf_tvs)?; + test_oprf_evaluate::(&ristretto_oprf_tvs)?; let ristretto_voprf_tvs = json_to_test_vectors!( rfc, @@ -112,8 +113,9 @@ fn test_vectors() -> Result<()> { assert_ne!(ristretto_voprf_tvs.len(), 0); test_voprf_seed_to_key::(&ristretto_voprf_tvs)?; test_voprf_blind::(&ristretto_voprf_tvs)?; - test_voprf_evaluate::(&ristretto_voprf_tvs)?; + test_voprf_blind_evaluate::(&ristretto_voprf_tvs)?; test_voprf_finalize::(&ristretto_voprf_tvs)?; + test_voprf_evaluate::(&ristretto_voprf_tvs)?; let ristretto_poprf_tvs = json_to_test_vectors!( rfc, @@ -123,8 +125,9 @@ fn test_vectors() -> Result<()> { assert_ne!(ristretto_poprf_tvs.len(), 0); test_poprf_seed_to_key::(&ristretto_poprf_tvs)?; test_poprf_blind::(&ristretto_poprf_tvs)?; - test_poprf_evaluate::(&ristretto_poprf_tvs)?; + test_poprf_blind_evaluate::(&ristretto_poprf_tvs)?; test_poprf_finalize::(&ristretto_poprf_tvs)?; + test_poprf_evaluate::(&ristretto_poprf_tvs)?; } let p256_oprf_tvs = @@ -132,24 +135,27 @@ fn test_vectors() -> Result<()> { assert_ne!(p256_oprf_tvs.len(), 0); test_oprf_seed_to_key::(&p256_oprf_tvs)?; test_oprf_blind::(&p256_oprf_tvs)?; - test_oprf_evaluate::(&p256_oprf_tvs)?; + test_oprf_blind_evaluate::(&p256_oprf_tvs)?; test_oprf_finalize::(&p256_oprf_tvs)?; + test_oprf_evaluate::(&p256_oprf_tvs)?; let p256_voprf_tvs = json_to_test_vectors!(rfc, String::from("P-256, SHA-256"), String::from("VOPRF")); assert_ne!(p256_voprf_tvs.len(), 0); test_voprf_seed_to_key::(&p256_voprf_tvs)?; test_voprf_blind::(&p256_voprf_tvs)?; - test_voprf_evaluate::(&p256_voprf_tvs)?; + test_voprf_blind_evaluate::(&p256_voprf_tvs)?; test_voprf_finalize::(&p256_voprf_tvs)?; + test_voprf_evaluate::(&p256_voprf_tvs)?; let p256_poprf_tvs = json_to_test_vectors!(rfc, String::from("P-256, SHA-256"), String::from("POPRF")); assert_ne!(p256_poprf_tvs.len(), 0); test_poprf_seed_to_key::(&p256_poprf_tvs)?; test_poprf_blind::(&p256_poprf_tvs)?; - test_poprf_evaluate::(&p256_poprf_tvs)?; + test_poprf_blind_evaluate::(&p256_poprf_tvs)?; test_poprf_finalize::(&p256_poprf_tvs)?; + test_poprf_evaluate::(&p256_poprf_tvs)?; Ok(()) } @@ -286,7 +292,7 @@ where } // Tests sksm, blinded_element -> evaluation_element -fn test_oprf_evaluate(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +fn test_oprf_blind_evaluate(tvs: &[VOPRFTestVectorParameters]) -> Result<()> where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, @@ -294,7 +300,7 @@ where for parameters in tvs { for i in 0..parameters.input.len() { let server = OprfServer::::new_with_key(¶meters.sksm)?; - let message = server.evaluate(&BlindedElement::deserialize( + let message = server.blind_evaluate(&BlindedElement::deserialize( ¶meters.blinded_element[i], )?); @@ -307,7 +313,7 @@ where Ok(()) } -fn test_voprf_evaluate(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +fn test_voprf_blind_evaluate(tvs: &[VOPRFTestVectorParameters]) -> Result<()> where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, @@ -323,10 +329,11 @@ where blinded_elements.push(BlindedElement::deserialize(blinded_element_bytes)?); } - let prepared_evaluation_elements = server.batch_evaluate_prepare(blinded_elements.iter()); + let prepared_evaluation_elements = + server.batch_blind_evaluate_prepare(blinded_elements.iter()); let prepared_elements: Vec<_> = prepared_evaluation_elements.collect(); - let VoprfServerBatchEvaluateFinishResult { messages, proof } = - server.batch_evaluate_finish(&mut rng, blinded_elements.iter(), &prepared_elements)?; + let VoprfServerBatchEvaluateFinishResult { messages, proof } = server + .batch_blind_evaluate_finish(&mut rng, blinded_elements.iter(), &prepared_elements)?; let messages: Vec<_> = messages.collect(); for (parameter, message) in parameters.evaluation_element.iter().zip(messages) { @@ -338,7 +345,7 @@ where Ok(()) } -fn test_poprf_evaluate(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +fn test_poprf_blind_evaluate(tvs: &[VOPRFTestVectorParameters]) -> Result<()> where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, @@ -357,10 +364,10 @@ where let PoprfServerBatchEvaluatePrepareResult { prepared_evaluation_elements, prepared_tweak, - } = server.batch_evaluate_prepare(blinded_elements.iter(), Some(¶meters.info))?; + } = server.batch_blind_evaluate_prepare(blinded_elements.iter(), Some(¶meters.info))?; let prepared_evaluation_elements: Vec<_> = prepared_evaluation_elements.collect(); let PoprfServerBatchEvaluateFinishResult { messages, proof } = - PoprfServer::batch_evaluate_finish::<_, _, Vec<_>>( + PoprfServer::batch_blind_evaluate_finish::<_, _, Vec<_>>( &mut rng, blinded_elements.iter(), &prepared_evaluation_elements, @@ -476,3 +483,56 @@ where } Ok(()) } + +// Tests input, sksm -> output +fn test_oprf_evaluate(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + for parameters in tvs { + for i in 0..parameters.input.len() { + let server = OprfServer::::new_with_key(¶meters.sksm)?; + + let server_evaluate_result = server.evaluate(¶meters.input[i])?; + + assert_eq!(¶meters.output[i], &server_evaluate_result.to_vec()); + } + } + Ok(()) +} + +fn test_voprf_evaluate(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + for parameters in tvs { + for i in 0..parameters.input.len() { + let server = VoprfServer::::new_with_key(¶meters.sksm)?; + + let server_evaluate_result = server.evaluate(¶meters.input[i])?; + + assert_eq!(¶meters.output[i], &server_evaluate_result.to_vec()); + } + } + Ok(()) +} + +fn test_poprf_evaluate(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + for parameters in tvs { + for i in 0..parameters.input.len() { + let server = PoprfServer::::new_with_key(¶meters.sksm)?; + + let server_evaluate_result = + server.evaluate(¶meters.input[i], Some(¶meters.info))?; + + assert_eq!(¶meters.output[i], &server_evaluate_result.to_vec()); + } + } + Ok(()) +} diff --git a/src/voprf.rs b/src/voprf.rs index 6f4ab81..4df3b05 100644 --- a/src/voprf.rs +++ b/src/voprf.rs @@ -19,8 +19,9 @@ use generic_array::GenericArray; use rand_core::{CryptoRng, RngCore}; use crate::common::{ - derive_keypair, deterministic_blind_unchecked, generate_proof, i2osp_2, verify_proof, - BlindedElement, EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE, + derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2, + server_evaluate_hash_input, verify_proof, BlindedElement, EvaluationElement, Mode, + PreparedEvaluationElement, Proof, STR_FINALIZE, }; #[cfg(feature = "serde")] use crate::serialization::serde::{Element, Scalar}; @@ -253,13 +254,13 @@ where /// Computes the second step for the multiplicative blinding version of /// DH-OPRF. This message is sent from the server (who holds the OPRF key) /// to the client. - pub fn evaluate( + pub fn blind_evaluate( &self, rng: &mut R, blinded_element: &BlindedElement, ) -> VoprfServerEvaluateResult { let mut prepared_evaluation_elements = - self.batch_evaluate_prepare(iter::once(blinded_element)); + self.batch_blind_evaluate_prepare(iter::once(blinded_element)); let prepared_evaluation_element = [prepared_evaluation_elements.next().unwrap()]; // This can't fail because we know the size of the inputs. @@ -267,7 +268,7 @@ where mut messages, proof, } = self - .batch_evaluate_finish( + .batch_blind_evaluate_finish( rng, iter::once(blinded_element), &prepared_evaluation_element, @@ -286,7 +287,7 @@ where /// [`Error::Batch`] if the number of `blinded_elements` and /// `evaluation_elements` don't match or is longer then [`u16::MAX`] #[cfg(feature = "alloc")] - pub fn batch_evaluate<'a, R: RngCore + CryptoRng, I>( + pub fn batch_blind_evaluate<'a, R: RngCore + CryptoRng, I>( &self, rng: &mut R, blinded_elements: &'a I, @@ -297,10 +298,10 @@ where <&'a I as IntoIterator>::IntoIter: ExactSizeIterator, { let prepared_evaluation_elements = self - .batch_evaluate_prepare(blinded_elements.into_iter()) + .batch_blind_evaluate_prepare(blinded_elements.into_iter()) .collect(); let VoprfServerBatchEvaluateFinishResult { messages, proof } = self - .batch_evaluate_finish::<_, _, Vec<_>>( + .batch_blind_evaluate_finish::<_, _, Vec<_>>( rng, blinded_elements.into_iter(), &prepared_evaluation_elements, @@ -310,11 +311,11 @@ where Ok(VoprfServerBatchEvaluateResult { messages, proof }) } - /// Alternative version of `batch_evaluate` without - /// memory allocation. Returned [`PreparedEvaluationElement`] have to be + /// Alternative version of `batch_blind_evaluate` without memory allocation. + /// Returned [`PreparedEvaluationElement`] have to be /// [`collect`](Iterator::collect)ed and passed into - /// [`batch_evaluate_finish`](Self::batch_evaluate_finish). - pub fn batch_evaluate_prepare<'a, I: Iterator>>( + /// [`batch_blind_evaluate_finish`](Self::batch_blind_evaluate_finish). + pub fn batch_blind_evaluate_prepare<'a, I: Iterator>>( &self, blinded_elements: I, ) -> VoprfServerBatchEvaluatePreparedEvaluationElements @@ -328,13 +329,13 @@ where }) } - /// See [`batch_evaluate_prepare`](Self::batch_evaluate_prepare) for more - /// details. + /// See [`batch_blind_evaluate_prepare`](Self::batch_blind_evaluate_prepare) + /// for more details. /// /// # Errors /// [`Error::Batch`] if the number of `blinded_elements` and /// `evaluation_elements` don't match or is longer then [`u16::MAX`] - pub fn batch_evaluate_finish< + pub fn batch_blind_evaluate_finish< 'a, 'b, R: RngCore + CryptoRng, @@ -371,6 +372,22 @@ where Ok(VoprfServerBatchEvaluateFinishResult { messages, proof }) } + /// Computes the output of the POPRF on the server side + /// + /// # Errors + /// [`Error::Input`] if the `input` is longer then [`u16::MAX`]. + pub fn evaluate(&self, input: &[u8]) -> Result::Hash>> { + let input_element = hash_to_group::(input, Mode::Voprf)?; + if CS::Group::is_identity_elem(input_element).into() { + return Err(Error::Input); + }; + let evaluated_element = input_element * &self.sk; + + let issued_element = CS::Group::serialize_elem(evaluated_element); + + server_evaluate_hash_input::(input, None, issued_element) + } + /// Retrieves the server's public key pub fn get_public_key(&self) -> ::Elem { self.pk @@ -431,7 +448,7 @@ where } /// Concrete type of [`EvaluationElement`]s returned by -/// [`VoprfServer::batch_evaluate_prepare`]. +/// [`VoprfServer::batch_blind_evaluate_prepare`]. pub type VoprfServerBatchEvaluatePreparedEvaluationElements = Map< Zip::Group as Group>::Scalar>>, fn( @@ -608,7 +625,7 @@ mod tests { let mut rng = OsRng; let client_blind_result = VoprfClient::::blind(input, &mut rng).unwrap(); let server = VoprfServer::::new(&mut rng).unwrap(); - let server_result = server.evaluate(&mut rng, &client_blind_result.message); + let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message); let client_finalize_result = client_blind_result .state .finalize( @@ -642,10 +659,10 @@ mod tests { } let server = VoprfServer::::new(&mut rng).unwrap(); let prepared_evaluation_elements: Vec<_> = server - .batch_evaluate_prepare(client_messages.iter()) + .batch_blind_evaluate_prepare(client_messages.iter()) .collect(); let VoprfServerBatchEvaluateFinishResult { messages, proof } = server - .batch_evaluate_finish( + .batch_blind_evaluate_finish( &mut rng, client_messages.iter(), &prepared_evaluation_elements, @@ -690,10 +707,10 @@ mod tests { } let server = VoprfServer::::new(&mut rng).unwrap(); let prepared_evaluation_elements: Vec<_> = server - .batch_evaluate_prepare(client_messages.iter()) + .batch_blind_evaluate_prepare(client_messages.iter()) .collect(); let VoprfServerBatchEvaluateFinishResult { messages, proof } = server - .batch_evaluate_finish( + .batch_blind_evaluate_finish( &mut rng, client_messages.iter(), &prepared_evaluation_elements, @@ -720,7 +737,7 @@ mod tests { let mut rng = OsRng; let client_blind_result = VoprfClient::::blind(input, &mut rng).unwrap(); let server = VoprfServer::::new(&mut rng).unwrap(); - let server_result = server.evaluate(&mut rng, &client_blind_result.message); + let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message); let wrong_pk = { let dst = GenericArray::from(STR_HASH_TO_GROUP) .concat(create_context_string::(Mode::Oprf)); @@ -736,6 +753,39 @@ mod tests { assert!(client_finalize_result.is_err()); } + fn verifiable_server_evaluate() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { + let input = b"input"; + let mut rng = OsRng; + let client_blind_result = VoprfClient::::blind(input, &mut rng).unwrap(); + let server = VoprfServer::::new(&mut rng).unwrap(); + let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message); + + let client_finalize = client_blind_result + .state + .finalize( + input, + &server_result.message, + &server_result.proof, + server.get_public_key(), + ) + .unwrap(); + + // We expect the outputs from client and server to be equal given an identical + // input + let server_evaluate = server.evaluate(input).unwrap(); + assert_eq!(client_finalize, server_evaluate); + + // We expect the outputs from client and server to be different given different + // inputs + let wrong_input = b"wrong input"; + let server_evaluate = server.evaluate(wrong_input).unwrap(); + assert!(client_finalize != server_evaluate); + } + fn zeroize_voprf_client() where ::OutputSize: @@ -769,7 +819,7 @@ mod tests { let mut rng = OsRng; let client_blind_result = VoprfClient::::blind(input, &mut rng).unwrap(); let server = VoprfServer::::new(&mut rng).unwrap(); - let server_result = server.evaluate(&mut rng, &client_blind_result.message); + let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message); let mut state = server; unsafe { ptr::drop_in_place(&mut state) }; @@ -796,6 +846,7 @@ mod tests { verifiable_batch_retrieval::(); verifiable_bad_public_key::(); verifiable_batch_bad_public_key::(); + verifiable_server_evaluate::(); zeroize_voprf_client::(); zeroize_voprf_server::(); @@ -805,6 +856,7 @@ mod tests { verifiable_batch_retrieval::(); verifiable_bad_public_key::(); verifiable_batch_bad_public_key::(); + verifiable_server_evaluate::(); zeroize_voprf_client::(); zeroize_voprf_server::();