Compare commits

..
Author SHA1 Message Date
raphaelrobertandGitHub f670733165 Updating to draft 11 (#80)
* draft-11

* Fix CI complaints

* Address review comments, CHANGELOG entry, minor fixes
2022-07-09 07:30:20 -04:00
9 changed files with 489 additions and 129 deletions
+8
View File
@@ -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
+1 -1
View File
@@ -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 = []
+1 -1
View File
@@ -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
+56 -7
View File
@@ -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
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::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::<CS, _, _>(Some(k), b, cs, ds, mode)?;
@@ -216,7 +216,7 @@ where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::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::<CS, _, _>(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
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::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 = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
@@ -437,12 +437,61 @@ where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
let hashed_point =
CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst).map_err(|_| Error::Input)?;
let hashed_point = hash_to_group::<CS>(input, mode)?;
Ok(hashed_point * blind)
}
/// Hashes `input` to a point on the curve
pub(crate) fn hash_to_group<CS: CipherSuite>(
input: &[u8],
mode: Mode,
) -> Result<<CS::Group as Group>::Elem>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
CS::Group::hash_to_curve::<CS::Hash>(&[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<CS: CipherSuite>(
input: &[u8],
info: Option<&[u8]>,
issued_element: GenericArray<u8, <<CS as CipherSuite>::Group as Group>::ElemLen>,
) -> Result<Output<CS::Hash>>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::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
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-voprf/>
pub(crate) fn create_context_string<CS: CipherSuite>(mode: Mode) -> GenericArray<u8, U11>
+136 -54
View File
@@ -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::<CipherSuite>::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::<CipherSuite>::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::<CipherSuite>::blind(
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf::OprfServer;
//! # let mut server_rng = OsRng;
//! # let server = OprfServer::<CipherSuite>::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::<CipherSuite>::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::<CipherSuite>::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::<CipherSuite>::blind(
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf::VoprfServer;
//! # let mut server_rng = OsRng;
//! # let server = VoprfServer::<CipherSuite>::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::<CipherSuite>::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::<CipherSuite>::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::<CipherSuite>::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 <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-10.html#name-poprf-public-input>
//! See
//! <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html#name-poprf-public-input>
//! 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]
+51 -5
View File
@@ -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<CS>) -> EvaluationElement<CS> {
pub fn blind_evaluate(&self, blinded_element: &BlindedElement<CS>) -> EvaluationElement<CS> {
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<Output<<CS as CipherSuite>::Hash>> {
let input_element = hash_to_group::<CS>(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::<CS>(input, None, issued_element)
}
}
/////////////////////////
@@ -312,7 +328,7 @@ mod tests {
let mut rng = OsRng;
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = OprfServer::<CS>::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::<CS>(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<CS: CipherSuite>()
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let input = b"input";
let mut rng = OsRng;
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = OprfServer::<CS>::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<CS: CipherSuite>()
where
<CS::Hash as OutputSizeUser>::OutputSize:
@@ -370,7 +414,7 @@ mod tests {
let mut rng = OsRng;
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = OprfServer::<CS>::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::<Ristretto255>();
base_inversion_unsalted::<Ristretto255>();
server_evaluate::<Ristretto255>();
zeroize_oprf_client::<Ristretto255>();
zeroize_oprf_server::<Ristretto255>();
@@ -397,6 +442,7 @@ mod tests {
base_retrieval::<NistP256>();
base_inversion_unsalted::<NistP256>();
server_evaluate::<NistP256>();
zeroize_oprf_client::<NistP256>();
zeroize_oprf_server::<NistP256>();
+86 -23
View File
@@ -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<R: RngCore + CryptoRng>(
blinding_factor_rng: &mut R,
input: &[u8],
blinding_factor_rng: &mut R,
) -> Result<PoprfClientBlindResult<CS>> {
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<R: RngCore + CryptoRng>(
pub fn blind_evaluate<R: RngCore + CryptoRng>(
&self,
rng: &mut R,
blinded_element: &BlindedElement<CS>,
@@ -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<Item = &'a BlindedElement<CS>>>(
pub fn batch_blind_evaluate_prepare<'a, I: Iterator<Item = &'a BlindedElement<CS>>>(
&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<Output<<CS as CipherSuite>::Hash>> {
let input_element = hash_to_group::<CS>(input, Mode::Poprf)?;
if CS::Group::is_identity_elem(input_element).into() {
return Err(Error::Input);
};
let tweak = compute_tweak::<CS>(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::<CS>(input, info, issued_element)
}
/// Retrieves the server's public key
pub fn get_public_key(&self) -> <CS::Group as Group>::Elem {
self.pk
@@ -808,9 +832,9 @@ mod tests {
let info = b"info";
let mut rng = OsRng;
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
let client_blind_result = PoprfClient::<CS>::blind(&mut rng, input).unwrap();
let client_blind_result = PoprfClient::<CS>::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::<CS>::new(&mut rng).unwrap();
let client_blind_result = PoprfClient::<CS>::blind(&mut rng, input).unwrap();
let client_blind_result = PoprfClient::<CS>::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<CS: CipherSuite>()
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let input = b"input";
let info = Some(b"info".as_slice());
let mut rng = OsRng;
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = PoprfServer::<CS>::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<CS: CipherSuite>()
where
<CS::Hash as OutputSizeUser>::OutputSize:
@@ -864,7 +925,7 @@ mod tests {
{
let input = b"input";
let mut rng = OsRng;
let client_blind_result = PoprfClient::<CS>::blind(&mut rng, input).unwrap();
let client_blind_result = PoprfClient::<CS>::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::<CS>::new(&mut rng).unwrap();
let client_blind_result = PoprfClient::<CS>::blind(&mut rng, input).unwrap();
let client_blind_result = PoprfClient::<CS>::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::<Ristretto255>();
verifiable_bad_public_key::<Ristretto255>();
verifiable_server_evaluate::<Ristretto255>();
zeroize_verifiable_client::<Ristretto255>();
zeroize_verifiable_server::<Ristretto255>();
@@ -923,6 +985,7 @@ mod tests {
verifiable_retrieval::<NistP256>();
verifiable_bad_public_key::<NistP256>();
verifiable_server_evaluate::<NistP256>();
zeroize_verifiable_client::<NistP256>();
zeroize_verifiable_server::<NistP256>();
+75 -15
View File
@@ -101,8 +101,9 @@ fn test_vectors() -> Result<()> {
assert_ne!(ristretto_oprf_tvs.len(), 0);
test_oprf_seed_to_key::<Ristretto255>(&ristretto_oprf_tvs)?;
test_oprf_blind::<Ristretto255>(&ristretto_oprf_tvs)?;
test_oprf_evaluate::<Ristretto255>(&ristretto_oprf_tvs)?;
test_oprf_blind_evaluate::<Ristretto255>(&ristretto_oprf_tvs)?;
test_oprf_finalize::<Ristretto255>(&ristretto_oprf_tvs)?;
test_oprf_evaluate::<Ristretto255>(&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::<Ristretto255>(&ristretto_voprf_tvs)?;
test_voprf_blind::<Ristretto255>(&ristretto_voprf_tvs)?;
test_voprf_evaluate::<Ristretto255>(&ristretto_voprf_tvs)?;
test_voprf_blind_evaluate::<Ristretto255>(&ristretto_voprf_tvs)?;
test_voprf_finalize::<Ristretto255>(&ristretto_voprf_tvs)?;
test_voprf_evaluate::<Ristretto255>(&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::<Ristretto255>(&ristretto_poprf_tvs)?;
test_poprf_blind::<Ristretto255>(&ristretto_poprf_tvs)?;
test_poprf_evaluate::<Ristretto255>(&ristretto_poprf_tvs)?;
test_poprf_blind_evaluate::<Ristretto255>(&ristretto_poprf_tvs)?;
test_poprf_finalize::<Ristretto255>(&ristretto_poprf_tvs)?;
test_poprf_evaluate::<Ristretto255>(&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::<NistP256>(&p256_oprf_tvs)?;
test_oprf_blind::<NistP256>(&p256_oprf_tvs)?;
test_oprf_evaluate::<NistP256>(&p256_oprf_tvs)?;
test_oprf_blind_evaluate::<NistP256>(&p256_oprf_tvs)?;
test_oprf_finalize::<NistP256>(&p256_oprf_tvs)?;
test_oprf_evaluate::<NistP256>(&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::<NistP256>(&p256_voprf_tvs)?;
test_voprf_blind::<NistP256>(&p256_voprf_tvs)?;
test_voprf_evaluate::<NistP256>(&p256_voprf_tvs)?;
test_voprf_blind_evaluate::<NistP256>(&p256_voprf_tvs)?;
test_voprf_finalize::<NistP256>(&p256_voprf_tvs)?;
test_voprf_evaluate::<NistP256>(&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::<NistP256>(&p256_poprf_tvs)?;
test_poprf_blind::<NistP256>(&p256_poprf_tvs)?;
test_poprf_evaluate::<NistP256>(&p256_poprf_tvs)?;
test_poprf_blind_evaluate::<NistP256>(&p256_poprf_tvs)?;
test_poprf_finalize::<NistP256>(&p256_poprf_tvs)?;
test_poprf_evaluate::<NistP256>(&p256_poprf_tvs)?;
Ok(())
}
@@ -286,7 +292,7 @@ where
}
// Tests sksm, blinded_element -> evaluation_element
fn test_oprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
fn test_oprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
@@ -294,7 +300,7 @@ where
for parameters in tvs {
for i in 0..parameters.input.len() {
let server = OprfServer::<CS>::new_with_key(&parameters.sksm)?;
let message = server.evaluate(&BlindedElement::deserialize(
let message = server.blind_evaluate(&BlindedElement::deserialize(
&parameters.blinded_element[i],
)?);
@@ -307,7 +313,7 @@ where
Ok(())
}
fn test_voprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
fn test_voprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::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<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
fn test_poprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
@@ -357,10 +364,10 @@ where
let PoprfServerBatchEvaluatePrepareResult {
prepared_evaluation_elements,
prepared_tweak,
} = server.batch_evaluate_prepare(blinded_elements.iter(), Some(&parameters.info))?;
} = server.batch_blind_evaluate_prepare(blinded_elements.iter(), Some(&parameters.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<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
for parameters in tvs {
for i in 0..parameters.input.len() {
let server = OprfServer::<CS>::new_with_key(&parameters.sksm)?;
let server_evaluate_result = server.evaluate(&parameters.input[i])?;
assert_eq!(&parameters.output[i], &server_evaluate_result.to_vec());
}
}
Ok(())
}
fn test_voprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
for parameters in tvs {
for i in 0..parameters.input.len() {
let server = VoprfServer::<CS>::new_with_key(&parameters.sksm)?;
let server_evaluate_result = server.evaluate(&parameters.input[i])?;
assert_eq!(&parameters.output[i], &server_evaluate_result.to_vec());
}
}
Ok(())
}
fn test_poprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
for parameters in tvs {
for i in 0..parameters.input.len() {
let server = PoprfServer::<CS>::new_with_key(&parameters.sksm)?;
let server_evaluate_result =
server.evaluate(&parameters.input[i], Some(&parameters.info))?;
assert_eq!(&parameters.output[i], &server_evaluate_result.to_vec());
}
}
Ok(())
}
+75 -23
View File
@@ -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<R: RngCore + CryptoRng>(
pub fn blind_evaluate<R: RngCore + CryptoRng>(
&self,
rng: &mut R,
blinded_element: &BlindedElement<CS>,
) -> VoprfServerEvaluateResult<CS> {
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<Item = &'a BlindedElement<CS>>>(
/// [`batch_blind_evaluate_finish`](Self::batch_blind_evaluate_finish).
pub fn batch_blind_evaluate_prepare<'a, I: Iterator<Item = &'a BlindedElement<CS>>>(
&self,
blinded_elements: I,
) -> VoprfServerBatchEvaluatePreparedEvaluationElements<CS, I>
@@ -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<Output<<CS as CipherSuite>::Hash>> {
let input_element = hash_to_group::<CS>(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::<CS>(input, None, issued_element)
}
/// Retrieves the server's public key
pub fn get_public_key(&self) -> <CS::Group as Group>::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<CS, I> = Map<
Zip<I, Repeat<<<CS as CipherSuite>::Group as Group>::Scalar>>,
fn(
@@ -608,7 +625,7 @@ mod tests {
let mut rng = OsRng;
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = VoprfServer::<CS>::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::<CS>::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::<CS>::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::<CS>::blind(input, &mut rng).unwrap();
let server = VoprfServer::<CS>::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::<CS>(Mode::Oprf));
@@ -736,6 +753,39 @@ mod tests {
assert!(client_finalize_result.is_err());
}
fn verifiable_server_evaluate<CS: CipherSuite>()
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let input = b"input";
let mut rng = OsRng;
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = VoprfServer::<CS>::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<CS: CipherSuite>()
where
<CS::Hash as OutputSizeUser>::OutputSize:
@@ -769,7 +819,7 @@ mod tests {
let mut rng = OsRng;
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = VoprfServer::<CS>::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::<Ristretto255>();
verifiable_bad_public_key::<Ristretto255>();
verifiable_batch_bad_public_key::<Ristretto255>();
verifiable_server_evaluate::<Ristretto255>();
zeroize_voprf_client::<Ristretto255>();
zeroize_voprf_server::<Ristretto255>();
@@ -805,6 +856,7 @@ mod tests {
verifiable_batch_retrieval::<NistP256>();
verifiable_bad_public_key::<NistP256>();
verifiable_batch_bad_public_key::<NistP256>();
verifiable_server_evaluate::<NistP256>();
zeroize_voprf_client::<NistP256>();
zeroize_voprf_server::<NistP256>();