Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f670733165 |
@@ -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
@@ -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 = []
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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>();
|
||||
|
||||
@@ -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(¶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<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(¶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<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(¶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<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(¶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<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(¶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(())
|
||||
}
|
||||
|
||||
+75
-23
@@ -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>();
|
||||
|
||||
Reference in New Issue
Block a user