From 06139e37e6178097ac7cd050872236d42ee9c479 Mon Sep 17 00:00:00 2001 From: Kevin Lewi Date: Sun, 13 Feb 2022 03:50:40 -0800 Subject: [PATCH] Syncing new test vectors and base mode (#58) * Syncing new test vectors and base mode * Working set of test vectors for VOPRF mode * Adding POPRF * POPRF test vectors in sync --- Cargo.toml | 3 +- src/error.rs | 6 +- src/group/elliptic_curve.rs | 17 +- src/group/mod.rs | 31 +- src/group/ristretto.rs | 17 +- src/lib.rs | 149 ++-- src/oprf.rs | 419 +++++++++++ src/poprf.rs | 717 ++++++++++++++++++ src/serialization.rs | 96 ++- src/tests/cfrg_vectors.rs | 1034 +++++++++++++++++++++++++ src/tests/mod.rs | 4 +- src/tests/test_cfrg_vectors.rs | 465 ++++++++++++ src/tests/voprf_test_vectors.rs | 358 --------- src/tests/voprf_vectors.rs | 637 ---------------- src/util.rs | 450 ++++++++++- src/voprf.rs | 1248 ++++--------------------------- 16 files changed, 3414 insertions(+), 2237 deletions(-) create mode 100644 src/oprf.rs create mode 100644 src/poprf.rs create mode 100644 src/tests/cfrg_vectors.rs create mode 100644 src/tests/test_cfrg_vectors.rs delete mode 100644 src/tests/voprf_test_vectors.rs delete mode 100644 src/tests/voprf_vectors.rs diff --git a/Cargo.toml b/Cargo.toml index da13b3a..62bc0d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ version = "0.3.0" [features] alloc = [] danger = [] -default = ["ristretto255-ciphersuite", "ristretto255-u64", "serde"] +default = ["ristretto255-ciphersuite", "ristretto255-u64", "serde", "std"] ristretto255 = ["generic-array/more_lengths"] ristretto255-ciphersuite = ["ristretto255", "sha2"] ristretto255-fiat-u32 = ["curve25519-dalek/fiat_u32_backend", "ristretto255"] @@ -26,6 +26,7 @@ serde = ["generic-array/serde", "serde_"] std = ["alloc"] [dependencies] +hex = "0.4" curve25519-dalek = { version = "3", default-features = false, optional = true } derive-where = { version = "=1.0.0-rc.2", features = ["zeroize-on-drop"] } digest = "0.10" diff --git a/src/error.rs b/src/error.rs index 38a3240..c17320c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -17,8 +17,8 @@ pub type Result = core::result::Result; pub enum Error { /// Size of input is empty or longer then [`u16::MAX`]. Input, - /// Size of metadata is longer then `u16::MAX - 21`. - Metadata, + /// Size of info is longer then `u16::MAX - 21`. + Info, /// Failure to deserialize bytes Deserialization, /// Batched items are more then [`u16::MAX`] or length don't match. @@ -29,6 +29,8 @@ pub enum Error { Seed, /// The protocol has failed and can't be completed. Protocol, + /// Issue with deriving a keypair + DeriveKeyPair, } /// Only used to implement [`Group`](crate::Group). diff --git a/src/group/elliptic_curve.rs b/src/group/elliptic_curve.rs index 3b2b354..22e49e6 100644 --- a/src/group/elliptic_curve.rs +++ b/src/group/elliptic_curve.rs @@ -19,9 +19,9 @@ use generic_array::GenericArray; use rand_core::{CryptoRng, RngCore}; use super::Group; -use crate::group::{STR_HASH_TO_GROUP, STR_HASH_TO_SCALAR}; -use crate::voprf::{self, Mode}; -use crate::{CipherSuite, Error, InternalError, Result}; +use crate::group::STR_HASH_TO_GROUP; +use crate::util::Mode; +use crate::{util, CipherSuite, Error, InternalError, Result}; impl Group for C where @@ -50,25 +50,22 @@ where IsLess + IsLessOrEqual<::BlockSize>, { let dst = - GenericArray::from(STR_HASH_TO_GROUP).concat(voprf::get_context_string::(mode)); + GenericArray::from(STR_HASH_TO_GROUP).concat(util::create_context_string::(mode)); Self::hash_from_bytes::>(input, &dst) .map_err(|_| InternalError::Input) } // Implements the `HashToScalar()` function - fn hash_to_scalar( + fn hash_to_scalar_with_dst( input: &[&[u8]], - mode: Mode, + dst: &[u8], ) -> Result where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { - let dst = - GenericArray::from(STR_HASH_TO_SCALAR).concat(voprf::get_context_string::(mode)); - - ::hash_to_scalar::>(input, &dst) + ::hash_to_scalar::>(input, dst) .map_err(|_| InternalError::Input) } diff --git a/src/group/mod.rs b/src/group/mod.rs index 7b25d0c..eed8ec0 100644 --- a/src/group/mod.rs +++ b/src/group/mod.rs @@ -15,6 +15,7 @@ use core::ops::{Add, Mul, Sub}; use digest::core_api::BlockSizeUser; use digest::OutputSizeUser; +use generic_array::sequence::Concat; use generic_array::typenum::{IsLess, IsLessOrEqual, U256}; use generic_array::{ArrayLength, GenericArray}; use rand_core::{CryptoRng, RngCore}; @@ -23,7 +24,7 @@ pub use ristretto::Ristretto255; use subtle::{Choice, ConstantTimeEq}; use zeroize::Zeroize; -use crate::voprf::Mode; +use crate::util::Mode; use crate::{CipherSuite, InternalError, Result}; pub(crate) const STR_HASH_TO_SCALAR: [u8; 13] = *b"HashToScalar-"; @@ -33,7 +34,8 @@ pub(crate) const STR_HASH_TO_GROUP: [u8; 12] = *b"HashToGroup-"; /// subgroup is noted additively — as in the draft RFC — in this trait. pub trait Group { /// The type of group elements - type Elem: Copy + type Elem: ConstantTimeEq + + Copy + Zeroize + for<'a> Add<&'a Self::Elem, Output = Self::Elem> + for<'a> Mul<&'a Self::Scalar, Output = Self::Elem>; @@ -74,6 +76,26 @@ pub trait Group { input: &[&[u8]], mode: Mode, ) -> Result + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { + let dst = GenericArray::from(STR_HASH_TO_SCALAR) + .concat(crate::util::create_context_string::(mode)); + + Self::hash_to_scalar_with_dst::(input, &dst) + } + + /// Hashes a slice of pseudo-random bytes to a scalar, allowing for + /// specifying a custom domain separation tag (DST) + /// + /// # Errors + /// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer + /// then [`u16::MAX`]. + fn hash_to_scalar_with_dst( + input: &[&[u8]], + dst: &[u8], + ) -> Result where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>; @@ -84,6 +106,11 @@ pub trait Group { /// Returns the identity group element fn identity_elem() -> Self::Elem; + /// Returns `true` if the element is equal to the identity element + fn is_identity_elem(elem: Self::Elem) -> Choice { + Self::identity_elem().ct_eq(&elem) + } + /// Serializes the `self` group element fn serialize_elem(elem: Self::Elem) -> GenericArray; diff --git a/src/group/ristretto.rs b/src/group/ristretto.rs index 6e8bc15..5b9b280 100644 --- a/src/group/ristretto.rs +++ b/src/group/ristretto.rs @@ -18,9 +18,9 @@ use generic_array::GenericArray; use rand_core::{CryptoRng, RngCore}; use subtle::ConstantTimeEq; -use super::{Group, STR_HASH_TO_GROUP, STR_HASH_TO_SCALAR}; -use crate::voprf::{self, Mode}; -use crate::{CipherSuite, Error, InternalError, Result}; +use super::{Group, STR_HASH_TO_GROUP}; +use crate::util::Mode; +use crate::{util, CipherSuite, Error, InternalError, Result}; /// [`Group`] implementation for Ristretto255. #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -59,7 +59,7 @@ impl Group for Ristretto255 { IsLess + IsLessOrEqual<::BlockSize>, { let dst = - GenericArray::from(STR_HASH_TO_GROUP).concat(voprf::get_context_string::(mode)); + GenericArray::from(STR_HASH_TO_GROUP).concat(util::create_context_string::(mode)); let mut uniform_bytes = GenericArray::<_, U64>::default(); ExpandMsgXmd::::expand_message(input, &dst, 64) @@ -71,19 +71,16 @@ impl Group for Ristretto255 { // Implements the `HashToScalar()` function from // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.1 - fn hash_to_scalar<'a, CS: CipherSuite>( + fn hash_to_scalar_with_dst<'a, CS: CipherSuite>( input: &[&[u8]], - mode: Mode, + dst: &[u8], ) -> Result where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { - let dst = - GenericArray::from(STR_HASH_TO_SCALAR).concat(voprf::get_context_string::(mode)); - let mut uniform_bytes = GenericArray::<_, U64>::default(); - ExpandMsgXmd::::expand_message(input, &dst, 64) + ExpandMsgXmd::::expand_message(input, dst, 64) .map_err(|_| InternalError::Input)? .fill_bytes(&mut uniform_bytes); diff --git a/src/lib.rs b/src/lib.rs index 2c88f6d..fa83970 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,13 +40,13 @@ //! //! ## Base Mode //! -//! In base mode, a [NonVerifiableClient] interacts with a [NonVerifiableServer] +//! In base mode, a [OprfClient] interacts with a [OprfServer] //! to compute the output of the VOPRF. //! //! ### Server Setup //! //! The protocol begins with a setup phase, in which the server must run -//! [NonVerifiableServer::new()] to produce an instance of itself. This instance +//! [OprfServer::new()] to produce an instance of itself. This instance //! must be persisted on the server and used for online client evaluations. //! //! ``` @@ -56,18 +56,18 @@ //! # type CipherSuite = p256::NistP256; //! use rand::rngs::OsRng; //! use rand::RngCore; -//! use voprf::NonVerifiableServer; +//! use voprf::OprfServer; //! //! let mut server_rng = OsRng; -//! let server = NonVerifiableServer::::new(&mut server_rng); +//! let server = OprfServer::::new(&mut server_rng); //! ``` //! //! ### Client Blinding //! //! In the first step, the client chooses an input, and runs -//! [NonVerifiableClient::blind] to produce a [NonVerifiableClientBlindResult], +//! [OprfClient::blind] to produce a [OprfClientBlindResult], //! which consists of a [BlindedElement] to be sent to the server and a -//! [NonVerifiableClient] which must be persisted on the client for the final +//! [OprfClient] which must be persisted on the client for the final //! step of the VOPRF protocol. //! //! ``` @@ -77,18 +77,18 @@ //! # type CipherSuite = p256::NistP256; //! use rand::rngs::OsRng; //! use rand::RngCore; -//! use voprf::NonVerifiableClient; +//! use voprf::OprfClient; //! //! let mut client_rng = OsRng; -//! let client_blind_result = NonVerifiableClient::::blind(b"input", &mut client_rng) +//! let client_blind_result = OprfClient::::blind(b"input", &mut client_rng) //! .expect("Unable to construct client"); //! ``` //! //! ### Server Evaluation //! //! In the second step, the server takes as input the message from -//! [NonVerifiableClient::blind] (a [BlindedElement]), and runs -//! [NonVerifiableServer::evaluate] to produce [EvaluationElement] to be sent to +//! [OprfClient::blind] (a [BlindedElement]), and runs +//! [OprfServer::evaluate] to produce [EvaluationElement] to be sent to //! the client. //! //! ``` @@ -96,44 +96,44 @@ //! # type CipherSuite = voprf::Ristretto255; //! # #[cfg(not(feature = "ristretto255"))] //! # type CipherSuite = p256::NistP256; -//! # use voprf::NonVerifiableClient; +//! # use voprf::OprfClient; //! # use rand::{rngs::OsRng, RngCore}; //! # //! # let mut client_rng = OsRng; -//! # let client_blind_result = NonVerifiableClient::::blind( +//! # let client_blind_result = OprfClient::::blind( //! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); -//! # use voprf::NonVerifiableServer; +//! # use voprf::OprfServer; //! # let mut server_rng = OsRng; -//! # let server = NonVerifiableServer::::new(&mut server_rng); +//! # let server = OprfServer::::new(&mut server_rng); //! let server_evaluate_result = server -//! .evaluate(&client_blind_result.message, None) +//! .evaluate(&client_blind_result.message) //! .expect("Unable to perform server evaluate"); //! ``` //! //! ### Client Finalization //! //! In the final step, the client takes as input the message from -//! [NonVerifiableServer::evaluate] (an [EvaluationElement]), and runs -//! [NonVerifiableClient::finalize] to produce an output for the protocol. +//! [OprfServer::evaluate] (an [EvaluationElement]), and runs +//! [OprfClient::finalize] to produce an output for the protocol. //! //! ``` //! # #[cfg(feature = "ristretto255")] //! # type CipherSuite = voprf::Ristretto255; //! # #[cfg(not(feature = "ristretto255"))] //! # type CipherSuite = p256::NistP256; -//! # use voprf::NonVerifiableClient; +//! # use voprf::OprfClient; //! # use rand::{rngs::OsRng, RngCore}; //! # //! # let mut client_rng = OsRng; -//! # let client_blind_result = NonVerifiableClient::::blind( +//! # let client_blind_result = OprfClient::::blind( //! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); -//! # use voprf::NonVerifiableServer; +//! # use voprf::OprfServer; //! # let mut server_rng = OsRng; -//! # let server = NonVerifiableServer::::new(&mut server_rng); +//! # let server = OprfServer::::new(&mut server_rng); //! # let message = server.evaluate( //! # &client_blind_result.message, //! # None, @@ -148,7 +148,7 @@ //! //! ## Verifiable Mode //! -//! In verifiable mode, a [VerifiableClient] interacts with a [VerifiableServer] +//! 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 @@ -161,7 +161,7 @@ //! ### Server Setup //! //! The protocol begins with a setup phase, in which the server must run -//! [VerifiableServer::new()] to produce an instance of itself. This instance +//! [VoprfServer::new()] to produce an instance of itself. This instance //! must be persisted on the server and used for online client evaluations. //! //! ``` @@ -171,10 +171,10 @@ //! # type CipherSuite = p256::NistP256; //! use rand::rngs::OsRng; //! use rand::RngCore; -//! use voprf::VerifiableServer; +//! use voprf::VoprfServer; //! //! let mut server_rng = OsRng; -//! let server = VerifiableServer::::new(&mut server_rng); +//! let server = VoprfServer::::new(&mut server_rng); //! //! // To be sent to the client //! println!("Server public key: {:?}", server.get_public_key()); @@ -187,9 +187,9 @@ //! ### Client Blinding //! //! In the first step, the client chooses an input, and runs -//! [VerifiableClient::blind] to produce a [VerifiableClientBlindResult], which +//! [VoprfClient::blind] to produce a [VoprfClientBlindResult], which //! consists of a [BlindedElement] to be sent to the server and a -//! [VerifiableClient] which must be persisted on the client for the final step +//! [VoprfClient] which must be persisted on the client for the final step //! of the VOPRF protocol. //! //! ``` @@ -199,18 +199,18 @@ //! # type CipherSuite = p256::NistP256; //! use rand::rngs::OsRng; //! use rand::RngCore; -//! use voprf::VerifiableClient; +//! use voprf::VoprfClient; //! //! let mut client_rng = OsRng; -//! let client_blind_result = VerifiableClient::::blind(b"input", &mut client_rng) +//! let client_blind_result = VoprfClient::::blind(b"input", &mut client_rng) //! .expect("Unable to construct client"); //! ``` //! //! ### Server Evaluation //! //! In the second step, the server takes as input the message from -//! [VerifiableClient::blind] (a [BlindedElement]), and runs -//! [VerifiableServer::evaluate] to produce a [VerifiableServerEvaluateResult], +//! [VoprfClient::blind] (a [BlindedElement]), and runs +//! [VoprfServer::evaluate] to produce a [VoprfServerEvaluateResult], //! which consists of an [EvaluationElement] to be sent to the client along with //! a proof. //! @@ -219,17 +219,17 @@ //! # type CipherSuite = voprf::Ristretto255; //! # #[cfg(not(feature = "ristretto255"))] //! # type CipherSuite = p256::NistP256; -//! # use voprf::VerifiableClient; +//! # use voprf::VoprfClient; //! # use rand::{rngs::OsRng, RngCore}; //! # //! # let mut client_rng = OsRng; -//! # let client_blind_result = VerifiableClient::::blind( +//! # let client_blind_result = VoprfClient::::blind( //! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); -//! # use voprf::VerifiableServer; +//! # use voprf::VoprfServer; //! # let mut server_rng = OsRng; -//! # let server = VerifiableServer::::new(&mut server_rng); +//! # let server = VoprfServer::::new(&mut server_rng); //! let server_evaluate_result = server //! .evaluate(&mut server_rng, &client_blind_result.message, None) //! .expect("Unable to perform server evaluate"); @@ -238,8 +238,8 @@ //! ### Client Finalization //! //! In the final step, the client takes as input the message from -//! [VerifiableServer::evaluate] (an [EvaluationElement]), the proof, and the -//! server's public key, and runs [VerifiableClient::finalize] to produce an +//! [VoprfServer::evaluate] (an [EvaluationElement]), the proof, and the +//! server's public key, and runs [VoprfClient::finalize] to produce an //! output for the protocol. //! //! ``` @@ -247,17 +247,17 @@ //! # type CipherSuite = voprf::Ristretto255; //! # #[cfg(not(feature = "ristretto255"))] //! # type CipherSuite = p256::NistP256; -//! # use voprf::VerifiableClient; +//! # use voprf::VoprfClient; //! # use rand::{rngs::OsRng, RngCore}; //! # //! # let mut client_rng = OsRng; -//! # let client_blind_result = VerifiableClient::::blind( +//! # let client_blind_result = VoprfClient::::blind( //! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); -//! # use voprf::VerifiableServer; +//! # use voprf::VoprfServer; //! # let mut server_rng = OsRng; -//! # let server = VerifiableServer::::new(&mut server_rng); +//! # let server = VoprfServer::::new(&mut server_rng); //! # let server_evaluate_result = server.evaluate( //! # &mut server_rng, //! # &client_blind_result.message, @@ -287,7 +287,7 @@ //! //! It is sometimes desirable to generate only a single, constant-size proof for //! an unbounded number of VOPRF evaluations (on arbitrary inputs). -//! [VerifiableClient] and [VerifiableServer] support a batch API for handling +//! [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. //! @@ -299,22 +299,22 @@ //! # type CipherSuite = voprf::Ristretto255; //! # #[cfg(not(feature = "ristretto255"))] //! # type CipherSuite = p256::NistP256; -//! # use voprf::VerifiableClient; +//! # use voprf::VoprfClient; //! # use rand::{rngs::OsRng, RngCore}; //! # //! let mut client_rng = OsRng; //! let mut client_states = vec![]; //! let mut client_messages = vec![]; //! for _ in 0..10 { -//! let client_blind_result = VerifiableClient::::blind(b"input", &mut client_rng) +//! let client_blind_result = VoprfClient::::blind(b"input", &mut client_rng) //! .expect("Unable to construct client"); //! client_states.push(client_blind_result.state); //! client_messages.push(client_blind_result.message); //! } //! ``` //! -//! Next, the server calls the [VerifiableServer::batch_evaluate_prepare] and -//! [VerifiableServer::batch_evaluate_finish] function on a set of client +//! Next, the server calls the [VoprfServer::batch_evaluate_prepare] and +//! [VoprfServer::batch_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: //! @@ -323,36 +323,36 @@ //! # type CipherSuite = voprf::Ristretto255; //! # #[cfg(not(feature = "ristretto255"))] //! # type CipherSuite = p256::NistP256; -//! # use voprf::{VerifiableServerBatchEvaluatePrepareResult, VerifiableServerBatchEvaluateFinishResult, VerifiableClient}; +//! # use voprf::{VoprfServerBatchEvaluatePrepareResult, VoprfServerBatchEvaluateFinishResult, VoprfClient}; //! # use rand::{rngs::OsRng, RngCore}; //! # //! # let mut client_rng = OsRng; //! # let mut client_states = vec![]; //! # let mut client_messages = vec![]; //! # for _ in 0..10 { -//! # let client_blind_result = VerifiableClient::::blind( +//! # let client_blind_result = VoprfClient::::blind( //! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); //! # client_states.push(client_blind_result.state); //! # client_messages.push(client_blind_result.message); //! # } -//! # use voprf::VerifiableServer; +//! # use voprf::VoprfServer; //! let mut server_rng = OsRng; -//! # let server = VerifiableServer::::new(&mut server_rng); -//! let VerifiableServerBatchEvaluatePrepareResult { +//! # let server = VoprfServer::::new(&mut server_rng); +//! let VoprfServerBatchEvaluatePrepareResult { //! prepared_evaluation_elements, //! t, //! } = server //! .batch_evaluate_prepare(client_messages.iter(), None) //! .expect("Unable to perform server batch evaluate"); //! let prepared_elements: Vec<_> = prepared_evaluation_elements.collect(); -//! let VerifiableServerBatchEvaluateFinishResult { messages, proof } = VerifiableServer::batch_evaluate_finish(&mut server_rng, client_messages.iter(), &prepared_elements, &t) +//! let VoprfServerBatchEvaluateFinishResult { messages, proof } = VoprfServer::batch_evaluate_finish(&mut server_rng, client_messages.iter(), &prepared_elements, &t) //! .expect("Unable to perform server batch evaluate"); //! let messages: Vec<_> = messages.collect(); //! ``` //! -//! If [`alloc`] is available, [VerifiableServer::batch_evaluate] can be called +//! If [`alloc`] is available, [VoprfServer::batch_evaluate] can be called //! to avoid having to collect output manually: //! //! ``` @@ -361,30 +361,30 @@ //! # type CipherSuite = voprf::Ristretto255; //! # #[cfg(not(feature = "ristretto255"))] //! # type CipherSuite = p256::NistP256; -//! # use voprf::{VerifiableServerBatchEvaluateResult, VerifiableClient}; +//! # use voprf::{VoprfServerBatchEvaluateResult, VoprfClient}; //! # use rand::{rngs::OsRng, RngCore}; //! # //! # let mut client_rng = OsRng; //! # let mut client_states = vec![]; //! # let mut client_messages = vec![]; //! # for _ in 0..10 { -//! # let client_blind_result = VerifiableClient::::blind( +//! # let client_blind_result = VoprfClient::::blind( //! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); //! # client_states.push(client_blind_result.state); //! # client_messages.push(client_blind_result.message); //! # } -//! # use voprf::VerifiableServer; +//! # use voprf::VoprfServer; //! let mut server_rng = OsRng; -//! # let server = VerifiableServer::::new(&mut server_rng); -//! let VerifiableServerBatchEvaluateResult { messages, proof } = server +//! # let server = VoprfServer::::new(&mut server_rng); +//! let VoprfServerBatchEvaluateResult { messages, proof } = server //! .batch_evaluate(&mut server_rng, &client_messages, None) //! .expect("Unable to perform server batch evaluate"); //! # } //! ``` //! -//! Then, the client calls [VerifiableClient::batch_finalize] on the client +//! 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. @@ -395,27 +395,27 @@ //! # type CipherSuite = voprf::Ristretto255; //! # #[cfg(not(feature = "ristretto255"))] //! # type CipherSuite = p256::NistP256; -//! # use voprf::{VerifiableServerBatchEvaluateResult, VerifiableClient}; +//! # use voprf::{VoprfServerBatchEvaluateResult, VoprfClient}; //! # use rand::{rngs::OsRng, RngCore}; //! # //! # let mut client_rng = OsRng; //! # let mut client_states = vec![]; //! # let mut client_messages = vec![]; //! # for _ in 0..10 { -//! # let client_blind_result = VerifiableClient::::blind( +//! # let client_blind_result = VoprfClient::::blind( //! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); //! # client_states.push(client_blind_result.state); //! # client_messages.push(client_blind_result.message); //! # } -//! # use voprf::VerifiableServer; +//! # use voprf::VoprfServer; //! # let mut server_rng = OsRng; -//! # let server = VerifiableServer::::new(&mut server_rng); -//! # let VerifiableServerBatchEvaluateResult { messages, proof } = server +//! # let server = VoprfServer::::new(&mut server_rng); +//! # let VoprfServerBatchEvaluateResult { messages, proof } = server //! # .batch_evaluate(&mut server_rng, &client_messages, None) //! # .expect("Unable to perform server batch evaluate"); -//! let client_batch_finalize_result = VerifiableClient::batch_finalize( +//! let client_batch_finalize_result = VoprfClient::batch_finalize( //! &[b"input"; 10], //! &client_states, //! &messages, @@ -498,6 +498,8 @@ extern crate serde_ as serde; mod ciphersuite; mod error; mod group; +mod oprf; +mod poprf; mod serialization; mod util; mod voprf; @@ -512,17 +514,16 @@ pub use crate::error::{Error, InternalError, Result}; pub use crate::group::Group; #[cfg(feature = "ristretto255")] pub use crate::group::Ristretto255; +pub use crate::oprf::{OprfClient, OprfClientBlindResult, OprfServer}; +pub use crate::poprf::{PoprfClient, PoprfServer, PoprfServerBatchEvaluateResult}; pub use crate::serialization::{ - BlindedElementLen, EvaluationElementLen, NonVerifiableClientLen, NonVerifiableServerLen, - ProofLen, VerifiableClientLen, VerifiableServerLen, + BlindedElementLen, EvaluationElementLen, OprfClientLen, OprfServerLen, ProofLen, + VoprfClientLen, VoprfServerLen, }; +pub use crate::util::{BlindedElement, EvaluationElement, Mode, Proof}; #[cfg(feature = "alloc")] -pub use crate::voprf::VerifiableServerBatchEvaluateResult; +pub use crate::voprf::VoprfServerBatchEvaluateResult; pub use crate::voprf::{ - BlindedElement, EvaluationElement, Mode, NonVerifiableClient, NonVerifiableClientBlindResult, - NonVerifiableServer, PreparedEvaluationElement, PreparedTscalar, Proof, VerifiableClient, - VerifiableClientBatchFinalizeResult, VerifiableClientBlindResult, VerifiableServer, - VerifiableServerBatchEvaluateFinishResult, VerifiableServerBatchEvaluateFinishedMessages, - VerifiableServerBatchEvaluatePrepareResult, - VerifiableServerBatchEvaluatePreparedEvaluationElements, VerifiableServerEvaluateResult, + VoprfClient, VoprfClientBatchFinalizeResult, VoprfClientBlindResult, VoprfServer, + VoprfServerEvaluateResult, }; diff --git a/src/oprf.rs b/src/oprf.rs new file mode 100644 index 0000000..c2c64f8 --- /dev/null +++ b/src/oprf.rs @@ -0,0 +1,419 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under both the MIT license found in the +// LICENSE-MIT file in the root directory of this source tree and the Apache +// License, Version 2.0 found in the LICENSE-APACHE file in the root directory +// of this source tree. + +//! Contains the main OPRF API + +use core::iter::{self, Map, Repeat, Zip}; + +use derive_where::derive_where; +use digest::core_api::BlockSizeUser; +use digest::{Digest, Output, OutputSizeUser}; +use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256, U8}; +use generic_array::GenericArray; +use rand_core::{CryptoRng, RngCore}; + +#[cfg(feature = "serde")] +use crate::serialization::serde::Scalar; +use crate::util::{derive_keypair, i2osp_2, BlindedElement, EvaluationElement, Mode, STR_FINALIZE}; +use crate::{CipherSuite, Error, Group, Result}; + +/////////////// +// Constants // +// ========= // +/////////////// + +//////////////////////////// +// High-level API Structs // +// ====================== // +//////////////////////////// + +/// A client which engages with a [OPRFServer] in base mode, meaning +/// that the OPRF outputs are not verifiable. +#[derive_where(Clone, ZeroizeOnDrop)] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(crate = "serde", bound = "") +)] +pub struct OprfClient +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + #[cfg_attr(feature = "serde", serde(with = "Scalar::"))] + pub(crate) blind: ::Scalar, +} + +/// A server which engages with a [OprfClient] in base mode, meaning +/// that the OPRF outputs are not verifiable. +#[derive_where(Clone, ZeroizeOnDrop)] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(crate = "serde", bound = "") +)] +pub struct OprfServer +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + #[cfg_attr(feature = "serde", serde(with = "Scalar::"))] + pub(crate) sk: ::Scalar, +} + +///////////////////////// +// API Implementations // +// =================== // +///////////////////////// + +impl OprfClient +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + /// Computes the first step for the multiplicative blinding version of + /// DH-OPRF. + /// + /// # Errors + /// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`]. + pub fn blind( + input: &[u8], + blinding_factor_rng: &mut R, + ) -> Result> { + let blind = CS::Group::random_scalar(blinding_factor_rng); + Self::deterministic_blind_unchecked_inner(input, blind) + } + + #[cfg(any(feature = "danger", test))] + /// Computes the first step for the multiplicative blinding version of + /// DH-OPRF, taking a blinding factor scalar as input instead of sampling + /// from an RNG. + /// + /// # Caution + /// + /// This should be used with caution, since it does not perform any checks + /// on the validity of the blinding factor! + /// + /// # Errors + /// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`]. + pub fn deterministic_blind_unchecked( + input: &[u8], + blind: ::Scalar, + ) -> Result> { + Self::deterministic_blind_unchecked_inner(input, blind) + } + + /// Inner function for computing blind output + /// + /// # Errors + /// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`]. + fn deterministic_blind_unchecked_inner( + input: &[u8], + blind: ::Scalar, + ) -> Result> { + let blinded_element = + crate::util::deterministic_blind_unchecked::(input, &blind, Mode::Oprf)?; + Ok(OprfClientBlindResult { + state: Self { blind }, + message: BlindedElement(blinded_element), + }) + } + + /// Computes the third step for the multiplicative blinding version of + /// DH-OPRF, in which the client unblinds the server's message. + /// + /// # Errors + /// - [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`]. + pub fn finalize( + &self, + input: &[u8], + evaluation_element: &EvaluationElement, + ) -> Result> { + let unblinded_element = evaluation_element.0 * &CS::Group::invert_scalar(self.blind); + let mut outputs = finalize_after_unblind::( + iter::once((input, unblinded_element)), + &[], + ); + outputs.next().unwrap() + } + + #[cfg(test)] + /// Only used for test functions + pub fn from_blind(blind: ::Scalar) -> Self { + Self { blind } + } + + #[cfg(feature = "danger")] + /// Exposes the blind group element + pub fn get_blind(&self) -> ::Scalar { + self.blind + } +} + +impl OprfServer +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + /// Produces a new instance of a [OPRFServer] using a supplied RNG + pub fn new(rng: &mut R) -> Self { + let mut seed = Output::::default(); + rng.fill_bytes(&mut seed); + // This can't fail as the hash output is type constrained. + Self::new_from_seed(&seed, &[]).unwrap() + } + + /// Produces a new instance of a [OPRFServer] using a supplied set + /// of bytes to represent the server's private key + /// + /// # Errors + /// [`Error::Deserialization`] if the private key is not a valid point on + /// the group or zero. + pub fn new_with_key(private_key_bytes: &[u8]) -> Result { + let sk = CS::Group::deserialize_scalar(private_key_bytes)?; + Ok(Self { sk }) + } + + /// Produces a new instance of a [OPRFServer] using a supplied set + /// of bytes which are used as a seed to derive the server's private key. + /// + /// Corresponds to DeriveKeyPair() function from the VOPRF specification. + /// + /// # Errors + /// [`Error::Seed`] if the `seed` is empty or longer then [`u16::MAX`]. + pub fn new_from_seed(seed: &[u8], info: &[u8]) -> Result { + let (sk, _) = derive_keypair::(seed, info, Mode::Oprf).map_err(|_| Error::Seed)?; + Ok(Self { sk }) + } + + // Only used for tests + #[cfg(test)] + pub fn get_private_key(&self) -> ::Scalar { + self.sk + } + + /// 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. + /// + /// # Errors + /// - [`Error::Protocol`] if the protocol fails and can't be completed. + pub fn evaluate(&self, blinded_element: &BlindedElement) -> Result> { + Ok(EvaluationElement(blinded_element.0 * &self.sk)) + } +} + +///////////////////////// +// Convenience Structs // +//==================== // +///////////////////////// + +/// Contains the fields that are returned by a non-verifiable client blind +#[derive_where(Debug; ::Scalar, ::Elem)] +pub struct OprfClientBlindResult +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + /// The state to be persisted on the client + pub state: OprfClient, + /// The message to send to the server + pub message: BlindedElement, +} + +///////////////////// +// Inner functions // +// =============== // +///////////////////// + +type FinalizeAfterUnblindResult<'a, C, I, IE> = Map< + Zip>>, + fn( + ( + (I, <::Group as Group>::Elem), + GenericArray, + ), + ) -> Result::Hash>>, +>; + +fn finalize_after_unblind< + 'a, + CS: CipherSuite, + I: AsRef<[u8]>, + IE: 'a + Iterator::Elem)>, +>( + inputs_and_unblinded_elements: IE, + _unused: &'a [u8], +) -> FinalizeAfterUnblindResult +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + let finalize_dst = GenericArray::from(STR_FINALIZE); + + inputs_and_unblinded_elements + // To make a return type possible, we have to convert to a `fn` pointer, + // which isn't possible if we `move` from context. + .zip(iter::repeat(finalize_dst)) + .map(|((input, unblinded_element), finalize_dst)| { + let elem_len = ::ElemLen::U16.to_be_bytes(); + + // hashInput = I2OSP(len(input), 2) || input || + // I2OSP(len(unblindedElement), 2) || unblindedElement || + // "Finalize" + // return Hash(hashInput) + Ok(CS::Hash::new() + .chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?) + .chain_update(input.as_ref()) + .chain_update(elem_len) + .chain_update(CS::Group::serialize_elem(unblinded_element)) + .chain_update(finalize_dst) + .finalize()) + }) +} + +/////////// +// Tests // +// ===== // +/////////// + +#[cfg(test)] +mod tests { + use core::ptr; + + use rand::rngs::OsRng; + + use super::*; + use crate::Group; + + fn prf( + input: &[u8], + key: ::Scalar, + info: &[u8], + mode: Mode, + ) -> Output + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { + let point = CS::Group::hash_to_curve::(&[input], mode).unwrap(); + + let res = point * &key; + + finalize_after_unblind::(iter::once((input, res)), info) + .next() + .unwrap() + .unwrap() + } + + fn base_retrieval() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { + let input = b"input"; + let mut rng = OsRng; + let client_blind_result = OprfClient::::blind(input, &mut rng).unwrap(); + let server = OprfServer::::new(&mut rng); + let message = server.evaluate(&client_blind_result.message).unwrap(); + let client_finalize_result = client_blind_result + .state + .finalize(input, &message) + .unwrap(); + let res2 = prf::(input, server.get_private_key(), &[], Mode::Oprf); + assert_eq!(client_finalize_result, res2); + } + + fn base_inversion_unsalted() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { + let mut rng = OsRng; + let mut input = [0u8; 64]; + rng.fill_bytes(&mut input); + let client_blind_result = OprfClient::::blind(&input, &mut rng).unwrap(); + let client_finalize_result = client_blind_result + .state + .finalize( + &input, + &EvaluationElement(client_blind_result.message.0), + ) + .unwrap(); + + let point = CS::Group::hash_to_curve::(&[&input], Mode::Oprf).unwrap(); + let res2 = finalize_after_unblind::(iter::once((input.as_ref(), point)), &[]) + .next() + .unwrap() + .unwrap(); + + assert_eq!(client_finalize_result, res2); + } + + fn zeroize_oprf_client() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { + let input = b"input"; + let mut rng = OsRng; + let client_blind_result = OprfClient::::blind(input, &mut rng).unwrap(); + + let mut state = client_blind_result.state; + unsafe { ptr::drop_in_place(&mut state) }; + assert!(state.serialize().iter().all(|&x| x == 0)); + + let mut message = client_blind_result.message; + unsafe { ptr::drop_in_place(&mut message) }; + assert!(message.serialize().iter().all(|&x| x == 0)); + } + + fn zeroize_oprf_server() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { + let input = b"input"; + let mut rng = OsRng; + let client_blind_result = OprfClient::::blind(input, &mut rng).unwrap(); + let server = OprfServer::::new(&mut rng); + let mut message = server.evaluate(&client_blind_result.message).unwrap(); + + let mut state = server; + unsafe { ptr::drop_in_place(&mut state) }; + assert!(state.serialize().iter().all(|&x| x == 0)); + + unsafe { ptr::drop_in_place(&mut message) }; + assert!(message.serialize().iter().all(|&x| x == 0)); + } + + #[test] + fn test_functionality() -> Result<()> { + use p256::NistP256; + + #[cfg(feature = "ristretto255")] + { + use crate::Ristretto255; + + base_retrieval::(); + base_inversion_unsalted::(); + + zeroize_oprf_client::(); + zeroize_oprf_server::(); + } + + base_retrieval::(); + base_inversion_unsalted::(); + + zeroize_oprf_client::(); + zeroize_oprf_server::(); + + Ok(()) + } +} diff --git a/src/poprf.rs b/src/poprf.rs new file mode 100644 index 0000000..06b8564 --- /dev/null +++ b/src/poprf.rs @@ -0,0 +1,717 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under both the MIT license found in the +// LICENSE-MIT file in the root directory of this source tree and the Apache +// License, Version 2.0 found in the LICENSE-APACHE file in the root directory +// of this source tree. + +//! Contains the main POPRF API + +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + +use derive_where::derive_where; +use digest::core_api::BlockSizeUser; +use digest::{Digest, Output, OutputSizeUser}; +use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256}; +use generic_array::GenericArray; +use rand_core::{CryptoRng, RngCore}; + +#[cfg(feature = "serde")] +use crate::serialization::serde::{Element, Scalar}; +use crate::util::{ + derive_keypair, generate_proof, i2osp_2, verify_proof, BlindedElement, EvaluationElement, Mode, + Proof, ProofElement, STR_FINALIZE, STR_INFO, +}; +use crate::{CipherSuite, Error, Group, Result}; + +//////////////////////////// +// High-level API Structs // +// ====================== // +//////////////////////////// + +/// A client which engages with a [PoprfServer] in verifiable mode, meaning +/// that the OPRF outputs can be checked against a server public key. +#[derive_where(Clone, ZeroizeOnDrop)] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar, ::Elem)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(crate = "serde", bound = "") +)] +pub struct PoprfClient +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + #[cfg_attr(feature = "serde", serde(with = "Scalar::"))] + pub(crate) blind: ::Scalar, + #[cfg_attr(feature = "serde", serde(with = "Element::"))] + pub(crate) blinded_element: ::Elem, +} + +/// A server which engages with a [PoprfClient] in verifiable mode, meaning +/// that the OPRF outputs can be checked against a server public key. +#[derive_where(Clone, ZeroizeOnDrop)] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar, ::Elem)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(crate = "serde", bound = "") +)] +pub struct PoprfServer +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + #[cfg_attr(feature = "serde", serde(with = "Scalar::"))] + pub(crate) sk: ::Scalar, + #[cfg_attr(feature = "serde", serde(with = "Element::"))] + pub(crate) pk: ::Elem, +} + +///////////////////////// +// API Implementations // +// =================== // +///////////////////////// + +impl PoprfClient +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + /// Computes the first step for the multiplicative blinding version of + /// DH-OPRF. + /// + /// # Errors + /// [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`]. + pub fn blind( + blinding_factor_rng: &mut R, + input: &[u8], + ) -> Result> { + let blind = CS::Group::random_scalar(blinding_factor_rng); + Self::deterministic_blind_unchecked_inner(input, blind) + } + + #[cfg(any(feature = "danger", test))] + /// Computes the first step for the multiplicative blinding version of + /// DH-OPRF, taking a blinding factor scalar as input instead of sampling + /// from an RNG. + /// + /// # Caution + /// + /// This should be used with caution, since it does not perform any checks + /// on the validity of the blinding factor! + /// + /// # Errors + /// [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`]. + pub fn deterministic_blind_unchecked( + input: &[u8], + blind: ::Scalar, + ) -> Result> { + Self::deterministic_blind_unchecked_inner(input, blind) + } + + /// Inner function for computing blind output + /// + /// # Errors + /// [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`]. + fn deterministic_blind_unchecked_inner( + input: &[u8], + blind: ::Scalar, + ) -> Result> { + let blinded_element = + crate::util::deterministic_blind_unchecked::(input, &blind, Mode::Poprf)?; + Ok(PoprfClientBlindResult { + state: Self { + blind, + blinded_element, + }, + message: BlindedElement(blinded_element), + }) + } + + /// Computes the third step for the multiplicative blinding version of + /// DH-OPRF, in which the client unblinds the server's message. + /// + /// # Errors + /// - [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`]. + /// - [`Error::Info`] if the `info` is longer than `u16::MAX`. + /// - [`Error::ProofVerification`] if the `proof` failed to verify. + pub fn finalize( + &self, + input: &[u8], + evaluation_element: &EvaluationElement, + proof: &Proof, + pk: ::Elem, + info: Option<&[u8]>, + ) -> Result> { + let inputs = core::array::from_ref(&input); + let clients = core::array::from_ref(self); + let messages = core::array::from_ref(evaluation_element); + + let batch_result = Self::batch_finalize(inputs, clients, messages, proof, pk, info)?; + Ok(batch_result.first().unwrap().clone()) + } + + /// Allows for batching of the finalization of multiple [PoprfClient] + /// and [EvaluationElement] pairs + /// + /// # Errors + /// - [`Error::Info`] if the `info` is longer than `u16::MAX`. + /// - [`Error::Batch`] if the number of `clients` and `messages` don't match + /// or is longer than [`u16::MAX`]. + /// - [`Error::ProofVerification`] if the `proof` failed to verify. + /// + /// The resulting messages can each fail individually with [`Error::Input`] + /// if the `input` is empty or longer than [`u16::MAX`]. + pub fn batch_finalize( + inputs: &[&[u8]], + clients: &[PoprfClient], + messages: &[EvaluationElement], + proof: &Proof, + pk: ::Elem, + info: Option<&[u8]>, + ) -> Result::Hash>>> { + let unblinded_elements = poprf_unblind(clients, messages, pk, proof, info)?; + + let mut inputs_and_unblinded_elements = alloc::vec![]; + for (input, unblinded_element) in inputs.iter().cloned().zip(unblinded_elements) { + inputs_and_unblinded_elements.push((input, unblinded_element)); + } + + finalize_after_unblind::(&inputs_and_unblinded_elements, info) + } + + #[cfg(test)] + /// Only used for test functions + pub fn get_blind(&self) -> ::Scalar { + self.blind + } +} + +impl PoprfServer +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + /// Produces a new instance of a [PoprfServer] using a supplied RNG + pub fn new(rng: &mut R) -> Self { + let mut seed = Output::::default(); + rng.fill_bytes(&mut seed); + // This can't fail as the hash output is type constrained. + Self::new_from_seed(&seed, &[]).unwrap() + } + + /// Produces a new instance of a [PoprfServer] using a supplied set of + /// bytes to represent the server's private key + /// + /// # Errors + /// [`Error::Deserialization`] if the private key is not a valid point on + /// the group or zero. + pub fn new_with_key(key: &[u8]) -> Result { + let sk = CS::Group::deserialize_scalar(key)?; + let pk = CS::Group::base_elem() * &sk; + Ok(Self { sk, pk }) + } + + /// Produces a new instance of a [PoprfServer] using a supplied set of + /// bytes which are used as a seed to derive the server's private key. + /// + /// Corresponds to DeriveKeyPair() function from the VOPRF specification. + /// + /// # Errors + /// [`Error::Seed`] if the `seed` is empty or longer than [`u16::MAX`]. + pub fn new_from_seed(seed: &[u8], info: &[u8]) -> Result { + let (sk, pk) = derive_keypair::(seed, info, Mode::Poprf).map_err(|_| Error::Seed)?; + Ok(Self { sk, pk }) + } + + // Only used for tests + #[cfg(test)] + pub fn get_private_key(&self) -> ::Scalar { + self.sk + } + + /// 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. + /// + /// # 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( + &self, + rng: &mut R, + blinded_element: &BlindedElement, + info: Option<&[u8]>, + ) -> Result> { + let batch_evaluate_result = + self.batch_evaluate(rng, std::vec![blinded_element.clone()], info)?; + Ok(PoprfServerEvaluateResult { + message: batch_evaluate_result.messages.get(0).unwrap().clone(), + proof: batch_evaluate_result.proof, + }) + } + + /// Allows for batching of the evaluation of multiple [BlindedElement] + /// messages from a [PoprfClient] + /// + /// # 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( + &self, + rng: &mut R, + blinded_elements: Vec>, + info: Option<&[u8]>, + ) -> Result> { + let g = CS::Group::base_elem(); + let tweak = compute_tweak::(self.sk, info)?; + let tweaked_key = g * &tweak; + + // evaluatedElement = G.ScalarInverse(t) * blindedElement + let evaluation_elements: Vec> = blinded_elements + .iter() + .map(|blinded_element| { + EvaluationElement(blinded_element.0 * &CS::Group::invert_scalar(tweak)) + }) + .collect(); + + let messages = evaluation_elements.clone(); + + let proof = generate_proof( + rng, + tweak, + g, + tweaked_key, + evaluation_elements + .into_iter() + .map(|element: EvaluationElement| ProofElement(element.0)), + blinded_elements + .into_iter() + .map(|element| ProofElement(element.0)), + Mode::Poprf, + )?; + + Ok(PoprfServerBatchEvaluateResult { messages, proof }) + } + + /// Retrieves the server's public key + pub fn get_public_key(&self) -> ::Elem { + self.pk + } +} + +impl BlindedElement +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + #[cfg(feature = "danger")] + /// Creates a [BlindedElement] from a raw group element. + /// + /// # Caution + /// + /// This should be used with caution, since it does not perform any checks + /// on the validity of the value itself! + pub fn from_value_unchecked(value: ::Elem) -> Self { + Self(value) + } + + #[cfg(feature = "danger")] + /// Exposes the internal value + pub fn value(&self) -> ::Elem { + self.0 + } +} + +impl EvaluationElement +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + #[cfg(feature = "danger")] + /// Creates an [EvaluationElement] from a raw group element. + /// + /// # Caution + /// + /// This should be used with caution, since it does not perform any checks + /// on the validity of the value itself! + pub fn from_value_unchecked(value: ::Elem) -> Self { + Self(value) + } + + #[cfg(feature = "danger")] + /// Exposes the internal value + pub fn value(&self) -> ::Elem { + self.0 + } +} + +///////////////////////// +// Convenience Structs // +//==================== // +///////////////////////// + +/// Contains the fields that are returned by a verifiable client blind +#[derive_where(Debug; ::Scalar, ::Elem)] +pub struct PoprfClientBlindResult +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + /// The state to be persisted on the client + pub state: PoprfClient, + /// The message to send to the server + pub message: BlindedElement, +} + +/// Contains the fields that are returned by a verifiable server evaluate +#[derive_where(Debug; ::Scalar, ::Elem)] +pub struct PoprfServerEvaluateResult +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + /// The message to send to the client + pub message: EvaluationElement, + /// The proof for the client to verify + pub proof: Proof, +} + +/// Contains the fields that are returned by a verifiable server batch evaluate +#[derive_where(Debug; ::Scalar, ::Elem)] +#[cfg(feature = "alloc")] +pub struct PoprfServerBatchEvaluateResult +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + /// The messages to send to the client + pub messages: alloc::vec::Vec>, + /// The proof for the client to verify + pub proof: Proof, +} + +///////////////////// +// Inner functions // +// =============== // +///////////////////// + +// Inner function for POPRF blind. Computes the tweaked key from the server +// public key and info. +fn compute_tweaked_key( + pk: ::Elem, + info: Option<&[u8]>, +) -> Result<::Elem> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + // None for info is treated the same as empty bytes + let info = info.unwrap_or_default(); + + // framedInfo = "Info" || I2OSP(len(info), 2) || info + // m = G.HashToScalar(framedInfo) + // T = G.ScalarBaseMult(m) + // tweakedKey = T + pkS + // if tweakedKey == G.Identity(): + // raise InvalidInputError + let info_len = i2osp_2(info.len()).map_err(|_| Error::Input)?; + let framed_info = [&STR_INFO, info_len.as_slice(), info]; + + // This can't fail, the size of the `input` is known. + let m = CS::Group::hash_to_scalar::(&framed_info, Mode::Poprf).unwrap(); + + let t = CS::Group::base_elem() * &m; + let tweaked_key = t + &pk; + + // Check if resulting element + match bool::from(CS::Group::is_identity_elem(tweaked_key)) { + true => Err(Error::Input), + false => Ok(tweaked_key), + } +} + +// Inner function for POPRF evaluate. Computes the tweak from the server +// private key and info. +fn compute_tweak( + sk: ::Scalar, + info: Option<&[u8]>, +) -> Result<::Scalar> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + // None for info is treated the same as empty bytes + let info = info.unwrap_or_default(); + + // framedInfo = "Info" || I2OSP(len(info), 2) || info + // m = G.HashToScalar(framedInfo) + // t = skS + m + // if t == 0: + // raise InverseError + let info_len = i2osp_2(info.len()).map_err(|_| Error::Input)?; + let framed_info = [&STR_INFO, info_len.as_slice(), info]; + + // This can't fail, the size of the `input` is known. + let m = CS::Group::hash_to_scalar::(&framed_info, Mode::Poprf).unwrap(); + + let t = sk + &m; + + // Check if resulting element is equal to zero + match bool::from(CS::Group::is_zero_scalar(t)) { + true => Err(Error::Input), + false => Ok(t), + } +} + +// Can only fail with [`Error::Info`], [`Error::Batch] or +// [`Error::ProofVerification`]. +fn poprf_unblind( + clients: &[PoprfClient], + messages: &[EvaluationElement], + pk: ::Elem, + proof: &Proof, + info: Option<&[u8]>, +) -> Result::Elem>> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + let info = info.unwrap_or_default(); + let tweaked_key = compute_tweaked_key::(pk, Some(info))?; + + let g = CS::Group::base_elem(); + + let blinds = clients + .iter() + // Convert to `fn` pointer to make a return type possible. + .map() -> _>::from(|x| x.blind)); + let evaluation_elements = messages.iter().map(|element| ProofElement(element.0)); + let blinded_elements = clients + .iter() + .map(|client| ProofElement(client.blinded_element)); + + verify_proof( + g, + tweaked_key, + evaluation_elements, + blinded_elements, + proof, + Mode::Poprf, + )?; + + Ok(blinds + .zip(messages.iter()) + .map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)) + .collect()) +} + +// Returned values can only fail with [`Error::Input`] or [`Error::Info`]. +fn finalize_after_unblind( + inputs_and_unblinded_elements: &[(&[u8], ::Elem)], + info: Option<&[u8]>, +) -> Result::Hash>>> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + let mut outputs = alloc::vec![]; + + let info = info.unwrap_or_default(); + let elem_len = ::ElemLen::U16.to_be_bytes(); + let finalize_dst = GenericArray::from(STR_FINALIZE); + + for (input, unblinded_element) in inputs_and_unblinded_elements.iter().cloned() { + // hashInput = I2OSP(len(input), 2) || input || + // I2OSP(len(info), 2) || info || + // I2OSP(len(unblindedElement), 2) || unblindedElement || + // "Finalize" + // return Hash(hashInput) + let output = CS::Hash::new() + .chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?) + .chain_update(input.as_ref()) + .chain_update(i2osp_2(info.as_ref().len()).map_err(|_| Error::Input)?) + .chain_update(info.as_ref()) + .chain_update(elem_len) + .chain_update(CS::Group::serialize_elem(unblinded_element)) + .chain_update(finalize_dst) + .finalize(); + outputs.push(output); + } + + Ok(outputs) +} + +/////////// +// Tests // +// ===== // +/////////// + +#[cfg(test)] +mod tests { + use core::ops::Add; + use core::ptr; + + use generic_array::typenum::Sum; + use generic_array::ArrayLength; + use rand::rngs::OsRng; + + use super::*; + use crate::Group; + + fn prf( + input: &[u8], + key: ::Scalar, + info: &[u8], + mode: Mode, + ) -> Output + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { + let t = compute_tweak::(key, Some(info)).unwrap(); + + let point = CS::Group::hash_to_curve::(&[input], mode).unwrap(); + + // evaluatedElement = G.ScalarInverse(t) * blindedElement + let res = point * &CS::Group::invert_scalar(t); + + finalize_after_unblind::(&[(input, res)], Some(info)) + .unwrap() + .first() + .unwrap() + .clone() + } + + fn verifiable_retrieval() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { + let input = b"input"; + let info = b"info"; + let mut rng = OsRng; + let server = PoprfServer::::new(&mut rng); + let client_blind_result = PoprfClient::::blind(&mut rng, input).unwrap(); + let server_result = server + .evaluate(&mut rng, &client_blind_result.message, Some(info)) + .unwrap(); + let client_finalize_result = client_blind_result + .state + .finalize( + input, + &server_result.message, + &server_result.proof, + server.get_public_key(), + Some(info), + ) + .unwrap(); + let res2 = prf::(input, server.get_private_key(), info, Mode::Poprf); + assert_eq!(client_finalize_result, res2); + } + + fn verifiable_bad_public_key() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { + let input = b"input"; + let info = b"info"; + let mut rng = OsRng; + let server = PoprfServer::::new(&mut rng); + let client_blind_result = PoprfClient::::blind(&mut rng, input).unwrap(); + let server_result = server + .evaluate(&mut rng, &client_blind_result.message, Some(info)) + .unwrap(); + let wrong_pk = { + // Choose a group element that is unlikely to be the right public key + CS::Group::hash_to_curve::(&[b"msg"], Mode::Oprf).unwrap() + }; + let client_finalize_result = client_blind_result.state.finalize( + input, + &server_result.message, + &server_result.proof, + wrong_pk, + Some(info), + ); + assert!(client_finalize_result.is_err()); + } + + fn zeroize_verifiable_client() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + ::ScalarLen: Add<::ElemLen>, + Sum<::ScalarLen, ::ElemLen>: ArrayLength, + { + let input = b"input"; + let mut rng = OsRng; + let client_blind_result = PoprfClient::::blind(&mut rng, input).unwrap(); + + let mut state = client_blind_result.state; + unsafe { ptr::drop_in_place(&mut state) }; + assert!(state.serialize().iter().all(|&x| x == 0)); + + let mut message = client_blind_result.message; + unsafe { ptr::drop_in_place(&mut message) }; + assert!(message.serialize().iter().all(|&x| x == 0)); + } + + fn zeroize_verifiable_server() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + ::ScalarLen: Add<::ElemLen>, + Sum<::ScalarLen, ::ElemLen>: ArrayLength, + ::ScalarLen: Add<::ScalarLen>, + Sum<::ScalarLen, ::ScalarLen>: ArrayLength, + { + let input = b"input"; + let info = b"info"; + let mut rng = OsRng; + let server = PoprfServer::::new(&mut rng); + let client_blind_result = PoprfClient::::blind(&mut rng, input).unwrap(); + let server_result = server + .evaluate(&mut rng, &client_blind_result.message, Some(info)) + .unwrap(); + + let mut state = server; + unsafe { ptr::drop_in_place(&mut state) }; + assert!(state.serialize().iter().all(|&x| x == 0)); + + let mut message = server_result.message; + unsafe { ptr::drop_in_place(&mut message) }; + assert!(message.serialize().iter().all(|&x| x == 0)); + + let mut proof = server_result.proof; + unsafe { ptr::drop_in_place(&mut proof) }; + assert!(proof.serialize().iter().all(|&x| x == 0)); + } + + #[test] + fn test_functionality() -> Result<()> { + use p256::NistP256; + + #[cfg(feature = "ristretto255")] + { + use crate::Ristretto255; + + verifiable_retrieval::(); + verifiable_bad_public_key::(); + + zeroize_verifiable_client::(); + zeroize_verifiable_server::(); + } + + verifiable_retrieval::(); + verifiable_bad_public_key::(); + + zeroize_verifiable_client::(); + zeroize_verifiable_server::(); + + Ok(()) + } +} diff --git a/src/serialization.rs b/src/serialization.rs index 55606f5..9a0069c 100644 --- a/src/serialization.rs +++ b/src/serialization.rs @@ -17,8 +17,8 @@ use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, Unsigned, U256}; use generic_array::{ArrayLength, GenericArray}; use crate::{ - BlindedElement, CipherSuite, Error, EvaluationElement, Group, NonVerifiableClient, - NonVerifiableServer, Proof, Result, VerifiableClient, VerifiableServer, + BlindedElement, CipherSuite, Error, EvaluationElement, Group, OprfClient, OprfServer, + PoprfClient, PoprfServer, Proof, Result, VoprfClient, VoprfServer, }; ////////////////////////////////////////////////////////// @@ -26,16 +26,16 @@ use crate::{ // ==================================================== // ////////////////////////////////////////////////////////// -/// Length of [`NonVerifiableClient`] in bytes for serialization. -pub type NonVerifiableClientLen = <::Group as Group>::ScalarLen; +/// Length of [`OprfClient`] in bytes for serialization. +pub type OprfClientLen = <::Group as Group>::ScalarLen; -impl NonVerifiableClient +impl OprfClient where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { /// Serialization into bytes - pub fn serialize(&self) -> GenericArray> { + pub fn serialize(&self) -> GenericArray> { CS::Group::serialize_scalar(self.blind) } @@ -53,21 +53,21 @@ where } /// Length of [`VerifiableClient`] in bytes for serialization. -pub type VerifiableClientLen = Sum< +pub type VoprfClientLen = Sum< <::Group as Group>::ScalarLen, <::Group as Group>::ElemLen, >; -impl VerifiableClient +impl VoprfClient where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { /// Serialization into bytes - pub fn serialize(&self) -> GenericArray> + pub fn serialize(&self) -> GenericArray> where ::ScalarLen: Add<::ElemLen>, - VerifiableClientLen: ArrayLength, + VoprfClientLen: ArrayLength, { ::serialize_scalar(self.blind) .concat(::serialize_elem(self.blinded_element)) @@ -90,16 +90,48 @@ where } } -/// Length of [`NonVerifiableServer`] in bytes for serialization. -pub type NonVerifiableServerLen = <::Group as Group>::ScalarLen; +impl PoprfClient +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + /// Serialization into bytes + pub fn serialize(&self) -> GenericArray> + where + ::ScalarLen: Add<::ElemLen>, + VoprfClientLen: ArrayLength, + { + ::serialize_scalar(self.blind) + .concat(::serialize_elem(self.blinded_element)) + } + + /// Deserialization from bytes + /// + /// # Errors + /// [`Error::Deserialization`] if failed to deserialize `input`. + pub fn deserialize(input: &[u8]) -> Result { + let mut input = input.iter().copied(); + + let blind = deserialize_scalar::(&mut input)?; + let blinded_element = deserialize_elem::(&mut input)?; + + Ok(Self { + blind, + blinded_element, + }) + } +} + +/// Length of [`OprfServer`] in bytes for serialization. +pub type OprfServerLen = <::Group as Group>::ScalarLen; -impl NonVerifiableServer +impl OprfServer where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { /// Serialization into bytes - pub fn serialize(&self) -> GenericArray> { + pub fn serialize(&self) -> GenericArray> { CS::Group::serialize_scalar(self.sk) } @@ -117,21 +149,49 @@ where } /// Length of [`VerifiableServer`] in bytes for serialization. -pub type VerifiableServerLen = Sum< +pub type VoprfServerLen = Sum< <::Group as Group>::ScalarLen, <::Group as Group>::ElemLen, >; -impl VerifiableServer +impl VoprfServer +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + /// Serialization into bytes + pub fn serialize(&self) -> GenericArray> + where + ::ScalarLen: Add<::ElemLen>, + VoprfServerLen: ArrayLength, + { + CS::Group::serialize_scalar(self.sk).concat(CS::Group::serialize_elem(self.pk)) + } + + /// Deserialization from bytes + /// + /// # Errors + /// [`Error::Deserialization`] if failed to deserialize `input`. + pub fn deserialize(input: &[u8]) -> Result { + let mut input = input.iter().copied(); + + let sk = deserialize_scalar::(&mut input)?; + let pk = deserialize_elem::(&mut input)?; + + Ok(Self { sk, pk }) + } +} + +impl PoprfServer where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { /// Serialization into bytes - pub fn serialize(&self) -> GenericArray> + pub fn serialize(&self) -> GenericArray> where ::ScalarLen: Add<::ElemLen>, - VerifiableServerLen: ArrayLength, + VoprfServerLen: ArrayLength, { CS::Group::serialize_scalar(self.sk).concat(CS::Group::serialize_elem(self.pk)) } diff --git a/src/tests/cfrg_vectors.rs b/src/tests/cfrg_vectors.rs new file mode 100644 index 0000000..81fc07b --- /dev/null +++ b/src/tests/cfrg_vectors.rs @@ -0,0 +1,1034 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under both the MIT license found in the +// LICENSE-MIT file in the root directory of this source tree and the Apache +// License, Version 2.0 found in the LICENSE-APACHE file in the root directory +// of this source tree. + +//! The VOPRF test vectors taken from: +//! https://github.com/cfrg/draft-irtf-cfrg-voprf/blob/master/draft-irtf-cfrg-voprf.md + +pub(crate) const VECTORS: &str = r#" +## OPRF(ristretto255, SHA-512) + +### OPRF Mode + +~~~ +Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a +3a3 +KeyInfo = 74657374206b6579 +skSm = 8ce0798c296bdeb665d52312d81a596dbb4ef0d25adb10c7f2b58c72dd2e5 +40a +~~~ + +#### Test Vector 1, Batch Size 1 + +~~~ +Input = 00 +Blind = c604c785ada70d77a5256ae21767de8c3304115237d262134f5e46e512cf +8e03 +BlindedElement = 8453ce4f98478a73faf24dd0c2e81d9a5e399171d2687cc258b +9e593623bde4d +EvaluationElement = 22bcfc0930ecddf4ada3f0cb421c8d6669576fc4fbbe24e1 +8c94d0f36e767466 +Output = 2765a7f9fa7e9d5440bbf1262dc1041277bed5f27fd27ee89662192a408 +508bb8711559d5a5390560065b83b946ed7b433d0c1df09bd23871804ae78e4a4d21 +5 +~~~ + +#### Test Vector 2, Batch Size 1 + +~~~ +Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Blind = 5ed895206bfc53316d307b23e46ecc6623afb3086da74189a416012be037 +e50b +BlindedElement = 86ef8baa01dd6cc34a067d2fc56cde51498a54cb0c30f63f083 +53d912164d711 +EvaluationElement = a27d5e498927ca96e493373a04e263115c31b918411df0ce +d382db4e66388766 +Output = 3d6c9ec7dd6f51b987b46b79128d98323accd7c1561faa50d287c5285ec +da1e660f3ee2929aebd431a7a7d511767cbd1054735a6e19aee1b9423a1e6f479535 +e +~~~ + +### VOPRF Mode + +~~~ +Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a +3a3 +KeyInfo = 74657374206b6579 +skSm = 24d1aca670a768b99e888c19f9ee3252d17e32689507b2b9b803ae23b1997 +f07 +pkSm = 46919d396c12fbb7a02f4ce8f51a9941ddc1c4335682e1b03b0ca5b3524c6 +619 +~~~ + +#### Test Vector 1, Batch Size 1 + +~~~ +Input = 00 +Blind = ed8366feb6b1d05d1f46acb727061e43aadfafe9c10e5a64e7518d63e326 +3503 +BlindedElement = 444550ea064013c569fe63567eb93e7a9496902a573ea1e6654 +76fd39d5edc40 +EvaluationElement = 7af7a45e4f1e0c6d410d41704e16d980ebff051fd0975fce +cd17f79a6b57a473 +Proof = 26982a26b2aa20f1e449be5a858c59d7992f7f4a13b007e3980f5c36e8ae +a7014268883db3094e08e3f493b3d23bae87ac098a33e775172c1027f1b5d025ca08 +ProofRandomScalar = 019cbd1d7420292528f8cdd62f339fdabb602f04a95dac9d +bcec831b8c681a09 +Output = 453a358544b4e92bbc4625d08ffdde64c0dbc4f9b1501d548e3a6d8094b +a70a993c13a6e65a46880bbd65272ba54cf199577760815098e5e10cb951b1fc5b02 +7 +~~~ + +#### Test Vector 2, Batch Size 1 + +~~~ +Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Blind = e6d0f1d89ad552e383d6c6f4e8598cc3037d6e274d22da3089e7afbd4171 +ea02 +BlindedElement = 82d9fc20daf67106ae2d2c584c615d103dafda653ac5b2b2c6f +aafc06e3f1c0a +EvaluationElement = 60c468920f4f949be9aaaf9b4fb27dc7bc89daca4a3aaa31 +e96efae56c02ac75 +Proof = 4a7490fd0a9e13cc66bcdeded002899a3e206364d9bdbaf9998a73dd728c +8602a6967f81a4948e6de797d638ee02ca44d933d05f2715fa1618b6a3324f3b2608 +ProofRandomScalar = 74ae06fd50d5f26c2519bd7b184f45dd3ef2cb50197d42df +9d013f7d6c312a0b +Output = a2bacfc82a4cac041edab1e1c0d0dc63f46631fb4886f8c395f0b184a9b +7cbbef2eee05bbd3f085552d8c80e77711b2ad9ba2b7574e2531591380e717d29c6f +5 +~~~ + +#### Test Vector 3, Batch Size 2 + +~~~ +Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Blind = 80513e77795feeec6d2c450589b0e1b178febd5c193a9fcba0d27f0a06e0 +d50f,533c2e6d91c934f919ac218973be55ba0d7b234160a0d4cf3bddafbda99e2e0 +c +BlindedElement = 70a6ac589da4cfff4a1135c21e438a50935317ad6900810a59e +76c2c28d8e562,5ed4710468c94e6c0181aef8276204ec6aef509f5cf1d7d6184693 +1481d23d76 +EvaluationElement = 5ef7bc4c54aa5fccb4328fd725d3c20130ebe3ced54f28b6 +e6c4591815158059,0ce1a236be8dba445cf57ddddec8f1c2d9be2c164add431fc18 +e3279be968c2d +Proof = 53afba40c6c27636a0694def258728f192d25ec5f97ee1e87a408fd20615 +6107d3b82b618242f10ff459d7d30d0a68d9e381254d2e5f6bc82671f093f47c0e01 +ProofRandomScalar = 3af5aec325791592eee4a8860522f8444c8e71ac33af5186 +a9706137886dce08 +Output = 453a358544b4e92bbc4625d08ffdde64c0dbc4f9b1501d548e3a6d8094b +a70a993c13a6e65a46880bbd65272ba54cf199577760815098e5e10cb951b1fc5b02 +7,a2bacfc82a4cac041edab1e1c0d0dc63f46631fb4886f8c395f0b184a9b7cbbef2 +eee05bbd3f085552d8c80e77711b2ad9ba2b7574e2531591380e717d29c6f5 +~~~ + +### POPRF Mode + +~~~ +Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a +3a3 +KeyInfo = 74657374206b6579 +skSm = 02c1b949c3d65f83b18890aa8d099f6063fa6a72add8d776bc15a291fd08f +f04 +pkSm = 1a2fac6b919790c613e0fbed070471778fbb1c6950d40a4e059acb652dc57 +161 +~~~ + +#### Test Vector 1, Batch Size 1 + +~~~ +Input = 00 +Info = 7465737420696e666f +Blind = 7e5bcbf82a46109ee0d24e9bcab41fc830a6ce8b82fc1e9213a043b743b9 +5800 +BlindedElement = da01485047605a666542d0599ef2fbeed0c2e45a97c6e3d420f +832918e09f535 +EvaluationElement = 3015fc16fe179bdb9054da5297c77d1f249dabf32e4fdcc4 +937d6ba5e99d7b53 +Proof = f10470180fc884a2f51472eddde9ad9a4080b00e13f63c130cece83b93ca +500f956b08e35ed2670ca504c704e0b74687451f5985627c93e2290a5da0dffc1d0b +ProofRandomScalar = 080d0a4d352de92672ab709b1ae1888cb48dfabc2d6ca5b9 +14b335512fe70508 +Output = 4d04eccb77a29bd8a00fb1e3f391e0601340c3dc874fc7bb16cfd92d961 +532d18b4edfffaec94457cb19111bca1ecd19e46124c6a5d29703d09df5e5ab521b2 +8 +~~~ + +#### Test Vector 2, Batch Size 1 + +~~~ +Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Info = 7465737420696e666f +Blind = de2e98f422bf7b99be19f7da7cac62f1599d35a225ec6340149a0aaff310 +2003 +BlindedElement = 909f8d2d517fa2235f8b35f91220636732541d9f3e309c6988d +6d8c987e5a357 +EvaluationElement = 241786b8f9da3e8c28d75dc23b5f8b251ec150ccb453efa7 +12f6e9b72e763a0a +Proof = a3748b980aec81add561bcd7ac4fe2b09a93bd8a127991788fd618bf7fb7 +93034a6f7f59cdcab538ed3e50d74b31f82dff14e3c8d3a081f744a6bdf93526ed0e +ProofRandomScalar = c4d002aa4cfcf281657cf36fe562bc60d9133e0e72a74432 +f685b2b6a4b42a0c +Output = a88ab2bceba2c9c5a0ee0ee45636e65042b5f274af864f8c1560d32ecee +4373c31907f237609d3f164beec32e3270588961c1d19cee467d2a3b0445ebdea215 +9 +~~~ + +#### Test Vector 3, Batch Size 2 + +~~~ +Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Info = 7465737420696e666f +Blind = e79a642b20f4c9118febffaf6b6a31471fe7794aa77ced123f07e56cb8cf +7c01,0bb106c0e1aac79e92dd2d051e90efe4e2e093bc1e82b80e8cce6afa4f51980 +2 +BlindedElement = 3206271954cce85425971fddfebe14acad819b9753ffc171815 +7e54a5e56542f,847b21e32855892256a3eee10ea5c512d362b34de1ab278573cf91 +edfcb14a03 +EvaluationElement = 76d3282ac9aabc9b0133df89e680ab0d43f2946c224db25e +798abdf0ed1d255a,e8483fbacb3e62787a803dd6d688e4db26be5392f529f1dd6a7 +f06e2b28dc52c +Proof = f5b8d39897f12dd1f8fc927e2f7f563629b7b45f1e6b5eeb469c043d2143 +7907a0e9236beec240a04e0fb906a7d126a8cb40e22730106446c1fa3a40a5283406 +ProofRandomScalar = 668b3aab5207735beb86c5379228da260159dc24f7c5c248 +3a81aff8fbffcc0d +Output = 4d04eccb77a29bd8a00fb1e3f391e0601340c3dc874fc7bb16cfd92d961 +532d18b4edfffaec94457cb19111bca1ecd19e46124c6a5d29703d09df5e5ab521b2 +8,a88ab2bceba2c9c5a0ee0ee45636e65042b5f274af864f8c1560d32ecee4373c31 +907f237609d3f164beec32e3270588961c1d19cee467d2a3b0445ebdea2159 +~~~ + +## OPRF(decaf448, SHAKE-256) + +### OPRF Mode + +~~~ +Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a +3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3 +KeyInfo = 74657374206b6579 +skSm = 416f3b0f4d13ac19e6aacade4ecf8b7e9c55d808311be2bea0dae4f4c56d0 +73e7229b8b72a8c7eb68bd2e98336baaec1ac47c82cf2c5e33b +~~~ + +#### Test Vector 1, Batch Size 1 + +~~~ +Input = 00 +Blind = d62851d4bc07947c858dc735e9e22aaf0576f161ab555182908dbd7947b1 +c988956fa73b17b373b72fd4e3c0264a26aa4cab20fd6193b933 +BlindedElement = d078a185d2d8a54b68d6df4e83640192d3659e18fec68d43e48 +02998d3c9fd819b32070caa78083c909d68daeb7fd420a73f931452a2b70d +EvaluationElement = 3452e46b6277b032627a7e5d22aa1b25459f8de90dda3137 +9ed490bb0078eeec05fc4265fafbb5252d4228f9f1f5453bbd391d6b8589f232 +Output = b93d3ed18489c1236cc965d202254de35767ea673560d6c225cec0b30fe +3adc88fee63f8a78d127cd64c7077e1d3ac4a7cc761335c0bcdc12d6981ad8730285 +8 +~~~ + +#### Test Vector 2, Batch Size 1 + +~~~ +Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Blind = ac345e8d755997956ddd1f267a2d86175aeae5e1168932285a6f602b4b20 +a570a697452b3ddbb7d0e29363adebbcb5673294396b82931f37 +BlindedElement = 283f0fab2be6ac3a3c8eacfd504f3ef63f518892f7b000f1dcc +1ca2e773aba0fbee48b100886b90d5a08377cbf5ccf69801ae2c23e1adbf2 +EvaluationElement = ae30bab51a34c45a76d00034b29e1c5346fbe3718c386302 +8e47226456880a85a2e5118f274a8c260dae62fcec3cde8624405fc7cddbc867 +Output = aaf99e5a044bbce915bf3ba381e25da62e4b2cea4cee2f47f3662940284 +579c0f8e1e011062ba010ca4f2c67a8157481c9ae7a458ea035a89e1948bfc5b8323 +b +~~~ + +### VOPRF Mode + +~~~ +Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a +3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3 +KeyInfo = 74657374206b6579 +skSm = 167e36766c08353fbc8fc46d89619f0306603a8ed33f7eafcca998ed2e890 +d15f64a22b0196aaa298e4a502275d4ced5c6131de64597c500 +pkSm = f27f3a898855240ef102d7bd6795aab2fa3972db3d47005cbd33e721cbed5 +a3fd37508d093ecc645fa80a7f928c4313cfbd4654e8ea7de8f +~~~ + +#### Test Vector 1, Batch Size 1 + +~~~ +Input = 00 +Blind = 4bdfc97a75132d92a1da241baff84fada3e7b12d5b712efcac9ba734d54c +2b24bff0ef6310404b5c05d60d7c258cea6500229ee015149f0f +BlindedElement = 1ceb0a3432ac6b583c31fa70b7c17ac86e0aa425e0593d04b58 +021670f725eee6664e6cd2041d90f157bc213a2aa4ed7929630b2d9898a76 +EvaluationElement = 3afaa02425294a4810766c68e9e4c3c507b109b9064ed56a +148a419371d5fb158f6ab5f0da62a6ba915bbe431097f5c71854821c1f10889d +Proof = f02f7ab2722508e343b5692078556e7ca9b2d63bf83dff902150b867775b +f375693cc6a0adf33178ba7e72d6179b36ed051065c93619752958746f0d52e2e3a9 +89d86df15f458847abdcc23976147b7b10c96452332aa03bfce1b89b7aeed080869d +7ce8c7acb7414e7dbfcda298b532 +ProofRandomScalar = 54534ad9db9f6df6ce515d1b8017923b65cada199e936a62 +3c8eb3bd08e9b3f6584a85e4ff26e9f869d30b6c7c6cc56fd94e306974fbcc3b +Output = b558e37f6435a12fefded196936a4c1d0882bf4a115002920744ecb3128 +43678f396f7d36711cf551750388ddf7a53a3aea7fd0ac60568cd2d4ead16a1ee106 +f +~~~ + +#### Test Vector 2, Batch Size 1 + +~~~ +Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Blind = beda1edc786e5fd0033feac1992c53a607d516a46251614940e76a2763b8 +0683e5b789398710bdbc774d9221dd33c509b4805fc26f0c8d0b +BlindedElement = 2e04b6883057a5b5ba020d077ae36dee76a07c2f3eb8cc55baf +dfb3da9c7405ffe50802f646ca3c3ef39d195c2d88ee56e73825c7cd2319a +EvaluationElement = 6270b2f73738aa846dca34d7b30b7c4f943e31d4d4fb35c5 +98f5d608cf25648b44553d43b158dc2707eda170dc439740c10d7b4355bf0f83 +Proof = f731f60aa18d508f07dd3b7851fe9f8cfe6f02c4ea2814cfe8af3203e493 +44041e6acf0f09fdffdc02d22728544b9bda8d0604e727f27a1efa16526f169191de +db35a1338bf399d8737d6d1638f6d4b895c0869b4194e66fb0dbb4b3e0437a2af0d7 +6dd8cfb0bf38c9de605dc5749603 +ProofRandomScalar = 00cc800042a0cff31f865698f8858efa75a1f0faef934317 +dd6a10bfbbb39f9f2d97dcd5ff4eae02980b08fc68da7b71d39399dc4eb0400a +Output = eb14608be2f14c25b2c9fdd23690d293d0c6aaac501a3405b626b8699cf +34bb9dd4c2d7987b6391519b9480da453611509ba98098b3e79a35acd00f5e9d8abc +e +~~~ + +#### Test Vector 3, Batch Size 2 + +~~~ +Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Blind = 89ae863bc6f3e8b59bbd1354548220e81cd0ffb6f9e4ec2173870ae684f8 +6b1c06e41ecdb9ef83429e58098b8f30a6b49d414ad5f941cf05,7b1f0b697d9efa2 +b60c984df38632f1096f2bf292478118d78e9edabbe9ad22900ad84b1a2cdcb869c7 +0c83260691e69ea7f473c3b478707 +BlindedElement = c86795bcec21f2b337865406ecbb9495dcacfb0b0d7d2a857dd +31f0f70619a403d42bb57fc53c9182878baa7be06e337c885ba0023190d63,7eba6e +7672c0a7cb7c725ac98adf2b081e05fce49bd5cbc6c0b687aceac45ee0aab63cb13d +0f0493a265996a7aa94c9b30f4fc0c385a36af +EvaluationElement = 14553405aed5bd3b2672fc74f52aa1d9efb9cdd5ec668476 +d74c60eca8930994aeaf61eba482173e5953988d702ce5175ab10c1585cbc4a2,727 +5d01889988341f8c9c8d0adaedb54af2d166112ac01f2c053fc772cb09d69a33ee0f +fc6a92ca0d752e35f4a33ba0677c37a3618ae07dd +Proof = 820c0da6f0ceb390355da6fb002549f37031e92337bde432d3518541d2f3 +e6e4f86fbaa2aa0aa53f15db278a0aa2d305226911e408c25f2bcb3a6774089d075d +3a92e273fbe5359a9c81f9e83082a2e8b02f34d248789f8da583296e7c531e9d8707 +90042248e589809a40631feaa914 +ProofRandomScalar = 7baaffc0af7cf69078ce1702514d93f32828684a1796b559 +988623c12413cf511d13cb07ecb6d54be4962fe28eed7d4386c156301dc2db01 +Output = b558e37f6435a12fefded196936a4c1d0882bf4a115002920744ecb3128 +43678f396f7d36711cf551750388ddf7a53a3aea7fd0ac60568cd2d4ead16a1ee106 +f,eb14608be2f14c25b2c9fdd23690d293d0c6aaac501a3405b626b8699cf34bb9dd +4c2d7987b6391519b9480da453611509ba98098b3e79a35acd00f5e9d8abce +~~~ + +### POPRF Mode + +~~~ +Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a +3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3 +KeyInfo = 74657374206b6579 +skSm = f68691e40ba92bfdf37acfff161f5404f9ae0e53c7cedb0a790ab17c4c0a7 +4a314c24974057464185e2d2e648f74ee6663443646db2c111a +pkSm = 2a742a63231b139ce19eab43e7a855f32e5dcbd16ef52a7f968456a814104 +5d49e3e28a995cfaa22ee104e22f2239f624b3fa7d41bf15186 +~~~ + +#### Test Vector 1, Batch Size 1 + +~~~ +Input = 00 +Info = 7465737420696e666f +Blind = ee671e4c9b6783bd5e4a55d2e8474fe0ec811b4cca7c0e51a886c4343d83 +c4e5228b87399f1dbf033ee131fe52bae62a0cb27eb7abfcab24 +BlindedElement = 4c371528ab436b8a6a5bea333cc5702c70cdddb80d12dc2eafa +06b87c15bba8b0b5451bc09f3d07e57c12af4c0398b09ae91b678fdeaf2aa +EvaluationElement = d27f65d6c41880303989752e40748e940add1ad32e7f76cc +bb873b7fff424d348ec8e43c11402e02934c1fcdadeacbca2d2e5171daaeef90 +Proof = bf2f61413c56c0351151c1995007ceb2e197c987056f20a54f0027e544a0 +b20a7891b9aa882203f2e09e1a0ca9464e3cdf130eea9e1123023460d3f280dac87d +23b8d2258666d002f57810d8847832b775984819e457c7bbe703947e7aeccdf59d3e +520437edefc26b814f9fa7fa9917 +ProofRandomScalar = c4b297c662a87631531aade91c0558d87224d92247bdfa41 +9a53af4cbdb352b0a2016e5e5f6c0bee4a642526ef9910289315b71fdee5df1e +Output = 1ffbf9591b674e6a089279a8319c75e949cc277d7b5c757361412180307 +90755e90af009768e1b9240c9734d8886c6121123384140b26c38c7a6c4217a1b3d9 +4 +~~~ + +#### Test Vector 2, Batch Size 1 + +~~~ +Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Info = 7465737420696e666f +Blind = 1abe4937f28f531b14ac96b844320e7a66810c2d9391cbb877348301ab59 +a3a91b4a2129672886ae5da7839f2ac8cf1c5fa92703f5b3fd06 +BlindedElement = dea615b00285247715173fc6db40cab1436607bc0eaed3d7a1a +1467b70c7ff2f2ce91c05bcaeda2b01952926f254f13e1a763a174caa693a +EvaluationElement = a692017b9e91efbe6641c3ef0cd3b352022ed08bb5ed0c1d +a0838589bebe53c2d2959818359cb0213b94cafa672673608b9a2280671d6c75 +Proof = ede122b8ce87d22fa1bb9dd38dc76da1a9ff812a8d2cbf3d2a6e86a10331 +a849d203bd925d6f130d80f333aa0443488731769e975b4c900d923d740fec13a61d +3175a0daf9a88d8f66704b36ca2b1b7fefd6cab4ffb50fe998e53ce4743ee9466a56 +886f79fd6d5b924553f64130c60a +ProofRandomScalar = a3e896e126d371f6380ca41757f6458b93b049e1b0d73ab5 +b8d914b08dff3e52e62ea8898d35b2862d28ff4c5f89353d25d6b5a8dc014d3b +Output = daeb206a0e1fc120ebe4ad885f851f456f7d8908166839b7dc541f71251 +4203d9a3589025b4bfad6a79c6d40bfbf217f44a9aa17874a1ec271b23cced72a44e +f +~~~ + +#### Test Vector 3, Batch Size 2 + +~~~ +Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Info = 7465737420696e666f +Blind = 255d8adc40b8f39f14cd8bd4ade8abbb95166afdc9e922203abe7a853985 +4c64b943b0b46e1e1b47cfb52e9a0867c8cde22bcbdd724d9f09,71bd897c56c86b3 +1b096103b7e2d26d0f4d66be95299379b41668dbbc5ece26cc212d9f2cbfaf479efa +17b7f6b056dfcfbee5bd7365cea26 +BlindedElement = 361b80bba04ff4b211e38e636a8530531213a44f44738992b18 +eaf0d9759eedcb7e4034e9bda6f8829250aff72343b0d2d1e23d612d94674,c68d79 +d1a614b90e6ab1dc14a982f9fdd423edc94a10d87d45e32935e363079967ad289482 +8b1764cca8dec5e9f919def474b1d03b6c069d +EvaluationElement = 32629ccfd36787d8d80756f025f6c23c21145dd22c28d974 +f34098e166a300731b691e1faa7e3959c1bb38312c43d1d693cbab4b90fe7d2e,eec +655a1a869b3f0f470f7a0f2cef69eb6539c6c1b9e49d9b380bccc7b510d466f45d88 +fa690b687a8507d1e0b275028d095292fc4aadd2d +Proof = 2f3501925c81837232ae34e5351518ad35e24f1d32f7459da3c19cae7746 +95e7dc1eca32133dd57cd0e2eb67c75c9edd9cd3ff9c5e1759314ea99a4eca322f6e +56f4b80795f67d1bf747834d2d7b3049351979ca876ecf28f87b81fba243269e3c09 +ea1889abd968af67c7ca511d0c3d +ProofRandomScalar = bbbf1ebe98b192e93cedceb9c0164e95b891bd8bc81721b8 +ea31835d6f9687a36c94592ab76579f42ce1be6961f0700496e71df8c17ab50c +Output = 1ffbf9591b674e6a089279a8319c75e949cc277d7b5c757361412180307 +90755e90af009768e1b9240c9734d8886c6121123384140b26c38c7a6c4217a1b3d9 +4,daeb206a0e1fc120ebe4ad885f851f456f7d8908166839b7dc541f712514203d9a +3589025b4bfad6a79c6d40bfbf217f44a9aa17874a1ec271b23cced72a44ef +~~~ + +## OPRF(P-256, SHA-256) + +### OPRF Mode + +~~~ +Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a +3a3 +KeyInfo = 74657374206b6579 +skSm = 88a91851d93ab3e4f2636babc60d6ce9d1aee2b86dece13fa8590d955a08d +987 +~~~ + +#### Test Vector 1, Batch Size 1 + +~~~ +Input = 00 +Blind = f70cf205f782fa11a0d61b2f5a8a2a1143368327f3077c68a1545e9aafbb +a6aa +BlindedElement = 0372ffe1ebd9273f17b09916d31e7884707e8902f7e3af2a1b3 +ae1dfbfae9b5126 +EvaluationElement = 02aa5b346b0375cd734014ffa9ed2135a1b07565c44fe64d +5accfe6ab6d8c37f77 +Output = 413c5d45657ce515914232ef0bafdbc1bfa5c272d4b403f2cea0ccf7ca1 +8f9be +~~~ + +#### Test Vector 2, Batch Size 1 + +~~~ +Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Blind = 482562df55c99bf9591cb0eab2a72d044c05ca2cc2ef9b609a38546f74b6 +d689 +BlindedElement = 02fefe6e044601a158175fb4bf90c06841ca7211dde4e56e5ca +c6dd45728cfa04a +EvaluationElement = 03167ed445f79ffa867268e30c0aa240ad1a863569016406 +6d833e350802e57273 +Output = 2a44e98a9df03b79dc27c178d96cfa69ba995159fe6a7b6013c7205f9ba +57038 +~~~ + +### VOPRF Mode + +~~~ +Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a +3a3 +KeyInfo = 74657374206b6579 +skSm = c8a626b52be02b06e9cdb1a05490392938642a30b1451b0cd1be1d3612b33 +6b5 +pkSm = 0201d3da874a209120ac442081e9ef9ed8ee76fda919d0f386cb5a0143755 +b10df +~~~ + +#### Test Vector 1, Batch Size 1 + +~~~ +Input = 00 +Blind = e74c5078a81806f74dd65065273c5bd886c7f87ff8c5f39f90320718eff7 +47e3 +BlindedElement = 029d750421c5c726658902c47d3675ebba01ba25d0bd127bf6e +338b801b166f1d2 +EvaluationElement = 0291e9890c7418a2fc1ac635d2650bae3f1a25a9ffcd0bc0 +1b3c39fcee4b095dca +Proof = 54ec2d8558f5c72ff32489556c3ba1f3087810c5f51cc025f07adc034df2 +dcd6d706e7bdae3119b70748cbf76b66d520de87bf90287a091cf6f8d2a465cf2200 +ProofRandomScalar = dfc19eb96faba6382ec845097904db87240b9dd47b1e487e +c625f11a7ba2cc3e +Output = a906579bce2c9123e5a105d4bdbcafb513d7d764e4f0937bee95b362527 +78424 +~~~ + +#### Test Vector 2, Batch Size 1 + +~~~ +Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Blind = dfe89ac0cdd6b74684580de0f95f8e06aa6f6663d48b1a4b998a539380ed +73cb +BlindedElement = 02e6085d4017ae0bede4b261977b588349d323414eb5c409e55 +2e2bd4c82df498b +EvaluationElement = 03a649c5ac48f33a6c6cd82120145e673e17395ca94ea824 +c7d2dda7203ba4159a +Proof = 1a79f6a52579f7acb0100c916390989a1dca3c1b3078402e102b8dd037f0 +b34d929d38239b34175f1328708ec197bfc532ef31dafdf1ee85db4ccf8769844fdb +ProofRandomScalar = 4f9a70536c175f11a827452672b60d4e9f89eba281046e28 +39dd2c7a98309b07 +Output = d13c62d285a71acb534dcebdf312bfec0e2a3fcb79f4ac32d2dfb0bc9aa +e3cc7 +~~~ + +#### Test Vector 3, Batch Size 2 + +~~~ +Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Blind = 9e68a597db2c14728fade716a6a82d600444b26de335ba38cf092d80c7cf +2cb6,d3d6e1e1006dc8984ad28a4c93ecfc36fc2171046b3c4284855cfa2434ed98d +c +BlindedElement = 03b2332e9dc41bd9b7997df58c1f432d13c4f018cb2095ef8eb +14ef3b323aceb86,03eea6961f7f16deaa8deb6f68a865ae04d8be760626cad589b2 +2cb90262e30b0b +EvaluationElement = 021f7b60c7c53fd3a6867cb38bb7f6febdbd802a78d10111 +00a779b67a801c3bbe,0266e83c8c525cd612669737496f0a736feb7d4209a520d2c +dc204971215db0262 +Proof = 92c5c32bc18f3f5dbdd51473f4e3ecc9b07797c63d679be5399b223ae801 +ddd1e469df512f907d317a0930dd0e644b26c96edc87d2f8e0a09e66bc73db8647c5 +ProofRandomScalar = 6e953a630772f68b53baade9962d164565d8c0e3a1ba1a33 +7759061965a423da +Output = a906579bce2c9123e5a105d4bdbcafb513d7d764e4f0937bee95b362527 +78424,d13c62d285a71acb534dcebdf312bfec0e2a3fcb79f4ac32d2dfb0bc9aae3c +c7 +~~~ + +### POPRF Mode + +~~~ +Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a +3a3 +KeyInfo = 74657374206b6579 +skSm = b75567bfc40aaaf7735c35c6ad5d55a725c9d42ac66df2e1dbd2027bde289 +264 +pkSm = 02eca084e8d6ac9ed1c5962e004e95e7c68a81e04be93ceabf79c619de2bc +c3eb9 +~~~ + +#### Test Vector 1, Batch Size 1 + +~~~ +Input = 00 +Info = 7465737420696e666f +Blind = 4238835743037876080d2e3e27bc3ce7b5fb6a1107ffedeaedb371767432 +b68c +BlindedElement = 02cb57f07ba100b93ce1bf8176963c8c7f73a76827f1c1401a9 +23d7ca4083e15aa +EvaluationElement = 03059d58ec9a801e33f57525c03241d8ffb61b67a18edd35 +222d864ffbb42b5d2f +Proof = 13889d6849850ccd0119981fc053a38a30a57d275091df2887943d1332f7 +38204f8a6cf2fb6e57c9b118ec82b9b012f8864561e4cd8866245f9c762b9d45dbf9 +ProofRandomScalar = 3d5c65b55a1b8960563b3420d7764097502850c445ccd86e +2d20d7e4ec77617b +Output = 15fce9922a2307349aac2eccc41941283e3c5e938aaf2506f99a6d8b6ee +34ef8 +~~~ + +#### Test Vector 2, Batch Size 1 + +~~~ +Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Info = 7465737420696e666f +Blind = c262bf51dc970d63acb5ab74318e54223c759e9747f59c0d4ecbc0873026 +67fb +BlindedElement = 02208809631cc08f553d7843db566c55746e760a77c63513d2b +22096f98452cf9d +EvaluationElement = 02301ee1cf1d01276649ac0f718ebbfa1c0d6a1b3e7ea82b +3085e9173910fcb0ef +Proof = cb69a1ec76643a2100cc9bfe6cf1ed1fa5ba3612ed3e3211036b5ed835a1 +38be3eb92126694e3e925ab138d4df885be18ed80371847f80baab82ce70588eebaf +ProofRandomScalar = 6c6990f0fcd9a655f77ff0b2ebcfe21e1a1ca4a84361e9f1 +b18e24c9a40ed5ef +Output = a06ed7380210856caaba173bcad06266186c6638d86e372c3c96b9bd2f3 +53543 +~~~ + +#### Test Vector 3, Batch Size 2 + +~~~ +Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Info = 7465737420696e666f +Blind = 3f95c9b1334d8af16ae1e69f5adc24e5aa89ebb63637c835fd39b17a1a44 +53ec,9801a9d83b5d1c0fc0812c10e18f146b14d7eb94755a918bac1ef8d69d21a7c +2 +BlindedElement = 0277b1e97f06cb0976bf196a30dc7b3f635a40ec1337d4bda00 +f809eaf244f7133,0306823c1610cc81b08db444d7c23cd368e8b6fc1a7fa3d727f2 +8bcc7d3afa9c41 +EvaluationElement = 030f395a0e0328018c78de95ff498a0afb54fbcf34197226 +49e211940852ad0171,0221335509649461a4d201d2887af62313466af660559c348 +e8ac326fbc1c147af +Proof = 187aaa12108c49c1395001ccaa677519572ac4680b0f41b346b9879d3ea6 +fdb9fced5c7f20b351d03786d031de79cd3c03723ce48053a13b640fe6051ee3584e +ProofRandomScalar = fa0ea4754fb56527be010296ea880e1c6a4dbbc9ede543a2 +ad0f83fd60fdacb6 +Output = 15fce9922a2307349aac2eccc41941283e3c5e938aaf2506f99a6d8b6ee +34ef8,a06ed7380210856caaba173bcad06266186c6638d86e372c3c96b9bd2f3535 +43 +~~~ + +## OPRF(P-384, SHA-384) + +### OPRF Mode + +~~~ +Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a +3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3 +KeyInfo = 74657374206b6579 +skSm = 8b0972b97a0339dbcdb993113426ce1fe1b11efefe53e010bc0ea279dda2e +37ac7a5599acec1a77f43a3ac7a8252782f +~~~ + +#### Test Vector 1, Batch Size 1 + +~~~ +Input = 00 +Blind = cda63dff3137c959747ec1d27852fce42d79fc710159f349e7da18455479 +e27473269d2926fec54d4567adabd7951ad6 +BlindedElement = 020db1b05b22ddd8a851792dfb5b10b4f237d69522097cbb012 +7ae537e3256f86e35a72554a6ebdb26c28342fee16473dd +EvaluationElement = 031e6e8c82d3284727a724a5854b3e2bf9958b4e5470601f +4ca37d33d26879eca817796cb7e98bbbb1d1739eeafb33c027 +Output = b2e380ca96ea80f7550a6b663e5f7752d7d7772c46169d72308a8425903 +1e804ba577ac34e632f535a9519a692734016 +~~~ + +#### Test Vector 2, Batch Size 1 + +~~~ +Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Blind = f9e066cf04a050c4fd762bff10c1b9bd5d37afc6f3644f8545b9a09a6d7a +3073b3c9b3d78588213957ea3a5dfd0f1fe4 +BlindedElement = 023c36bf6352c93d27b118972d1040cf22f99d5a1c8134afb89 +8d30b319f70a096973db23410881f84eea599c0c73220bd +EvaluationElement = 0240b6a002d0190793ea62a7499244027753d63b0a57cea1 +98c8c6dc883cdeb273ab385699bb414f1040bb6819313cd675 +Output = 1d155a7ba2ea75c4f1e76fb0a37231e9b0776eed3f24a6541a01907ca8a +fb984a74408e6d2de8e481cae5dd03bdae3ce +~~~ + +### VOPRF Mode + +~~~ +Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a +3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3 +KeyInfo = 74657374206b6579 +skSm = 70855cb96c961b39ea3ea5776d89c8b7623f5891a26e8437f86e2c713bdb0 +da23415590a28184dc22088a215ebc7fe45 +pkSm = 02d7bdae4b97ecf0fbb8c00cff3a3a9b6d0fb0cc34f8490a98a74dbb59a85 +f43bda8ca7b3c0b05164f38d8efdef2c3426a +~~~ + +#### Test Vector 1, Batch Size 1 + +~~~ +Input = 00 +Blind = 61247a74d0c62c98ddff1365bb9b82b279e775b7220c673c782e351691be +a8206a6b6856c044df390ab5683964fc7aac +BlindedElement = 026601d99c313b827a09aad832fcc814ac5257a57bb49d65c05 +e247df9518315a66557fc8af56b4521c51900aaab1a2ea9 +EvaluationElement = 020b478b9c9b1a5935e07fb532eac2e596b78170a0e755ec +c71829419e63a2119eae23be281e109de205cd85af7e42228a +Proof = 02d0946f1795048bc803171aea5b4a9a5f256bd5fa9414e5fa76dd17a4aa +a94307814d57c2cca239485e29bb76d4ac1b4d3d62dfbb8e43c7135b2ebe50fe923e +30bd99e1e6ec961db18fa6e67c63dd6652284c15860156c08d64d838efbeeb68 +ProofRandomScalar = f5685928c72d9dab8ddfe45de734ce0d4ff5823d2e40c4fc +f880e9a8272b46eea593b1095e7d38ba6ff37c42b3c48598 +Output = f18884ace2e342f849cea7f2f17de902b9884574fdaa8f507356f482c6b +67013f329e8c899b3c2c154af1defaa11d656 +~~~ + +#### Test Vector 2, Batch Size 1 + +~~~ +Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Blind = ef54a703503046d8272eaea47cfa963b696f07af04cbc6545ca16de56540 +574e2bc92534ac475d6a3649f3e9cdf20a7f +BlindedElement = 0279e61686e698fdbac5cf484f54846db8cdf6f403fa88209c3 +4c56c584fe4ca600ac81b61aad11c5e639ff1add3b30de4 +EvaluationElement = 03900e8e3f5b8bf698e7aa0aacb8dbdfaedf80220b1f640d +e2049615985b19b913569cc2feb90725a3661146fb88ef3755 +Proof = 343536346a1145b81336eafc239f225dc6a154752492707c0465f029aa9a +f0fe2bf0428285e43b596db633b50f0801b62b0e9c64c62f329b8a84324a415e4a58 +6cbf9477b1285c9b74f614c352e06658a8997486b8177006491e84aa96a3de09 +ProofRandomScalar = 0cdd9475ad6d9e630235ff21b634bc650bf837aaa273530d +c66aa53bb9adb4f0ed499871eb81ae8c1af769a56d4fc42b +Output = f91d172cdecdea4f8299c8b39426db4c47428b82f8872b8539ad9b019de +b48b8d3c928c572ed988d5591a4442c060438 +~~~ + +#### Test Vector 3, Batch Size 2 + +~~~ +Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Blind = 485cccf5018abbf875b8e81c5ade0def4fe6fa8dfc15388367a60f23616c +d1468dae601875f7dd570624d0ae9d7be2e7,b0d53a6f8da29c3cf4f8695135d6454 +24c747bec642bc91375ff142da4687426b0b4f35c14eb2477c52e1ffe177f193b +BlindedElement = 03dbcb21b211e7b5d2cf0c36d782308af28458539423f67a293 +36355e55035137eb768b1935b5a825c589a2913f0c2894e,036c8b2fa4dd9cd05756 +1d377b4686cddc82317ad3e5eda08bece2a8616ca724937ff933e340a47fc09bfe9b +0fc1ef9ab6 +EvaluationElement = 03a1cba477a408162aacdca43e059309fd61cc14687a107b +d492a1ec688a010ff49c60684e0f973412a7da2e627b1553a5,036fe6df8a99bb7b2 +c4a5020ab4c6d7e71b5abca2d5d5a418f2314b614deb40c7b3acad982951b5f524e5 +6f0e9ac7d8e95 +Proof = 1e26bf1210717b88dfae585008100e9ccaebe93b8605ca168a608cbf1855 +697b7b87d0b9c6bdca85e43143b3630e87f2fe9ce519dba3d477d2a869bcad0db9dc +6239cd11938213f9bfd63d39de090a6fc90cd1f33f164b2c54c38bc31ad98ddf +ProofRandomScalar = b36f4c2a140b7a3c53dd8efb6171d3bb4d73591be8483a1a +38e40c13a04b0f2180dda3c36e3d43c3a8f127158d010945 +Output = f18884ace2e342f849cea7f2f17de902b9884574fdaa8f507356f482c6b +67013f329e8c899b3c2c154af1defaa11d656,f91d172cdecdea4f8299c8b39426db +4c47428b82f8872b8539ad9b019deb48b8d3c928c572ed988d5591a4442c060438 +~~~ + +### POPRF Mode + +~~~ +Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a +3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3 +KeyInfo = 74657374206b6579 +skSm = 2b65a799c107905abcc4f94bdc4756e8641112ae1fc21708cd9d62a952629 +38ded6834e46bad252b4e533ee7eec7e26e +pkSm = 0286f37b6295bba7ebf35d2bfbb944d441fc416e51eb5ceeb63ac98afa6a6 +27ccafe20bd600c728bc5b1300148ef2ba6e6 +~~~ + +#### Test Vector 1, Batch Size 1 + +~~~ +Input = 00 +Info = 7465737420696e666f +Blind = 9572d3a8a106f875023c9722b2de94efaa02c8e46a9e48f3e2ee00241f9a +75f3f7493200a8a605644334de4987fb60da +BlindedElement = 0252f98f04a956afa469c62ca2850f751b112dc019d4e713c66 +2fc0735ef8573f1497cea55b750f27f0efc8330e394a3ab +EvaluationElement = 03fb20c33a7f6f01f2bb388318a6db84f7183bc3bd5e5840 +302fe38b6b313649b523238b4c4c625614440dd6ddbbcc7272 +Proof = d33c83c1840a48759659a4d417769ae3bb1adb86326a36fa1ff24f70066b +75d0200e5c1e7d9847e91f7d3d6843efc62101c401a7c952cde32ada6fec848450d8 +564e2c778af47ece4f50a88c6d2281bdd858b90fdfad8b093c986bc1e59aaa2e +ProofRandomScalar = 7e82569cb56d97e9c20e59311bac3a50735d573abb787b25 +1879b77de4df554c91e25e117919a9db2af19b32ce0d501d +Output = af52cf184180177970be0770e1c7920aa307b767556a13de38a64723d8d +cc7b344af9b6dd8f117ac2cef249ee3acc8fb +~~~ + +#### Test Vector 2, Batch Size 1 + +~~~ +Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Info = 7465737420696e666f +Blind = 01e6e57b7ec6752a45c74f3ed36a3eb8ad0dafb634f668e415357a04fab5 +01c0f6764e854701129e38071b008286c5fc +BlindedElement = 0257c264a1016e7a1a8236e46cb3bc11a0f13178b03262e0153 +1da14a05e75a811ba4669fc41cc9453298f71c23834f91c +EvaluationElement = 0295529cd99f4255be59966e430bef38c93a5261b0624612 +091327c9aedaaaa40d22b03280ec15620bc91d48970f18c68f +Proof = b5dfd5fc5ddc61ad8234c544aacbf280193da985d9204d5a30ef9d1a5964 +c1e70ffc3d9c986c93f561ab6f91f012c8ef9b9b6f2d1c178f01fe172c37c98fd4ef +05e5b15c15e810241ed6dda051500165d9f79d4e83580ea4c810a95dddcb593c +ProofRandomScalar = 6b61028c0ce57aa6729d935ef02e2dd607cb7efcf4ae3bba +c5ec43774e65a9980f648a5af772f5e7337fbeefbee276ca +Output = 8bc546462de3087cddafcf81435d5802c0c31f557c791b115a092d5b71e +a2b6e20986bb624ead85c7a63c976c05dcddd +~~~ + +#### Test Vector 3, Batch Size 2 + +~~~ +Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Info = 7465737420696e666f +Blind = ebd2fec41edafcba833ccaac567c14d2fa01f55b33a2fbbb37118f2f5603 +b1298346e02cbdf55c95ef9b1aadda5ef281,be210603388cbcabb8cb630aa1ad04d +73e349009a438ce248380bd4b7e6758211fe9692922fb61f00f1a39bc735cefce +BlindedElement = 02b1938a7613e9567a67aac83c50529238e3323c212dd407491 +1980c4d998479174ddb9c925d1b761b33da2ea0bd0ea057,031b9a47b7caf732ff32 +db035d1d073fd925c17dbb6c83e00a49af674166bf264bdb00c303edb26af96fed6f +e9ce44dc36 +EvaluationElement = 02c75f2d383c18692e0e11b08e9187c4c047d28116977c8e +5e1e872f1cf5eab457c04fd50274cd5cc4b1996a607470694e,02e72fbfff4c70479 +23b967ee9a6b37d902b49a465242c12b2b910daa5f30c3f947899283ed0a6c758348 +55a1ac0c64065 +Proof = bded438b699d3bb8bab26954f9a7fb5bb402f043c3364dc4f2b68976748a +77868dec1fa2d0774d306043ee8abbad5ef8ceee6a331be2906124f53f37c96d7f5a +4aa543053ccca87b577a32d803c3ca4841e37b3c4b5cf20aad11c59dec72a350 +ProofRandomScalar = c7a86f11c143a291e349b70b34e67b38fe9dc6f90b473750 +87d72e891df74070810500dfd391282c15d87bacdc9867a5 +Output = af52cf184180177970be0770e1c7920aa307b767556a13de38a64723d8d +cc7b344af9b6dd8f117ac2cef249ee3acc8fb,8bc546462de3087cddafcf81435d58 +02c0c31f557c791b115a092d5b71ea2b6e20986bb624ead85c7a63c976c05dcddd +~~~ + +## OPRF(P-521, SHA-512) + +### OPRF Mode + +~~~ +Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a +3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a +3a3 +KeyInfo = 74657374206b6579 +skSm = 00dc7a8db919a1076810a0c1503716d91668fa9edc60952317f26d47a090b +70dcfd3f530d07f48675cf8236d1daa81f3ff0f289942632e5cefd27a2190f0cefdc +302 +~~~ + +#### Test Vector 1, Batch Size 1 + +~~~ +Input = 00 +Blind = 00b638b3000884019316267eae9b424f812592e4dc9cd7f7aebfb1d3d2b8 +c7fa7904503aef20c694a01d3e1154fe98e7232be9eaec5789a012a559367b1f9965 +4ddf +BlindedElement = 02016f3fc7b3c84f673c75b3bb3e00ddd81e734cc84fe3bd4a7 +671e0a971879b7678c048f40aae87179614abc2261522303257a92127a195298744c +54094b7b87499c0 +EvaluationElement = 0301ddff1ac88acd812a2917cee4917f8a692eaabf9fd052 +9981441b83e368175b566657729a8be5ba2573e33e7734c146ef4c8b7d41f4503842 +80797318ff3a62d79c +Output = 383e3098d74b43f75d2e1136d7e7c08702d992e6f5f24f2bd438f98b86d +9d143ce87281b2daf7d67c94370903ba81495655d6e9626443a895b37bb74c0276f2 +a +~~~ + +#### Test Vector 2, Batch Size 1 + +~~~ +Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Blind = 00219598d5f1544830f9d667b683234c68ef3db95227fe3ebdfd963d0307 +0055fef107bfeb3c79c86b934061f894227b23a69eb0b53f168a4a2230ef6a7d703a +c4ce +BlindedElement = 0201e75b88d5de2b839f356a05c359ed610601450d83dcc9def +649fbd00a9790161e9333cb07978d1567ecab037c498ae2b00d9181abfd7bcee3fee +dc11de88c54190f +EvaluationElement = 0200b99d9694bbf9b9ea783e18e5fd049fb2fdf169cf386a +be304ccf8cdd633f7a1e25083ec6a6ca3a6e82367b38ee3c991e024097cf6fad928b +023817cdc5dea21751 +Output = 5100f12a88477ba993cfe8eb5a82a835892b7fa3bdb47dc1db19725e4c1 +138798e0f965df4f649e3a159aaca1fdd07034f7b91c0c9ac3d064b50953bb5c867c +3 +~~~ + +### VOPRF Mode + +~~~ +Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a +3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a +3a3 +KeyInfo = 74657374206b6579 +skSm = 00ef23ce13076b43a5a33e8fb8f94c940bbeabb762e5380d69cc9fa82c22a +8de39431c99e8b9d9fb75ea90446f895db04fe402b8be2c9df839f7d10ea6a23e7e0 +eb4 +pkSm = 020006090cc9a6f2eaef7e12759a8b5362e9972b4f36c4b3a3d71c4b67469 +638593ed8f46291542e0f04fd462a8e8ab96047be087a9d3fb182f4c138c6fc95659 +3b205 +~~~ + +#### Test Vector 1, Batch Size 1 + +~~~ +Input = 00 +Blind = 01dd6b45efbc57c5f087181c9f03d5b5e51b3a90cc9da17604b2e59a9375 +9eb0985d2259c20e3783be009527adf47f8fb5b1437cba7731c34ac70bc6ab1b8c14 +ff42 +BlindedElement = 0201b93fc5997dc0e8acd5b3ffa3a6f1be1522986c17ed60e5b +ad7b057136b3b7e31b5a7073a744a1304bf9bce4a27b02d77f1caf73a5f72686fa9e +83dbe9f730b4304 +EvaluationElement = 0301142205a8fb983efb6d76111db30e2a6e7c54724fe0ee +54f842a477cbf03adcb2cca8df2f165a65694e7a056948f8afd651b32ea8153cc26f +819cc5b1243f383910 +Proof = 00fd70ea3e4b7b008de749adb7403ca66f7aa56ae06a587d7d74a06daf6d +5c4dcce8a81acdbee1fcd21ce55db4440383b9fcfc1d584db6fa022a5fd62595c7d9 +39820116ad656f5ad470b5bdd208248a0a0b064960c80e180239691c5eb77b4a760e +eda8c27cf3cddc37d6329ad4997a37ffe9aba6f96d45e3e67fad86ec7b1d3a47487f +ProofRandomScalar = 01ce330164821b9b2a108e3ef8964622075015ac9ea0f838 +0dcce04b4c70b85f82bd8d1806c3f85daa0e690689a7ed6faa65712283a076c4eaee +988dcf39d6775f40 +Output = b3e837431aaafdfa8efbf486d70ca2d4364ef86afc7a8941d9bf1a6adb7 +bfd8c5302f91ee5796d956b5d3ea95fd0138d55d3059b1f4febf8cfd552e31fa2cf9 +7 +~~~ + +#### Test Vector 2, Batch Size 1 + +~~~ +Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Blind = 001745a97be4680b39889979a8b4b4322450628389ff2d90c0799597e99c +926ae54b2fce5ca13daa8cabbd4da53324fbd20554f2c56460442edb7d6ee76b64ab +68d1 +BlindedElement = 0300c2eed082810750dc327122ac1d9de647d1943f1767bd546 +56835ef71dd68347436c121df49c997b0adb11cc421ec8abc5611b6d9e86468e4666 +001e3db386ace9f +EvaluationElement = 02002ddf4ceaed6eefea8f12ca765c6d800e3f514f4fc75f +52f55a555cbecfb724f2bde39d5890f4b0dfcd3ea9029f663c7babddb30dd0692bfe +0f76190b7ee30bf68a +Proof = 018d1fbf1bc79cb4636d905f03bba48e0d872ac89c4c7e8bcc6884dc796d +22ceb648dd373b196d23335052a8d8013b154b38d233d68612213e3cea8f9024a1d0 +1e160004438ef7171638b7799a987739064e2313f9a902413d1f11f0eb5c7e8ca00d +9c83d03aa12bef8059144bc169bec7f4258858438daaa3ebdacad658e5f3eb7c6d5b +ProofRandomScalar = 0013559a0599ff077b4ebcbe7f73e9fc1bc25fff3fc5fd6c +8bc664e27822fdece106def4a69460e9777347a314fbbe5035803d3aa65819e81997 +c4d89909e25ce20e +Output = e8f92bac6c7ae89918d724697d8c45da339f55b61d527c50104e6658280 +3a8e6dcceae31b0d499e471aca460194a011d6b8b94fe2886b8b5a0c242079bfbf09 +c +~~~ + +#### Test Vector 3, Batch Size 2 + +~~~ +Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Blind = 017b1679ed98960e4cee27f330d5d3dccebf40596dc7e8b057938841423f +8b336f12c6c4dfa3a822d8f670e5aa46e733baaec9f93d5e14ad9ab99dfcbcb2ad15 +7a8b,00010fddc6356f1aa3fb05702631e213b4bbbe8fe5176fff25526ed5b1772ba +6164952c3c2da8017fdf337f81f5cbd0ec805923a335fa1bde3dbb840b3924c5ceba +6 +BlindedElement = 03001658908fb353f15fd637ec5b9703cc1dfe5a8aab4f5fd51 +9c0e41f69300769918d28963c07e5678ecb98c235c406f29dd1cd1dadadb4e23b98a +1cb290992b9aa46,030123068d8554e65999a6df1fde0bf10550ec5e223ef1d30c07 +3dee509933502a59106aae80e67d33f93400e2c32b5d9bd49f4e8cb97f08f4181998 +d330013b9e07a3 +EvaluationElement = 03012d790d77da1675a8e4ca5d9ce622046e31ea4af9511f +a32ef80fbe60e9e04866feeb7f31818c1ff0ed263cecf07f787129450f9f7322fa9b +593edc5cb626d1abb8,020198f45b9988bf0ab56c37bbaed0df4099c9b7b7a89b5c7 +030a451687cd9f8fd777f587dd7dcd717f529149d21c0763da4a03441d96e6fa2458 +88a352d08ddda70db +Proof = 01d6f0f91660fc391573f819587ad310bf3da80a2dd4cda113c1fbbec9d1 +cd88a0e4d783833c4c64bbcb6414688d925af2a4a5845b2e3b6634df5e5d901047d5 +148f01a1ec61207132162fc851126b07397beeeae6814e8e135e4a03ca8bd346568d +23c85e0a64b9b83de207ff8d17674863bde02eaeb0fa16d05a9b44cb01654bd0806d +ProofRandomScalar = 001caeef2365ebf9c1edbdb24825e5735614aaf644f03458 +a1f30c90229f8068bec0ae930eef110e98ea1cbc6d849b4c9ca5b7a970d0320ba5f4 +f95f5cd4f501d720 +Output = b3e837431aaafdfa8efbf486d70ca2d4364ef86afc7a8941d9bf1a6adb7 +bfd8c5302f91ee5796d956b5d3ea95fd0138d55d3059b1f4febf8cfd552e31fa2cf9 +7,e8f92bac6c7ae89918d724697d8c45da339f55b61d527c50104e66582803a8e6dc +ceae31b0d499e471aca460194a011d6b8b94fe2886b8b5a0c242079bfbf09c +~~~ + +### POPRF Mode + +~~~ +Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a +3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a +3a3 +KeyInfo = 74657374206b6579 +skSm = 01f8c6fb8fb3265b70e97870f942f7de645132f694c139446ab758871bdf0 +058a2a83b4679fc9fc1c6a0936b2f2e8e079a75b20688d4fe828e74d16bfc6255289 +92e +pkSm = 03013db33ba3e475e5696be39d99fd9ffd96452c4fe78df4eef5723097943 +1f734aceefad464c4885b99313a775f5f4524db3c8404400169fd139ca053b75c6d7 +e848e +~~~ + +#### Test Vector 1, Batch Size 1 + +~~~ +Input = 00 +Info = 7465737420696e666f +Blind = 00dc9f04fb076cffe7d179d692a05b0c2210b6c008c1062c1e54514ef654 +eefc0519dd1867571c9d518e305fdf463231b6ec8b7498e2122a7a6033b6261a1696 +a773 +BlindedElement = 03009a6b363627cbc6ba5f241493a724a69ca7a85f203fb5100 +bde9f36ee57e3fe75a5b41d10c6d9a2799fcee9cd1f4bcd730cb8d9be7aa5e8a7a48 +8b6ae3004afd2a8 +EvaluationElement = 03009ae81470679a5c5733401488cc6648a522a208e698e9 +879307e794158ce508e08a50556ec66a055f05f5d5276231258d95d004a49a308037 +2f3e9d2075753c010f +Proof = 0122e18e5c3e2242617098cf1d6b5868d66fb4f4816ddd3769e5b7f326f0 +ea3d79cd8b8b87be31c1acb9559a2ffdd13f4af7ee143e5081a2db996f3a7d2da839 +73e100f559c9dbb7b16df3d5f609d2f8f2184e9e204e6444db72608e4816beee31c8 +59dfabfe137bc3bae06947d767cd8cb6ad634134cf6faec24bc8341d51b584872ae1 +ProofRandomScalar = 00c07a53a1c70f44466b3861be4f8ef48c2bb1aec2e478e3 +41c467fd4a2638aeca63ed6c4bc48d008bca3f36f043e0eb73a44aba77e5e37d5ab1 +389e09b80a34cfaa +Output = 70ad5e29de9f6e35f16afab3b97c1b26fdf6be0da60aff48a99980ddb8d +7c2d728a8a5d2837179bfddd612712e014c0c9b9596cbb5a6ee6761c564dbb8921b4 +e +~~~ + +#### Test Vector 2, Batch Size 1 + +~~~ +Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Info = 7465737420696e666f +Blind = 0085ad3fc8c91caec3bd7699591b10d6da93877a470e128f38030627dffc +bbf1f576b38677841fc47af778f9d85ac9bce6279388ddf4607e295e64cea6f4f950 +78b8 +BlindedElement = 0201eeaedeb3692cc0ecfeacdf9cab61947eb0d23bbfe2e1fbf +8de0907f9410b6089d060d3af63411fd81b9d588fa2c48bf8ec63ec66c14b86d2371 +24042ca83fc99e1 +EvaluationElement = 0300886138e19945036ebe6f4195cf9f688d9e5a7c89597d +feea6e0e5fcf4b53a9dfa280c8409b6abe8051e3394279d0b669440af8a27aad169d +e10446eb88e09d6801 +Proof = 01cb4d8a14eeb472ee3e2fbfe3f6d49f3654cfe6238254bea17ce30848ca +934e20e82c2a33d140de55b24fab047811e20b46f6dcaf3c0945c802e84891316186 +17ef001e233aa2c3d674bce7465278faab6300d4f6b5463e1597d74e2a69865bb068 +1604f9210edbf50bf341d836dc09af85e603b4b2b8b55c90c2efd979a4e312b653e1 +ProofRandomScalar = 003a09eed29f2e7f8950d766270d390db7a53b8080b89cb9 +e024e1e008d83bd90e94f501281b6b49c351c959348b3a65f24c6f74e77a62905a6d +3e4b0b10600a7cbc +Output = ee2d8e42030da6283ab59a11f41a171c65e208306e00c6f965a56c10f33 +bf0942bb38b7e1a33c70bc3542d27220379cbcef8b91898c720be948e9db214a14bb +9 +~~~ + +#### Test Vector 3, Batch Size 2 + +~~~ +Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a +Info = 7465737420696e666f +Blind = 019dd87ebabccec2627d4006b698d9ba57f6e207c989448d39fe0431e60c +9a9a4110596d5a16fa6cdf3f66467524f295b5dc8f3492c6da02dd7387bd1dc40065 +b232,00adaeeed48a6f9a8fb57640c3bff88d3ab3cc52ef969f02beaba2c6e32c2f3 +7baaf4ee9c691833dc081e2a0fb6ff636525457a21c1fc56bf3514635ac7fb8618f7 +3 +BlindedElement = 030170a994d40e8517aed3a7efae01b650dc131c1ae07158f02 +ad70d211348a4b328add9d17e93d2e747dd8bc6960a5ab3bec6a9a29f793bf5663d0 +ab108b5f84fa751,03011045c5ea9567626cf6d2baa158830b035e66c249df4967bb +bf917e64bf27e4ec49623704a7c621b32f05e1c7bf1b89960c82d4203c4efa6a1056 +e083be789d017f +EvaluationElement = 0300fadd09cc84c7e91c2173be0e65ee3c1b6ef98cf0cdd7 +57ec432f12f13b5d457edc0311d61ddd831f8becd5231bdc492e92c9d0c103e55ea5 +16ee00fe64c10d0e8e,03005941fb7eabc6353a5c2e4a6b284b7b8ee8da6c4435af6 +c4c472195bb0deb44e7dc215299c7fe38feafa2b0a1a1db7dd3090c5ce8171247f64 +7da20e04acef8164d +Proof = 0186deebd9e2db71ca43bcb57311371390c2d04ac9c3189e155f10c9c548 +f6f22c051d38203493176e8392ef405c783759c735cb6f7219636c140cb2dc070a11 +58c001bfdb12f34e00a582e22e1eb2fbdebcffe4b6e0818de5c50451617213e1ab20 +fa5392eb3b535206140a2619732c012d5f331a615755f5397feb9e1fb16d1320d20d +ProofRandomScalar = 010a82559ee5e4ba79c390c4033405e3f792bc49daa905c6 +94707e7e0191104b34d68c7cc81c2e392da60b838eadf434b693d9b4f7c7beb31e37 +008156656c19382b +Output = 70ad5e29de9f6e35f16afab3b97c1b26fdf6be0da60aff48a99980ddb8d +7c2d728a8a5d2837179bfddd612712e014c0c9b9596cbb5a6ee6761c564dbb8921b4 +e,ee2d8e42030da6283ab59a11f41a171c65e208306e00c6f965a56c10f33bf0942b +b38b7e1a33c70bc3542d27220379cbcef8b91898c720be948e9db214a14bb9 +~~~ +"#; diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 7665c25..56a7fc7 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -5,7 +5,7 @@ // License, Version 2.0 found in the LICENSE-APACHE file in the root directory // of this source tree. +mod cfrg_vectors; mod mock_rng; mod parser; -mod voprf_test_vectors; -mod voprf_vectors; +mod test_cfrg_vectors; diff --git a/src/tests/test_cfrg_vectors.rs b/src/tests/test_cfrg_vectors.rs new file mode 100644 index 0000000..7d61bda --- /dev/null +++ b/src/tests/test_cfrg_vectors.rs @@ -0,0 +1,465 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under both the MIT license found in the +// LICENSE-MIT file in the root directory of this source tree and the Apache +// License, Version 2.0 found in the LICENSE-APACHE file in the root directory +// of this source tree. + +use alloc::string::{String, ToString}; +use alloc::vec; +use alloc::vec::Vec; +use core::ops::Add; + +use digest::core_api::BlockSizeUser; +use digest::OutputSizeUser; +use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256}; +use generic_array::ArrayLength; +use json::JsonValue; + +use crate::tests::mock_rng::CycleRng; +use crate::tests::parser::*; +use crate::{ + BlindedElement, CipherSuite, EvaluationElement, Group, OprfClient, OprfServer, PoprfClient, + PoprfServer, PoprfServerBatchEvaluateResult, Proof, Result, VoprfClient, VoprfServer, + VoprfServerBatchEvaluateResult, +}; + +#[derive(Debug)] +struct VOPRFTestVectorParameters { + seed: Vec, + sksm: Vec, + pksm: Vec, + input: Vec>, + info: Vec, + key_info: Vec, + blind: Vec>, + blinded_element: Vec>, + evaluation_element: Vec>, + proof: Vec, + proof_random_scalar: Vec, + output: Vec>, +} + +fn populate_test_vectors(values: &JsonValue) -> VOPRFTestVectorParameters { + VOPRFTestVectorParameters { + seed: decode(values, "Seed"), + sksm: decode(values, "skSm"), + pksm: decode(values, "pkSm"), + input: decode_vec(values, "Input"), + info: decode(values, "Info"), + key_info: decode(values, "KeyInfo"), + blind: decode_vec(values, "Blind"), + blinded_element: decode_vec(values, "BlindedElement"), + evaluation_element: decode_vec(values, "EvaluationElement"), + proof: decode(values, "Proof"), + proof_random_scalar: decode(values, "ProofRandomScalar"), + output: decode_vec(values, "Output"), + } +} + +fn decode(values: &JsonValue, key: &str) -> Vec { + values[key] + .as_str() + .and_then(|s| hex::decode(&s.to_string()).ok()) + .unwrap_or_default() +} + +fn decode_vec(values: &JsonValue, key: &str) -> Vec> { + let s = values[key].as_str().unwrap(); + let res = match s.contains(',') { + true => Some( + s.split(',') + .map(|x| hex::decode(&x.to_string()).unwrap()) + .collect(), + ), + false => Some(vec![hex::decode(&s.to_string()).unwrap()]), + }; + res.unwrap() +} + +macro_rules! json_to_test_vectors { + ( $v:ident, $cs:expr, $mode:expr ) => { + $v[$cs][$mode] + .members() + .map(|x| populate_test_vectors(&x)) + .collect::>() + }; +} + +#[test] +fn test_vectors() -> Result<()> { + use p256::NistP256; + + let rfc = json::parse(rfc_to_json(super::cfrg_vectors::VECTORS).as_str()) + .expect("Could not parse json"); + + #[cfg(feature = "ristretto255")] + { + use crate::Ristretto255; + + let ristretto_oprf_tvs = json_to_test_vectors!( + rfc, + String::from("ristretto255, SHA-512"), + String::from("OPRF") + ); + assert_ne!(ristretto_oprf_tvs.len(), 0); + test_oprf_seed_to_key::(&ristretto_oprf_tvs)?; + test_oprf_blind::(&ristretto_oprf_tvs)?; + test_oprf_evaluate::(&ristretto_oprf_tvs)?; + test_oprf_finalize::(&ristretto_oprf_tvs)?; + + let ristretto_voprf_tvs = json_to_test_vectors!( + rfc, + String::from("ristretto255, SHA-512"), + String::from("VOPRF") + ); + assert_ne!(ristretto_voprf_tvs.len(), 0); + test_voprf_seed_to_key::(&ristretto_voprf_tvs)?; + test_voprf_blind::(&ristretto_voprf_tvs)?; + test_voprf_evaluate::(&ristretto_voprf_tvs)?; + test_voprf_finalize::(&ristretto_voprf_tvs)?; + + let ristretto_poprf_tvs = json_to_test_vectors!( + rfc, + String::from("ristretto255, SHA-512"), + String::from("POPRF") + ); + assert_ne!(ristretto_poprf_tvs.len(), 0); + test_poprf_seed_to_key::(&ristretto_poprf_tvs)?; + test_poprf_blind::(&ristretto_poprf_tvs)?; + test_poprf_evaluate::(&ristretto_poprf_tvs)?; + test_poprf_finalize::(&ristretto_poprf_tvs)?; + } + + let p256base_tvs = + json_to_test_vectors!(rfc, String::from("P-256, SHA-256"), String::from("OPRF")); + assert_ne!(p256base_tvs.len(), 0); + + let p256verifiable_tvs = + json_to_test_vectors!(rfc, String::from("P-256, SHA-256"), String::from("VOPRF")); + assert_ne!(p256verifiable_tvs.len(), 0); + + test_oprf_seed_to_key::(&p256base_tvs)?; + test_oprf_blind::(&p256base_tvs)?; + test_oprf_evaluate::(&p256base_tvs)?; + test_oprf_finalize::(&p256base_tvs)?; + + test_voprf_seed_to_key::(&p256verifiable_tvs)?; + test_voprf_blind::(&p256verifiable_tvs)?; + test_voprf_evaluate::(&p256verifiable_tvs)?; + test_voprf_finalize::(&p256verifiable_tvs)?; + + Ok(()) +} + +fn test_oprf_seed_to_key(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + for parameters in tvs { + let server = OprfServer::::new_from_seed(¶meters.seed, ¶meters.key_info)?; + + assert_eq!( + ¶meters.sksm, + &CS::Group::serialize_scalar(server.get_private_key()).to_vec() + ); + } + Ok(()) +} + +fn test_voprf_seed_to_key(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + for parameters in tvs { + let server = VoprfServer::::new_from_seed(¶meters.seed, ¶meters.key_info)?; + + assert_eq!( + ¶meters.sksm, + &CS::Group::serialize_scalar(server.get_private_key()).to_vec() + ); + assert_eq!( + ¶meters.pksm, + CS::Group::serialize_elem(server.get_public_key()).as_slice() + ); + } + Ok(()) +} + +fn test_poprf_seed_to_key(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + for parameters in tvs { + let server = PoprfServer::::new_from_seed(¶meters.seed, ¶meters.key_info)?; + + assert_eq!( + ¶meters.sksm, + &CS::Group::serialize_scalar(server.get_private_key()).to_vec() + ); + assert_eq!( + ¶meters.pksm, + CS::Group::serialize_elem(server.get_public_key()).as_slice() + ); + } + Ok(()) +} + +// Tests input -> blind, blinded_element +fn test_oprf_blind(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + for parameters in tvs { + for i in 0..parameters.input.len() { + let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?; + let client_result = + OprfClient::::deterministic_blind_unchecked(¶meters.input[i], blind)?; + + assert_eq!( + ¶meters.blind[i], + &CS::Group::serialize_scalar(client_result.state.blind).to_vec() + ); + assert_eq!( + parameters.blinded_element[i].as_slice(), + client_result.message.serialize().as_slice(), + ); + } + } + Ok(()) +} + +// Tests input -> blind, blinded_element +fn test_voprf_blind(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + for parameters in tvs { + for i in 0..parameters.input.len() { + let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?; + let client_blind_result = + VoprfClient::::deterministic_blind_unchecked(¶meters.input[i], blind)?; + + assert_eq!( + ¶meters.blind[i], + &CS::Group::serialize_scalar(client_blind_result.state.get_blind()).to_vec() + ); + assert_eq!( + parameters.blinded_element[i].as_slice(), + client_blind_result.message.serialize().as_slice(), + ); + } + } + Ok(()) +} + +// Tests input -> blind, blinded_element +fn test_poprf_blind(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + for parameters in tvs { + for i in 0..parameters.input.len() { + let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?; + let client_blind_result = + PoprfClient::::deterministic_blind_unchecked(¶meters.input[i], blind)?; + + assert_eq!( + ¶meters.blind[i], + &CS::Group::serialize_scalar(client_blind_result.state.get_blind()).to_vec() + ); + assert_eq!( + parameters.blinded_element[i].as_slice(), + client_blind_result.message.serialize().as_slice(), + ); + } + } + Ok(()) +} + +// Tests sksm, blinded_element -> evaluation_element +fn test_oprf_evaluate(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + for parameters in tvs { + for i in 0..parameters.input.len() { + let server = OprfServer::::new_with_key(¶meters.sksm)?; + let message = server.evaluate(&BlindedElement::deserialize( + ¶meters.blinded_element[i], + )?)?; + + assert_eq!( + ¶meters.evaluation_element[i], + &message.serialize().as_slice() + ); + } + } + Ok(()) +} + +fn test_voprf_evaluate(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + ::ScalarLen: Add<::ScalarLen>, + Sum<::ScalarLen, ::ScalarLen>: ArrayLength, +{ + for parameters in tvs { + let mut rng = CycleRng::new(parameters.proof_random_scalar.clone()); + let server = VoprfServer::::new_with_key(¶meters.sksm)?; + + let mut blinded_elements = vec![]; + for blinded_element_bytes in ¶meters.blinded_element { + blinded_elements.push(BlindedElement::deserialize(blinded_element_bytes)?); + } + + let VoprfServerBatchEvaluateResult { messages, proof } = + server.batch_evaluate(&mut rng, blinded_elements)?; + + for (parameter, message) in parameters.evaluation_element.iter().zip(messages) { + assert_eq!(¶meter, &message.serialize().as_slice()); + } + + assert_eq!(¶meters.proof, &proof.serialize().as_slice()); + } + Ok(()) +} + +fn test_poprf_evaluate(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + ::ScalarLen: Add<::ScalarLen>, + Sum<::ScalarLen, ::ScalarLen>: ArrayLength, +{ + for parameters in tvs { + let mut rng = CycleRng::new(parameters.proof_random_scalar.clone()); + let server = PoprfServer::::new_with_key(¶meters.sksm)?; + + let mut blinded_elements = vec![]; + for blinded_element_bytes in ¶meters.blinded_element { + blinded_elements.push(BlindedElement::deserialize(blinded_element_bytes)?); + } + + let PoprfServerBatchEvaluateResult { messages, proof } = + server.batch_evaluate(&mut rng, blinded_elements, Some(¶meters.info))?; + + for (parameter, message) in parameters.evaluation_element.iter().zip(messages) { + assert_eq!(¶meter, &message.serialize().as_slice()); + } + + assert_eq!(¶meters.proof, &proof.serialize().as_slice()); + } + Ok(()) +} + +// Tests input, blind, evaluation_element -> output +fn test_oprf_finalize(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + for parameters in tvs { + for i in 0..parameters.input.len() { + let client = + OprfClient::::from_blind(CS::Group::deserialize_scalar(¶meters.blind[i])?); + + let client_finalize_result = client.finalize( + ¶meters.input[i], + &EvaluationElement::deserialize(¶meters.evaluation_element[i])?, + )?; + + assert_eq!(¶meters.output[i], &client_finalize_result.to_vec()); + } + } + Ok(()) +} + +fn test_voprf_finalize(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + for parameters in tvs { + let mut clients = vec![]; + for i in 0..parameters.input.len() { + let client = VoprfClient::::from_blind_and_element( + CS::Group::deserialize_scalar(¶meters.blind[i])?, + CS::Group::deserialize_elem(¶meters.blinded_element[i])?, + ); + clients.push(client.clone()); + } + + let messages: Vec<_> = parameters + .evaluation_element + .iter() + .map(|x| EvaluationElement::deserialize(x).unwrap()) + .collect(); + + let batch_result = VoprfClient::batch_finalize( + ¶meters.input, + &clients, + &messages, + &Proof::deserialize(¶meters.proof)?, + CS::Group::deserialize_elem(¶meters.pksm)?, + )?; + + assert_eq!( + parameters.output, + batch_result + .map(|arr| arr.map(|message| message.to_vec())) + .collect::>>()? + ); + } + Ok(()) +} + +fn test_poprf_finalize(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + for parameters in tvs { + let mut clients = vec![]; + for i in 0..parameters.input.len() { + let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?; + let client_blind_result = + PoprfClient::::deterministic_blind_unchecked(¶meters.input[i], blind)?; + let client = client_blind_result.state; + clients.push(client.clone()); + } + + let messages: Vec<_> = parameters + .evaluation_element + .iter() + .map(|x| EvaluationElement::deserialize(x).unwrap()) + .collect(); + + let batch_result = PoprfClient::batch_finalize( + parameters + .input + .iter() + .map(|input| input.as_slice()) + .collect::>() + .as_slice(), + &clients, + &messages, + &Proof::deserialize(¶meters.proof)?, + CS::Group::deserialize_elem(¶meters.pksm)?, + Some(¶meters.info), + )?; + + let result: Vec> = batch_result.iter().map(|arr| arr.to_vec()).collect(); + + assert_eq!(parameters.output, result); + } + Ok(()) +} diff --git a/src/tests/voprf_test_vectors.rs b/src/tests/voprf_test_vectors.rs deleted file mode 100644 index b3fb3ea..0000000 --- a/src/tests/voprf_test_vectors.rs +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. -// -// This source code is licensed under both the MIT license found in the -// LICENSE-MIT file in the root directory of this source tree and the Apache -// License, Version 2.0 found in the LICENSE-APACHE file in the root directory -// of this source tree. - -use alloc::string::{String, ToString}; -use alloc::vec; -use alloc::vec::Vec; -use core::ops::Add; - -use digest::core_api::BlockSizeUser; -use digest::OutputSizeUser; -use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256}; -use generic_array::ArrayLength; -use json::JsonValue; - -use crate::tests::mock_rng::CycleRng; -use crate::tests::parser::*; -use crate::{ - BlindedElement, CipherSuite, EvaluationElement, Group, NonVerifiableClient, - NonVerifiableServer, Proof, Result, VerifiableClient, VerifiableServer, -}; - -#[derive(Debug)] -struct VOPRFTestVectorParameters { - seed: Vec, - sksm: Vec, - pksm: Vec, - input: Vec>, - info: Vec, - blind: Vec>, - blinded_element: Vec>, - evaluation_element: Vec>, - proof: Vec, - proof_random_scalar: Vec, - output: Vec>, -} - -fn populate_test_vectors(values: &JsonValue) -> VOPRFTestVectorParameters { - VOPRFTestVectorParameters { - seed: decode(values, "seed"), - sksm: decode(values, "skSm"), - pksm: decode(values, "pkSm"), - input: decode_vec(values, "Input"), - info: decode(values, "Info"), - blind: decode_vec(values, "Blind"), - blinded_element: decode_vec(values, "BlindedElement"), - evaluation_element: decode_vec(values, "EvaluationElement"), - proof: decode(values, "Proof"), - proof_random_scalar: decode(values, "ProofRandomScalar"), - output: decode_vec(values, "Output"), - } -} - -fn decode(values: &JsonValue, key: &str) -> Vec { - values[key] - .as_str() - .and_then(|s| hex::decode(&s.to_string()).ok()) - .unwrap_or_default() -} - -fn decode_vec(values: &JsonValue, key: &str) -> Vec> { - let s = values[key].as_str().unwrap(); - let res = match s.contains(',') { - true => Some( - s.split(',') - .map(|x| hex::decode(&x.to_string()).unwrap()) - .collect(), - ), - false => Some(vec![hex::decode(&s.to_string()).unwrap()]), - }; - res.unwrap() -} - -macro_rules! json_to_test_vectors { - ( $v:ident, $cs:expr, $mode:expr ) => { - $v[$cs][$mode] - .members() - .map(|x| populate_test_vectors(&x)) - .collect::>() - }; -} - -#[test] -fn test_vectors() -> Result<()> { - use p256::NistP256; - - let rfc = json::parse(rfc_to_json(super::voprf_vectors::VECTORS).as_str()) - .expect("Could not parse json"); - - #[cfg(feature = "ristretto255")] - { - use crate::Ristretto255; - - let ristretto_base_tvs = json_to_test_vectors!( - rfc, - String::from("ristretto255, SHA-512"), - String::from("Base") - ); - - let ristretto_verifiable_tvs = json_to_test_vectors!( - rfc, - String::from("ristretto255, SHA-512"), - String::from("Verifiable") - ); - - test_base_seed_to_key::(&ristretto_base_tvs)?; - test_base_blind::(&ristretto_base_tvs)?; - test_base_evaluate::(&ristretto_base_tvs)?; - test_base_finalize::(&ristretto_base_tvs)?; - - test_verifiable_seed_to_key::(&ristretto_verifiable_tvs)?; - test_verifiable_blind::(&ristretto_verifiable_tvs)?; - test_verifiable_evaluate::(&ristretto_verifiable_tvs)?; - test_verifiable_finalize::(&ristretto_verifiable_tvs)?; - } - - let p256base_tvs = - json_to_test_vectors!(rfc, String::from("P-256, SHA-256"), String::from("Base")); - - let p256verifiable_tvs = json_to_test_vectors!( - rfc, - String::from("P-256, SHA-256"), - String::from("Verifiable") - ); - - test_base_seed_to_key::(&p256base_tvs)?; - test_base_blind::(&p256base_tvs)?; - test_base_evaluate::(&p256base_tvs)?; - test_base_finalize::(&p256base_tvs)?; - - test_verifiable_seed_to_key::(&p256verifiable_tvs)?; - test_verifiable_blind::(&p256verifiable_tvs)?; - test_verifiable_evaluate::(&p256verifiable_tvs)?; - test_verifiable_finalize::(&p256verifiable_tvs)?; - - Ok(()) -} - -fn test_base_seed_to_key(tvs: &[VOPRFTestVectorParameters]) -> Result<()> -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - for parameters in tvs { - let server = NonVerifiableServer::::new_from_seed(¶meters.seed)?; - - assert_eq!( - ¶meters.sksm, - &CS::Group::serialize_scalar(server.get_private_key()).to_vec() - ); - } - Ok(()) -} - -fn test_verifiable_seed_to_key(tvs: &[VOPRFTestVectorParameters]) -> Result<()> -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - for parameters in tvs { - let server = VerifiableServer::::new_from_seed(¶meters.seed)?; - - assert_eq!( - ¶meters.sksm, - &CS::Group::serialize_scalar(server.get_private_key()).to_vec() - ); - assert_eq!( - ¶meters.pksm, - CS::Group::serialize_elem(server.get_public_key()).as_slice() - ); - } - Ok(()) -} - -// Tests input -> blind, blinded_element -fn test_base_blind(tvs: &[VOPRFTestVectorParameters]) -> Result<()> -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - for parameters in tvs { - for i in 0..parameters.input.len() { - let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?; - let client_result = NonVerifiableClient::::deterministic_blind_unchecked( - ¶meters.input[i], - blind, - )?; - - assert_eq!( - ¶meters.blind[i], - &CS::Group::serialize_scalar(client_result.state.blind).to_vec() - ); - assert_eq!( - parameters.blinded_element[i].as_slice(), - client_result.message.serialize().as_slice(), - ); - } - } - Ok(()) -} - -// Tests input -> blind, blinded_element -fn test_verifiable_blind(tvs: &[VOPRFTestVectorParameters]) -> Result<()> -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - for parameters in tvs { - for i in 0..parameters.input.len() { - let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?; - let client_blind_result = - VerifiableClient::::deterministic_blind_unchecked(¶meters.input[i], blind)?; - - assert_eq!( - ¶meters.blind[i], - &CS::Group::serialize_scalar(client_blind_result.state.get_blind()).to_vec() - ); - assert_eq!( - parameters.blinded_element[i].as_slice(), - client_blind_result.message.serialize().as_slice(), - ); - } - } - Ok(()) -} - -// Tests sksm, blinded_element -> evaluation_element -fn test_base_evaluate(tvs: &[VOPRFTestVectorParameters]) -> Result<()> -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - for parameters in tvs { - for i in 0..parameters.input.len() { - let server = NonVerifiableServer::::new_with_key(¶meters.sksm)?; - let message = server.evaluate( - &BlindedElement::deserialize(¶meters.blinded_element[i])?, - Some(¶meters.info), - )?; - - assert_eq!( - ¶meters.evaluation_element[i], - &message.serialize().as_slice() - ); - } - } - Ok(()) -} - -fn test_verifiable_evaluate(tvs: &[VOPRFTestVectorParameters]) -> Result<()> -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, - ::ScalarLen: Add<::ScalarLen>, - Sum<::ScalarLen, ::ScalarLen>: ArrayLength, -{ - use crate::{ - VerifiableServerBatchEvaluateFinishResult, VerifiableServerBatchEvaluatePrepareResult, - }; - - for parameters in tvs { - let mut rng = CycleRng::new(parameters.proof_random_scalar.clone()); - let server = VerifiableServer::::new_with_key(¶meters.sksm)?; - - let mut blinded_elements = vec![]; - for blinded_element_bytes in ¶meters.blinded_element { - blinded_elements.push(BlindedElement::deserialize(blinded_element_bytes)?); - } - - let VerifiableServerBatchEvaluatePrepareResult { - prepared_evaluation_elements, - t, - } = server.batch_evaluate_prepare(blinded_elements.iter(), Some(¶meters.info))?; - let prepared_elements: Vec<_> = prepared_evaluation_elements.collect(); - let VerifiableServerBatchEvaluateFinishResult { messages, proof } = - VerifiableServer::batch_evaluate_finish( - &mut rng, - blinded_elements.iter(), - &prepared_elements, - &t, - )?; - let messages: Vec<_> = messages.collect(); - - for (parameter, message) in parameters.evaluation_element.iter().zip(messages) { - assert_eq!(¶meter, &message.serialize().as_slice(),); - } - - assert_eq!(¶meters.proof, &proof.serialize().as_slice()); - } - Ok(()) -} - -// Tests input, blind, evaluation_element -> output -fn test_base_finalize(tvs: &[VOPRFTestVectorParameters]) -> Result<()> -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - for parameters in tvs { - for i in 0..parameters.input.len() { - let client = NonVerifiableClient::::from_blind(CS::Group::deserialize_scalar( - ¶meters.blind[i], - )?); - - let client_finalize_result = client.finalize( - ¶meters.input[i], - &EvaluationElement::deserialize(¶meters.evaluation_element[i])?, - Some(¶meters.info), - )?; - - assert_eq!(¶meters.output[i], &client_finalize_result.to_vec()); - } - } - Ok(()) -} - -fn test_verifiable_finalize(tvs: &[VOPRFTestVectorParameters]) -> Result<()> -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - for parameters in tvs { - let mut clients = vec![]; - for i in 0..parameters.input.len() { - let client = VerifiableClient::::from_blind_and_element( - CS::Group::deserialize_scalar(¶meters.blind[i])?, - CS::Group::deserialize_elem(¶meters.blinded_element[i])?, - ); - clients.push(client.clone()); - } - - let messages: Vec<_> = parameters - .evaluation_element - .iter() - .map(|x| EvaluationElement::deserialize(x).unwrap()) - .collect(); - - let batch_result = VerifiableClient::batch_finalize( - ¶meters.input, - &clients, - &messages, - &Proof::deserialize(¶meters.proof)?, - CS::Group::deserialize_elem(¶meters.pksm)?, - Some(¶meters.info), - )?; - - assert_eq!( - parameters.output, - batch_result - .map(|arr| arr.map(|message| message.to_vec())) - .collect::>>()? - ); - } - Ok(()) -} diff --git a/src/tests/voprf_vectors.rs b/src/tests/voprf_vectors.rs deleted file mode 100644 index 9203072..0000000 --- a/src/tests/voprf_vectors.rs +++ /dev/null @@ -1,637 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. -// -// This source code is licensed under both the MIT license found in the -// LICENSE-MIT file in the root directory of this source tree and the Apache -// License, Version 2.0 found in the LICENSE-APACHE file in the root directory -// of this source tree. - -//! The VOPRF test vectors taken from: -//! https://github.com/cfrg/draft-irtf-cfrg-voprf/blob/master/draft-irtf-cfrg-voprf.md - -pub(crate) const VECTORS: &str = r#" -## OPRF(ristretto255, SHA-512) - -### Base Mode - -~~~ -seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a -3a3 -skSm = 74db8e13d2c5148a1181d57cc06debd730da4df1978b72ac18bc48992a0d2 -c0f -~~~ - -#### Test Vector 1, Batch Size 1 - -~~~ -Input = 00 -Info = 7465737420696e666f -Blind = c604c785ada70d77a5256ae21767de8c3304115237d262134f5e46e512cf -8e03 -BlindedElement = 744441a5d3ee12571a84d34812443eba2b6521a47265ad655f0 -1e759b3dd7d35 -EvaluationElement = 4254c503ee2013262473eec926b109b018d699b8dd954ee8 -78bc17b159696353 -Output = 9aef8983b729baacb7ecf1be98d1276ca29e7d62dbf39bc595be018b66b -199119f18579a9ae96a39d7d506c9e00f75b433a870d76ba755a3e7196911fff89ff -3 -~~~ - -#### Test Vector 2, Batch Size 1 - -~~~ -Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a -Info = 7465737420696e666f -Blind = 5ed895206bfc53316d307b23e46ecc6623afb3086da74189a416012be037 -e50b -BlindedElement = f4eeea4e1bcb2ec818ee2d5c1fcec56c24064a9ff4bea5b3dd6 -877800fc28e4d -EvaluationElement = 185dae43b6209dacbc41a62fd4889700d11eeeff4e83ffbc -72d54daee7e25659 -Output = f556e2d83e576b4edc890472572d08f0d90d2ecc52a73b35b2a8416a72f -f676549e3a83054fdf4fd16fe03e03bee7bb32cbd83c7ca212ea0d03b8996c2c268b -2 -~~~ - -### Verifiable Mode - -~~~ -seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a -3a3 -skSm = ad08ad9c7107691d792d346d743e8a79b8f6ae0673d58cbf7389d7003598c -903 -pkSm = 7a5627aec2f2209a2fc62f39f57a8f5ffc4bbfd679d0273e6081b2b621ee3 -b52 -~~~ - -#### Test Vector 1, Batch Size 1 - -~~~ -Input = 00 -Info = 7465737420696e666f -Blind = ed8366feb6b1d05d1f46acb727061e43aadfafe9c10e5a64e7518d63e326 -3503 -BlindedElement = 56c6926e940df23d5dfe6a48949c5a9e5b503df3bff36454ba4 -821afa1528718 -EvaluationElement = 523774950001072a4fb1f1f3300f7feb1eeddb5b8304baa9 -c3d463c11e7f0509 -Proof = c973c8cfbcdbb12a09e7640e44e45d85d420ed0539a18dc6c67c189b4f28 -c70dd32f9b13717ee073e1e73333a7cb17545dd42ed8a2008c5dae11a3bd7e70260d -ProofRandomScalar = 019cbd1d7420292528f8cdd62f339fdabb602f04a95dac9d -bcec831b8c681a09 -Output = 2d9ed987fdfa623a5b4d5e445b127e86212b7c8f2567c175b424c59602f -bba7c36975df5e4ecdf060430c8b1b581fc97e953535fd82089e15afbafcf310b339 -9 -~~~ - -#### Test Vector 2, Batch Size 1 - -~~~ -Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a -Info = 7465737420696e666f -Blind = e6d0f1d89ad552e383d6c6f4e8598cc3037d6e274d22da3089e7afbd4171 -ea02 -BlindedElement = 5cd133d03df2e1ff919ed85501319c2039853dd7dc59da73605 -fd5791b835d23 -EvaluationElement = c0ba1012cbfb0338dadb435ef1d910eb179dc18c0d0a341f -0249a3a9ff03b06e -Proof = 156761aee4eb6a5e1e32bc0adb56ea46d65883777e152d4c607a3a3b8abf -3b036ecebae005d3f26222a8da0a3924cceed8a1a7c707ef4ba077456c3e80f8c40f -ProofRandomScalar = 74ae06fd50d5f26c2519bd7b184f45dd3ef2cb50197d42df -9d013f7d6c312a0b -Output = f5da1276b5ca3de4591534cf2d96f7bb49059bd374f40259f42dca89d72 -3cac69ed3ae567128aaa2dfdf777f333615524aec24bc77b0a38e200e6a07b6c638e -b -~~~ - -#### Test Vector 3, Batch Size 2 - -~~~ -Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a -Info = 7465737420696e666f -Blind = 80513e77795feeec6d2c450589b0e1b178febd5c193a9fcba0d27f0a06e0 -d50f,533c2e6d91c934f919ac218973be55ba0d7b234160a0d4cf3bddafbda99e2e0 -c -BlindedElement = 1c7ee9c1b4145dabeba9ad159531432a20718cb44a86f79dc73 -f6f8671c9bf5e,7c1ef37881602cb6d3cf995e6ee310ed51e39b80ce0a825a316bc6 -21d0580a14 -EvaluationElement = a8a66348d351408cb7e2d26341a1258ba91c1a7d1b380f62 -15bdfc242500991b,5a4b72bee9d2ca80ea220571690e2f92fadd0c13635b2888bc1 -ff255f8fee975 -Proof = caad28bac17ce71d59b43956e8d80f3edde3d0c317144bef3d10d9733ef1 -cf09fd910c663ea85ad7cfaf641d73314694fe18d3f6b89cfe001b18163ff908d10a -ProofRandomScalar = 3af5aec325791592eee4a8860522f8444c8e71ac33af5186 -a9706137886dce08 -Output = 2d9ed987fdfa623a5b4d5e445b127e86212b7c8f2567c175b424c59602f -bba7c36975df5e4ecdf060430c8b1b581fc97e953535fd82089e15afbafcf310b339 -9,f5da1276b5ca3de4591534cf2d96f7bb49059bd374f40259f42dca89d723cac69e -d3ae567128aaa2dfdf777f333615524aec24bc77b0a38e200e6a07b6c638eb -~~~ - -## OPRF(decaf448, SHAKE-256) - -### Base Mode - -~~~ -seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a -3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3 -skSm = 82c2a6492e1792e6ccdf1d7cff410c717681bd53ad47da7646b14ebd05885 -53e4c034e02b3ae5e724600a17a638ad528c04f793df56c2618 -~~~ - -#### Test Vector 1, Batch Size 1 - -~~~ -Input = 00 -Info = 7465737420696e666f -Blind = d1080372f0fcf8c5eace50914e7127f576725f215cc7c111673c635ce668 -bbbb9b50601ad89b358ab8c23ed0b6c9d040365ec9d060868714 -BlindedElement = 1c354d6d31500c7c5ae6fb10901ac87552ea3af1824e79871e2 -596ef537f86abac64859cf6f35911ab74f0b09a06ecc757a65a104e9e49fb -EvaluationElement = 9e5bbf27b2312a493b2f2f1d051b7cdf3801769ec5dc0724 -51b68c4d0d4ed9303979ec4798261a01fabd8d25540f48a11dd8342fded95383 -Output = 5f8c28d5e760786cbd000ac58444bd216141472b9370b058408a714da5e -3dd51fc572f96c99a9338bc8569abc991bc1523fa1467cd3a0de3aef7f154bd65d92 -e -~~~ - -#### Test Vector 2, Batch Size 1 - -~~~ -Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a -Info = 7465737420696e666f -Blind = aed1ffa44fd8f0ed16373606a3cf7df589cca86d8ea1abbf5768771dbef3 -d401c74ae55ba1e28b9565e1e4018eb261a14134a4ce60c1c718 -BlindedElement = e8111f22d50595f68f01a6a9135f50e8702c90794c2637fbe00 -9046f0c455884cc77ee7a87f3abf494afe780b3620ab0e7fb65c65ba902b2 -EvaluationElement = 0ec625f99914ba702f0e6bc5d0f837cb4deaf7ab3ac55458 -7182c3dfe1dad6d1540964f9581d26e8ef0a47b61c5f145109a5fffe04ad528e -Output = 7f0e40c08d8220f88c0961925f764ee0e4e08909d497f462a97a2030b40 -b44986fa76d344efb9b0acab23db81356fc8c380b80701a61a5fa76097a5d2ea7aa9 -e -~~~ - -### Verifiable Mode - -~~~ -seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a -3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3 -skSm = 5d295b55d1d6e46411bbb4151d154dc61711012ff2390255b3345988f8e3c -458089d52e9b1d837049898f9e4e63a4534f0ed3b3a47c7051c -pkSm = 8e623ef9b65ef2ce148ce56249ee5e69ed6acd3e504a07905cc4c09312551 -8d30ae7d6de274438b822d5a55a4365216ac588a4c400fbf6ff -~~~ - -#### Test Vector 1, Batch Size 1 - -~~~ -Input = 00 -Info = 7465737420696e666f -Blind = 4c936db1779a621b6c71475ac3111fd5703a59b713929f36dfd1e892a7fe -814479c93d8b4b6e11d1f6fe5351e51457b665fa7b76074e531f -BlindedElement = 74bb2406b15a86ba94b0686901545f8ddc23e64918de47c76fa -0bf812387021392c73e01068ac9cc07c7647b3d0d4e648c27bb3880ddb8e5 -EvaluationElement = 90997b495c19f16561a3286a7bcba9a4ee6e12bab4d580d5 -004ae5064d90a389124e81066f3f1dbf9a729ab46ed674c3292f56d54a0d5641 -Proof = 668f6ef88b249d51b6c94bfe82f2bec35ab7386bc9f3d14209d0247a5b6e -bedec4c333947fff96d322f516f4674cc07638b8e854c52be7045d83d65aff518104 -60ec43417a6c6efbfb67ba7b0257b1237c64e6792195e338474d09df32b076c0b702 -ec8c639b34c29878b87aad70d63c -ProofRandomScalar = 1b3f5a55b2f18f8c53d4ecf2e1c27e1028f1c345bb504486 -4aa9dd8439d7520a7ba6183d50ef08bdf6c781aa465660c93e8195a8d231b62f -Output = 7db8c49354861f2d71c8175681c9cc930a00251330b2acc5c321f9833fe -d4113a1cb3e05a3840082c24e8d49470474dd1c7586f3663f32f66dc3888c63dc0e6 -e -~~~ - -#### Test Vector 2, Batch Size 1 - -~~~ -Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a -Info = 7465737420696e666f -Blind = 15b3355179392f40c3d5a15f0d5ffc354e340454ec779f575e4573a3886a -b5e57e4da2985cea9e32f6d95539ce2c7189e1bd7462e8c5483a -BlindedElement = ea3418614d71144ac4ecbd2c63c30ce34718b739ba0a5dd3585 -efd9800b9debdad4cffc25dcc39b4691aaffba19ead8a425d7d50f016f57e -EvaluationElement = 7e12ab491c3787a1f17118f7a0308f8c41f4cd6e850cf7fa -ba030b6c1bf1888337149e7c2fc88068626a0107be18e8b9e29f41c8d1510049 -Proof = 91ed184bf518a155749a99d39bed3f9dc9895054e55fab0ebd0ce4270e84 -52fcc8da055e8c2f75f2306ecacaa594de592e0d0b059b8eb30e15d5c3132b71ebc4 -933596c563ee8ce8681e0e40534e92ce487a0e33e341f02a9aaa1f750d9efa7545a0 -008b2f8dde5047ce68d00c2e962e -ProofRandomScalar = 2f2e9955be83a4b25743ebd3618d4fad8b7288477da50bed -9befa58af639ddd950fec34205f8a4f166fadcb8fa71a3ffdd2e98f4c8ef5e26 -Output = 66125718c5d651c88ab57dda67c52a506d436600f1521b7684c869b9a2b -3e67d1b41c47593e79fd6b70aaae8d3689536897ae8964ffcd433c0884c12c94929d -c -~~~ - -#### Test Vector 3, Batch Size 2 - -~~~ -Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a -Info = 7465737420696e666f -Blind = 614bb578f29cc677ea9e7aea3e4839413997e020f9377b63c13584156a09 -a46dd2a425c41eac0e313a47e99d05df72c6e1d58e6592577a0d,4c115060bca87db -7d73e00cbb8559f84cb7a221b235b0950a0ab553f03f10e1386abe954011b7da62bb -6599418ef90b5d4ea98cc28aff517 -BlindedElement = 909b0b8bcb900bd9e70f27258d7264015c50f3717361afff22d -16ad84758d2c6b7a1963263d0d035f63b88df8b473f9365c53abcec34b201,726315 -ee47e217344da7036a24f806177e221c9f6eae5763f9089b16bada69b85aec56c3ca -83b6f5f1091640ea3fe3e9429ff2aa7772efef -EvaluationElement = 46d8dec85a27698b4b69a67299eab1da0ec2bbed013a3a59 -b932e2938e2e2c5bcc8274febf49b7903419c18b895f17c4a9a504737d7a3fdc,fe4 -7eca9d06b400c80cc2b749284312c6f97c7b5d88055fe56b068c441e053fe909c6c2 -2bb7cd646a932e2d3838b7b3e2e883cfe0ed1a2a1 -Proof = 1f63637de4f945f5937ac015a508420f119f7b6a8e001439a1923a1705ce -ee704ad17664ff4c72f89566f83ceccee3001d44d849ac4dad2bc05b9bc718ba787f -c3c5b09198c4ab244455bac64a9a231b18c4682c0e6e30ae5398f5c041ee2c5b02c6 -19b7497c5bf070fdb4656353de1d -ProofRandomScalar = a614f1894bcf6a1c7cef33909b794fe6e69a642b20f4c911 -8febffaf6b6a31471fe7794aa77ced123f07e56cc27de60b0ab106c0b8eab127 -Output = 7db8c49354861f2d71c8175681c9cc930a00251330b2acc5c321f9833fe -d4113a1cb3e05a3840082c24e8d49470474dd1c7586f3663f32f66dc3888c63dc0e6 -e,66125718c5d651c88ab57dda67c52a506d436600f1521b7684c869b9a2b3e67d1b -41c47593e79fd6b70aaae8d3689536897ae8964ffcd433c0884c12c94929dc -~~~ - -## OPRF(P-256, SHA-256) - -### Base Mode - -~~~ -seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a -3a3 -skSm = c15d9e9ab36d495d9d62954db6aafe06d3edabf41600d58f9be0737af2719 -e97 -~~~ - -#### Test Vector 1, Batch Size 1 - -~~~ -Input = 00 -Info = 7465737420696e666f -Blind = 5d9e7f6efd3093c32ecceabd57fb03cf760c926d2a7bfa265babf29ec98a -f0d0 -BlindedElement = 03e9097c54d2ea05f99424bdf984ea30ecc3614029bd5f1139e -70c4e1ae3bdbd92 -EvaluationElement = 0202e4d1a338659c211900c39855f30025359928d261e6c9 -558d667b3fbbc811cd -Output = 15b96275d06b85741f491fe0cad5cb835baa6c39066cbea73132dcf95e8 -58e1c -~~~ - -#### Test Vector 2, Batch Size 1 - -~~~ -Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a -Info = 7465737420696e666f -Blind = 825155ab61f17605af2ae2e935c78d857c9407bcd45128d57d338f1671b5 -fcbe -BlindedElement = 03fa1ea45dd58d6b516c1252f2791610bf5ff1828c93be8af66 -786f45fb4d14db5 -EvaluationElement = 02657822553416d91bb3d707040fd0d5a0555f5cbae7519d -f3a297747a3ad1dd57 -Output = e97f3f451f3cfce45a530dec0a0dec934cd78c5b656771549072ee236ce -070b9 -~~~ - -### Verifiable Mode - -~~~ -seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a -3a3 -skSm = 7f62054fcd598b5e023c08ef0f04e05e26867438d5e355e846c9d8788d5c7 -a12 -pkSm = 03d6c3f69cfa683418a533fc52143a377f166e571ae50581abcb97ffd4e71 -24395 -~~~ - -#### Test Vector 1, Batch Size 1 - -~~~ -Input = 00 -Info = 7465737420696e666f -Blind = cee64d86fd20ab4caa264a26c0e3d42fb773b3173ba76f9588c9b14779bd -8d91 -BlindedElement = 029e103c4003ab9bf4a42e2003dd180922c8517927a68320058 -178fee56c6ac8a0 -EvaluationElement = 02856ac0748085d250d842b8b8fff6c1a9f688c961de52c4 -a1e6c004c48196a123 -Proof = 2a95bd827cf47873c886967ef6c17fe0e46efddd3b5f639927215cb7592a -4bf12a29117174a1af5899d64855352690e416b37f2a95580846a6bec445d82364fc -ProofRandomScalar = 70a5204b2b606f5a28328916e1e5ea5a17862d7a261fdd6d -959759758d5e34ac -Output = 14afc50acf64589445991da5b60add8b3f71205d53a983023d3cdaf8c95 -c300d -~~~ - -#### Test Vector 2, Batch Size 1 - -~~~ -Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a -Info = 7465737420696e666f -Blind = 5c4b401063eff0bf242b4cd534a79bacfc2e715b2db1e7a3ad4ff8af1b24 -daa2 -BlindedElement = 0323aabcfa93e9570524253671b3ce083144b183cecb562ec8f -8a8472fc8cf341b -EvaluationElement = 03087bc7e00b8ad80b8a27484b91f8bf824a5d896a703135 -4edfa3269866493d9f -Proof = fe55ecc9a92f940d4a56207a58e5554c6976b9425c917d24237b0a35c312 -bdcdea778a5c56690309ff28f26cc8bc5994e85868e3c870e5a32c0a559d80deccb8 -ProofRandomScalar = 3b9217801b5d51cef66d9fdbd94a53533e7c5057e09e2200 -65ea8c257c0dd606 -Output = 533c79459ee0ffa8844ac37572f3616e10a1074dcbf945ce37b0c651cbb -5775f -~~~ - -#### Test Vector 3, Batch Size 2 - -~~~ -Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a -Info = 7465737420696e666f -Blind = f0c7822ba317fb5e86028c44b92bd3aedcf6744d388ca013ef33edd36930 -4eda,3b9631be9f8b274d9aaf671bfb6a775229bf435021b89c683259773bc686956 -b -BlindedElement = 021af4563c31cf1513bc5ae0b89c5b527c7ac70614b9d31c44c -eb292ab49c91cc4,03f7e7ebe5610710c360df40cbd90dc52c2da500664e879f2afb -78e71f815abee1 -EvaluationElement = 03c8678cdb95e2f0eac027932c51893a20326b774ef23531 -bcd95def84060d240d,02b68c3891314a9696b5dff5df4b4e5b325938e2c5cb90f5f -b9ba6a1133aa4dd14 -Proof = 6efbde69d36e3f9d53a79a73ce46d5d8ef31f0df2fb3f6f2c882b21fdf0e -d76dcd755e42f35f00daaa6e964f48125cf1d642b1cea2e5faa2fb868584a8752bf2 -ProofRandomScalar = 8306b863276ae74049615162a416d507a6532c99c1ea3f03 -d05f6e78dc1edabe -Output = 14afc50acf64589445991da5b60add8b3f71205d53a983023d3cdaf8c95 -c300d,533c79459ee0ffa8844ac37572f3616e10a1074dcbf945ce37b0c651cbb577 -5f -~~~ - -## OPRF(P-384, SHA-384) - -### Base Mode - -~~~ -seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a -3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3 -skSm = b9ff42e68ef6f8eaa3b4d15d15ceb6f3f36b9dc332a3473d64840fc7b4462 -6c6e70336bdecbe01d9c512b7e7d7e6af21 -~~~ - -#### Test Vector 1, Batch Size 1 - -~~~ -Input = 00 -Info = 7465737420696e666f -Blind = 359073c015b92d15450f7fb395bf52c6ea98384c491fe4e4d423b59de7b0 -df382902c13bdc9993d3717bda68fc080b99 -BlindedElement = 0285d803c65fda56993a296b99e8f4944e45cccb9b322bbc265 -c91a21d2c9cd146212aefbf3126ed59d84c32d6ab823b66 -EvaluationElement = 026061a4ccfe38777e725855c96570fe85303cd70567007e -489d0aa8bfced0e47579ecbc290e5150b9e84bf25188294f7e -Output = bc2c3c895f96d769703aec18359cbc0e84b41248559f0bd44f1e5467522 -3c77e00874bbe61c1c320d3c95aee5a8c752f -~~~ - -#### Test Vector 2, Batch Size 1 - -~~~ -Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a -Info = 7465737420696e666f -Blind = 21ece4f9b6ffd01ce82082545413bd9bb5e8f3c63b86ae88d9ce0530b01c -b1c23382c7ec9bdd6e75898e4877d8e2bc17 -BlindedElement = 0211dd06e40b902006c33a92dc476a7c708b6b46c990656239c -d6867ff0be5867d859517eaf7ea9bad10702b80a9dc6bdc -EvaluationElement = 03a1d34b657f6267b29338592e3c769db5d3fc8713bf2eb7 -238efb8138d5af8c56f9437315a5c58761b35cbfc0e1d2511d -Output = ee37530d0d7b20635fbc476317343b257750ffb3e83a2865ce2a46e5959 -1f854b8301d6ca7d063322314a33b953c8bd5 -~~~ - -### Verifiable Mode - -~~~ -seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a -3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3 -skSm = 42c4d1c15d27be015844404088967afe48c8ae96d4f00ce48e4d38ecabfb8 -feb5b748de625cdf81ab076745d6211be95 -pkSm = 0389ad5e50eebf9617ae3a5778e4f0665b56aa9066919e1fa5580d8dd781d -560824e0e78aae816af6eff8abe2ad0585a0d -~~~ - -#### Test Vector 1, Batch Size 1 - -~~~ -Input = 00 -Info = 7465737420696e666f -Blind = 102f6338df84c9602bfa9e7d690b1f7a173d07e6d54a419db4a6308f8b09 -589e4283efb9cd1ee4061c6bf884e60a8774 -BlindedElement = 02ae8990d580dcd52b6bc273bc6d0fd25be50b057511b953d9c -c95bb27cb3e1fd3249ae19744ed496c6e4104ebc1ed48f1 -EvaluationElement = 024cffdae0cae5fa4d6a68246ae797dbe06508284b65e0f0 -9046977ab5d52a8b38f0245607db74979e5276fc636332cdee -Proof = 128ad4f987ce1e3a9aab1e487df15d8c8000d5c4c9f14bd7fd699fabdb8d -a3f577d91625fabb0d9cf6069f8af6d9cc232dd63cd161be84a1e146e0110dc741e6 -26a082193aa0a26e03118b662f1b903667f6e6fba51d69a2d65982a3b64ecb35 -ProofRandomScalar = 90f67cafc0ffaa7a1e1d1ced3c477fea691e696032c8709c -86cbcda2b184ad0029d29abeabede9788d11782429bff297 -Output = 8a0b4829bc8422b1a2301d5471256892883c5e3fe27b998d1010225a706 -545637336a20a76f842d8a22e591d382c77e4 -~~~ - -#### Test Vector 2, Batch Size 1 - -~~~ -Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a -Info = 7465737420696e666f -Blind = 8aec1d0c3d16afd032da7ba961449a56cec6fb918e932b06d5778ac7f67b -ecfb3e3869237f74106241777f230582e84a -BlindedElement = 02a384f2d9635adffcc5482344c519036c019f3cc0918ec737c -67cdda10ac0f73a9fe348835531f1900ea2c1f06dacdce4 -EvaluationElement = 0306f0f71b58d53ae0973538a7bf2ce8fba7143efc88d2ef -ca6cf1f98fb8399b16840d1fbbe7897807db930f67916418ae -Proof = 2c47297ee0093061ca2c87b430b2851a860aaae76c2bdba48779ba4294e7 -de0556ede3e6b881a04970b68a6126e2fa197d69e6784fbbd173604501c0edd21696 -628f0fd7cb13be28f94e5e15c042ffccadd780b2448d7d9d528e9615e4e70539 -ProofRandomScalar = bb1876a7f7165ac7ec79bfd5213ea2e374252f29a6e19915 -f81b0c7dcea93ce6580e089ede31c1b6b5b33494581b4868 -Output = 8c52d40c1f6cc80208bd610178a5034d6c4a05584e19b69617f846b09a8 -545443c63c8aa4d85bf0aad368e0591b1216a -~~~ - -#### Test Vector 3, Batch Size 2 - -~~~ -Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a -Info = 7465737420696e666f -Blind = 41fabd4722d92472d858051ce9ad1a533176a862c697b2c392aff2aeb77e -b20c2ae6ba52fe31e13e03bf1d9f39878b23,51171628f1d28bb7402ca4aea6465e2 -67b7f977a1fb71593281099ef2625644aee0b6c5f5e6e01a2b052b3bd4caf539b -BlindedElement = 02d4e6186c9ffa92565055f43f27bb1e2c4103c3325bba0b499 -adb99a157987d20fb374096814e438a6b483efa8f2a3307,033a3b052416a8a6d842 -a0baea6f5fab99d36645a70c89897a536970d34038eca35afac24906294cb7925b1b -05e4327c8f -EvaluationElement = 037bf8e28a0607b1f8aa59363380b5a7450b66b98017cf03 -3797f6c6c74e7625a445f71ace1bea7836ea5baa75d54eb5bd,03b793a9cb2d76991 -f1d6cd822abfbfa89fdfa1a06ef42b0bc8ade161e1996ed08c288a08366d4140c762 -7bba4e3472bcf -Proof = 27240901b6855d2b58ce84afefa91dd11819d7d5df73f94865a9d7e19020 -41200eb732b60b57fa0daf6e456402bb1ccb1aed901af35d3d790cd7c618604b766b -b9271010354da9e4e5507e0468adf177977143db2ddb94d9b70e837ad7578275 -ProofRandomScalar = 1b538ff23749be19e92df82df1acd3f606cc9faa9dc7ab25 -1997738a3a232f352c2059c25684e6ccea420f8d0c793fa0 -Output = 8a0b4829bc8422b1a2301d5471256892883c5e3fe27b998d1010225a706 -545637336a20a76f842d8a22e591d382c77e4,8c52d40c1f6cc80208bd610178a503 -4d6c4a05584e19b69617f846b09a8545443c63c8aa4d85bf0aad368e0591b1216a -~~~ - -## OPRF(P-521, SHA-512) - -### Base Mode - -~~~ -seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a -3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a -3a3 -skSm = 00a2f8572ee764d2ec34363fb62ef9e8ff48883b5357b6802f43fffe5c5fd -0d11f766bf7086aab33e2dce02cc71d77250ef6ed360a3fd56244abb6bdbc3aa6534 -da1 -~~~ - -#### Test Vector 1, Batch Size 1 - -~~~ -Input = 00 -Info = 7465737420696e666f -Blind = 01b983705fcc9a39607288b935b0797ac6b3c4b2e848823ac9ae16b3a3b5 -816be03432370deb7c3c17d9fc7cb4e0ce646e04e42d638e0fa7a434ed340772a8b5 -d626 -BlindedElement = 03006ce4a27e778a624d943cf4db48f9d393d3d4dd9cd44b78a -cf2d5b668a12f0ca587962de8c82b5aaa1f0166eb60d511f060aaab895fc6c519332 -77bc945add6d74a -EvaluationElement = 030055f7cd3ee3b1734e73ad8bbd4baca72ae8d051160c27 -7ee329f23fa2365f9f138b38e6e2c59cc287242eeca01fae83d0c7cc3bb19724ac59 -8a188816e7cfe1ca88 -Output = aa59060a41ec8ca7b6c47f9c5a31883a44ffd95869a09dbe845ea8ce20c -b290dba0b57c505824a0dcf6f961a2baeb8e6b49df8c158761a3fdb46f39e8e7fcb8 -b -~~~ - -#### Test Vector 2, Batch Size 1 - -~~~ -Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a -Info = 7465737420696e666f -Blind = 01a03b1096b0316bc8567c89bd70267d35c8ddcb2be2cdc867089a2eb5cf -471b1e6eb4b043b9644c8539857abe3a2022e9c9fd6a1695bbabe8add48bcd149ff3 -b841 -BlindedElement = 0201459ba64ad0e0f9f689f0ad5ab29ca5b960f5c9da3aef412 -6d2d547b871e754b17971fd45e0d64bdcfc8d256c342a141f04e2640705c38936c8c -f53c22ea6b13966 -EvaluationElement = 030094036457e8e5bf77719b11f01dd4aa2959efdb3329c3 -e3b25493efc3ab572c2e7db104cd5922645320ef51bbb282f84e5f6b08e9b49354f9 -d6a9f3a4327a1de6e4 -Output = 5efe6f00f45ec4e87e4c9b89aeaec61313c15c0a0a21ee2e41362d6af54 -536adf2f68d23c729b92b6fa8d5611764b0272be6cc153d47a0256c8cb44bd740037 -a -~~~ - -### Verifiable Mode - -~~~ -seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a -3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a -3a3 -skSm = 0064799c2f9c0f9e6b9ac2aca5c42687cf15742fb73e086c4954aa0bdc8b8 -25911ff03712e8d308c0a6ff5435375036f189391234bf21aac57fa73df155d70da4 -7bd -pkSm = 03013e587a7750213bb7c2b338a4507635f1ba60ece346de32ad975373e56 -fbabd878f9956996aac83a550ed5f5ba98fcc56817f6230cc7e84cb7eb2a1e1db51d -bfc1b -~~~ - -#### Test Vector 1, Batch Size 1 - -~~~ -Input = 00 -Info = 7465737420696e666f -Blind = 00bbb82117c88bbd91b8954e16c0b9ceed3ce992b198be1ebfba9ba970db -d75beefbfc6d056b7f7ba1ef79f4facbf2d912c26ce2ecc5bb8d66419b379952e96b -d6f5 -BlindedElement = 02002ff3ef3f2411aa0358936f852be710af790c9affbced8c3 -9b018fd97de0a45d80c66cbf0dbda690ee4f594e0795627e6c6f37a500f223c30f31 -c24e73501532e7c -EvaluationElement = 0300769fd56c5174c4e3922900fcefdd5a89c9592f4d8e8f -2396678fa72c01d4f8551ec92d4b5287ca673dc29d8db9bb05d2396121a6b8732b68 -ebf310fc2620059d67 -Proof = 011fd92f54f6a955a333648d843807bd88f644d235a7d592189da42d721e -a6f7b55ec813146f35982487910aa15bbf5ce90653edb6a1b48c0bfd15758e9358aa -731601baa67a3a59db301f41caa020986ae9e93a80d6c06d92e8c5eef6056fa6f342 -6b6054d118dc9fecb77fdcb4fc86b9857ada6de18394ff7d6c574cbd08d746b9dde0 -ProofRandomScalar = 00ce4f0d824939827888f4c28773466f3c0a05741260040b -c9f302a4fea13f1d8f2f6b92a02a32d5eb06f81de7960470f06169bee12cf47965b7 -2a59946ca3879670 -Output = a647c5a940aa19d767ab0e163d1357ca068206b2b78f9e8e1021c0bb0f3 -27d20cb8fadf996199d86d4cc0a08ac314493319979e1c2a98a96085b8fabff9f0d0 -7 -~~~ - -#### Test Vector 2, Batch Size 1 - -~~~ -Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a -Info = 7465737420696e666f -Blind = 009055c99bf9591cb0eab2a72d044c05ca2cc2ef9b609a38546f74b6d688 -f70cf205f782fa11a0d61b2f5a8a2a1143368327f3077c68a1545e9aafbba6a90dc0 -d40a -BlindedElement = 0301e2ecf7313820e9d47763e12633ce6acf9b3dec89928c83b -de1ede2180dc73553af1317408846af5c53ebfed00d19a4125f4ffb7df9f4260ccc0 -84a6f7482414a9d -EvaluationElement = 02000e69591ab605652cb3310e774edf79417e102cf89005 -c2c7f2bd3a06060d740817802f2cf484748d93df5b281a4bd835617a97ec9809519d -474ca53bba15cdf014 -Proof = 0076fa4275414acb9f87dc9e4f20971d51fcd0d38a980854ac2ad1bd5737 -eec23bfb4599d021881f7b3872d2e90d9b47e4219f490cf7f0235b2f0859cb2ef15d -dfd401acb6b0844edf066a5767b4b85536bfee69bdf472acf7a59254cf6578f9f35e -ba51bb58c6428d6b7c9e5c9af97edc66d98886fda9544048bf9ceea6fc745bf970da -ProofRandomScalar = 00b5dfc19eb96faba6382ec845097904db87240b9dd47b1e -487ec625f11a7ba2cc3de74c5078a81806f74dd65065273c5bd886c7f87ff8c5f39f -90320718eff747e3 -Output = 8d109503ccced41cbec087dab86c607763020be93bdd5ec8508cb078607 -1a2b22a7b06150242bcaf6ea1b555a994e0266647eb72914caf73cabe53ddfb0f940 -d -~~~ - -#### Test Vector 3, Batch Size 2 - -~~~ -Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a -Info = 7465737420696e666f -Blind = 01c6cf092d80c7cf2cb55388d899515238094c800bdd9c65f71780ba85f5 -ae9b4703e17e559ca3ccd1944f9a70536c175f11a827452672b60d4e9f89eba28104 -6e29,00cba1ba1a337759061965a423d9d3d6e1e1006dc8984ad28a4c93ecfc36fc2 -171046b3c4284855cfa2434ed98db9e68a597db2c14728fade716a6a82d600444b26 -e -BlindedElement = 0201e22c01df5ac0502842fad603f7a1e1183bcc79a5cb04bb7 -befdea870a9a6ea96fbccd752ea9927a9e1e28438098f693461e81832a3f690616bf -983fced079f3a33,0300b49216dd8ba5ba1275d8345679f70fbc6baf4f4b32a03e91 -7165a18afa9fad849c48eecb4bae965057ef7c215b52b42ca53c8d5f650633e0bb70 -97f2bd809d09ea -EvaluationElement = 03002949c2478249b918a0cf2cd870226541a81d2f3e88c4 -7119f732301e749c3dea317c11174a18b89d1b9d2aa4f6ae92ae724e03a4800a26b7 -c827b00199f1114bcd,0300924ab017ea6e6328a0b0f341bbeb7d209c67ac169fa4e -f7b04055c66b92aa9657f5d83b0b1ee9c79f3f0198519c97fef07dbecf3f6d477755 -0242a1c87953f9461 -Proof = 01f5d3c3f835d91aa88202f0fe8728180eeffe7fbc66ffe3f7a7dd958696 -a7cd3d47b3c0ec6cd59e9ee23090137293e6f42269923f3d4a1659bc706fd9762070 -7d230028cd4b0aa237b91a352fce81248936826ba99e7bd5103a871715126014b8d4 -7447e5f20192ed377a7431516fbd82763098ba23f9d15b84fe24fb1126beb0d46f03 -ProofRandomScalar = 00d47b0d4ca4c64825ba085de242042b84d9ebe3b2e9de07 -678ff96713dfe16f40f2c662a56ed2db95e1e7bf2dea02bd1fa76e953a630772f68b -53baade9962d1646 -Output = a647c5a940aa19d767ab0e163d1357ca068206b2b78f9e8e1021c0bb0f3 -27d20cb8fadf996199d86d4cc0a08ac314493319979e1c2a98a96085b8fabff9f0d0 -7,8d109503ccced41cbec087dab86c607763020be93bdd5ec8508cb0786071a2b22a -7b06150242bcaf6ea1b555a994e0266647eb72914caf73cabe53ddfb0f940d -~~~ -"#; diff --git a/src/util.rs b/src/util.rs index 25ccb2f..a50480f 100644 --- a/src/util.rs +++ b/src/util.rs @@ -9,10 +9,425 @@ use core::convert::TryFrom; -use generic_array::typenum::{IsLess, U2, U256}; +use derive_where::derive_where; +use digest::core_api::BlockSizeUser; +use digest::{Digest, OutputSizeUser}; +use generic_array::sequence::Concat; +use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U1, U11, U2, U256}; use generic_array::{ArrayLength, GenericArray}; +use rand_core::{CryptoRng, RngCore}; +use subtle::ConstantTimeEq; -use crate::InternalError; +#[cfg(feature = "serde")] +use crate::serialization::serde::{Element, Scalar}; +use crate::{CipherSuite, Error, Group, InternalError, Result}; + +/////////////// +// Constants // +// ========= // +/////////////// + +pub(crate) const STR_FINALIZE: [u8; 8] = *b"Finalize"; +pub(crate) const STR_SEED: [u8; 5] = *b"Seed-"; +pub(crate) const STR_DERIVE_KEYPAIR: [u8; 13] = *b"DeriveKeyPair"; +pub(crate) const STR_COMPOSITE: [u8; 9] = *b"Composite"; +pub(crate) const STR_CHALLENGE: [u8; 9] = *b"Challenge"; +pub(crate) const STR_INFO: [u8; 4] = *b"Info"; +pub(crate) const STR_VOPRF: [u8; 8] = *b"VOPRF09-"; + +/// Determines the mode of operation (either base mode or verifiable mode). This +/// is only used for custom implementations for [`Group`]. +#[derive(Clone, Copy, Debug)] +pub enum Mode { + /// Non-verifiable mode. + Oprf, + /// Verifiable mode. + Voprf, + /// Partially-oblivious mode. + Poprf, +} + +impl Mode { + /// Mode as it is represented in a context string. + pub fn to_u8(self) -> u8 { + match self { + Mode::Oprf => 0, + Mode::Voprf => 1, + Mode::Poprf => 2, + } + } +} + +//////////////////////////// +// High-level API Structs // +// ====================== // +//////////////////////////// + +/// The first client message sent from a client (either verifiable or not) to a +/// server (either verifiable or not). +#[derive_where(Clone, ZeroizeOnDrop)] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Elem)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(crate = "serde", bound = "") +)] +pub struct BlindedElement( + #[cfg_attr(feature = "serde", serde(with = "Element::"))] + pub(crate) ::Elem, +) +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>; + +/// The server's response to the [BlindedElement] message from a client (either +/// verifiable or not) to a server (either verifiable or not). +#[derive_where(Clone, ZeroizeOnDrop)] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Elem)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(crate = "serde", bound = "") +)] +pub struct EvaluationElement( + #[cfg_attr(feature = "serde", serde(with = "Element::"))] + pub(crate) ::Elem, +) +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>; + +/// A proof produced by a [PoprfServer] that the OPRF output matches +/// against a server public key. +#[derive_where(Clone, ZeroizeOnDrop)] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(crate = "serde", bound = "") +)] +pub struct Proof +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + #[cfg_attr(feature = "serde", serde(with = "Scalar::"))] + pub(crate) c_scalar: ::Scalar, + #[cfg_attr(feature = "serde", serde(with = "Scalar::"))] + pub(crate) s_scalar: ::Scalar, +} + +///////////////////// +// Proof Functions // +// =============== // +///////////////////// + +/// A wrapper around group elements used to generate and verify proofs +#[derive_where(Clone, ZeroizeOnDrop)] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Elem)] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(crate = "serde", bound = "") +)] +pub(crate) struct ProofElement( + #[cfg_attr(feature = "serde", serde(with = "Element::"))] + pub(crate) ::Elem, +) +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>; + +// Can only fail with [`Error::Batch`]. +#[allow(clippy::many_single_char_names)] +pub(crate) fn generate_proof( + rng: &mut R, + k: ::Scalar, + a: ::Elem, + b: ::Elem, + cs: impl Iterator> + ExactSizeIterator, + ds: impl Iterator> + ExactSizeIterator, + mode: Mode, +) -> Result> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.2.2-1 + + let (m, z) = compute_composites(Some(k), b, cs, ds, mode)?; + + let r = CS::Group::random_scalar(rng); + let t2 = a * &r; + let t3 = m * &r; + + // Bm = GG.SerializeElement(B) + let bm = CS::Group::serialize_elem(b); + // a0 = GG.SerializeElement(M) + let a0 = CS::Group::serialize_elem(m); + // a1 = GG.SerializeElement(Z) + let a1 = CS::Group::serialize_elem(z); + // a2 = GG.SerializeElement(t2) + let a2 = CS::Group::serialize_elem(t2); + // a3 = GG.SerializeElement(t3) + let a3 = CS::Group::serialize_elem(t3); + + let elem_len = ::ElemLen::U16.to_be_bytes(); + + // h2Input = I2OSP(len(Bm), 2) || Bm || + // I2OSP(len(a0), 2) || a0 || + // I2OSP(len(a1), 2) || a1 || + // I2OSP(len(a2), 2) || a2 || + // I2OSP(len(a3), 2) || a3 || + // "Challenge" + let h2_input = [ + &elem_len, + bm.as_slice(), + &elem_len, + &a0, + &elem_len, + &a1, + &elem_len, + &a2, + &elem_len, + &a3, + &GenericArray::from(STR_CHALLENGE), + ]; + + // This can't fail, the size of the `input` is known. + let c_scalar = CS::Group::hash_to_scalar::(&h2_input, mode).unwrap(); + let s_scalar = r - &(c_scalar * &k); + + Ok(Proof { c_scalar, s_scalar }) +} + +// Can only fail with [`Error::ProofVerification`] or [`Error::Batch`]. +#[allow(clippy::many_single_char_names)] +pub(crate) fn verify_proof( + a: ::Elem, + b: ::Elem, + cs: impl Iterator> + ExactSizeIterator, + ds: impl Iterator> + ExactSizeIterator, + proof: &Proof, + mode: Mode, +) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.4.1-2 + let (m, z) = compute_composites(None, b, cs, ds, mode)?; + let t2 = (a * &proof.s_scalar) + &(b * &proof.c_scalar); + let t3 = (m * &proof.s_scalar) + &(z * &proof.c_scalar); + + // Bm = GG.SerializeElement(B) + let bm = CS::Group::serialize_elem(b); + // a0 = GG.SerializeElement(M) + let a0 = CS::Group::serialize_elem(m); + // a1 = GG.SerializeElement(Z) + let a1 = CS::Group::serialize_elem(z); + // a2 = GG.SerializeElement(t2) + let a2 = CS::Group::serialize_elem(t2); + // a3 = GG.SerializeElement(t3) + let a3 = CS::Group::serialize_elem(t3); + + let elem_len = ::ElemLen::U16.to_be_bytes(); + + // h2Input = I2OSP(len(Bm), 2) || Bm || + // I2OSP(len(a0), 2) || a0 || + // I2OSP(len(a1), 2) || a1 || + // I2OSP(len(a2), 2) || a2 || + // I2OSP(len(a3), 2) || a3 || + // "Challenge" + let h2_input = [ + &elem_len, + bm.as_slice(), + &elem_len, + &a0, + &elem_len, + &a1, + &elem_len, + &a2, + &elem_len, + &a3, + &GenericArray::from(STR_CHALLENGE), + ]; + + // This can't fail, the size of the `input` is known. + let c = CS::Group::hash_to_scalar::(&h2_input, mode).unwrap(); + + match c.ct_eq(&proof.c_scalar).into() { + true => Ok(()), + false => Err(Error::ProofVerification), + } +} + +pub(crate) type ComputeCompositesResult = ( + <::Group as Group>::Elem, + <::Group as Group>::Elem, +); + +// Can only fail with [`Error::Batch`]. +fn compute_composites( + k_option: Option<::Scalar>, + b: ::Elem, + c_slice: impl Iterator> + ExactSizeIterator, + d_slice: impl Iterator> + ExactSizeIterator, + mode: Mode, +) -> Result> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.2.3-2 + + let elem_len = ::ElemLen::U16.to_be_bytes(); + + if c_slice.len() != d_slice.len() { + return Err(Error::Batch); + } + + let len = u16::try_from(c_slice.len()).map_err(|_| Error::Batch)?; + + // seedDST = "Seed-" || contextString + let seed_dst = GenericArray::from(STR_SEED).concat(create_context_string::(mode)); + + // h1Input = I2OSP(len(Bm), 2) || Bm || + // I2OSP(len(seedDST), 2) || seedDST + // seed = Hash(h1Input) + let seed = CS::Hash::new() + .chain_update(&elem_len) + .chain_update(CS::Group::serialize_elem(b)) + .chain_update(i2osp_2_array(&seed_dst)) + .chain_update(seed_dst) + .finalize(); + let seed_len = i2osp_2_array(&seed); + + let mut m = CS::Group::identity_elem(); + let mut z = CS::Group::identity_elem(); + + for (i, (c, d)) in (0..len).zip(c_slice.zip(d_slice)) { + // Ci = GG.SerializeElement(Cs[i]) + let ci = CS::Group::serialize_elem(c.0); + // Di = GG.SerializeElement(Ds[i]) + let di = CS::Group::serialize_elem(d.0); + // h2Input = I2OSP(len(seed), 2) || seed || I2OSP(i, 2) || + // I2OSP(len(Ci), 2) || Ci || + // I2OSP(len(Di), 2) || Di || + // "Composite" + let h2_input = [ + seed_len.as_slice(), + &seed, + &i.to_be_bytes(), + &elem_len, + &ci, + &elem_len, + &di, + &GenericArray::from(STR_COMPOSITE), + ]; + + // This can't fail, the size of the `input` is known. + let di = CS::Group::hash_to_scalar::(&h2_input, mode).unwrap(); + m = c.0 * &di + &m; + z = match k_option { + Some(_) => z, + None => d.0 * &di + &z, + }; + } + + z = match k_option { + Some(k) => m * &k, + None => z, + }; + + Ok((m, z)) +} + +///////////////////// +// Inner Functions // +// =============== // +///////////////////// + +#[allow(clippy::type_complexity)] +pub(crate) fn derive_keypair( + seed: &[u8], + info: &[u8], + mode: Mode, +) -> Result<(::Scalar, ::Elem), Error> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + let context_string = create_context_string::(mode); + let dst = GenericArray::from(STR_DERIVE_KEYPAIR).concat(context_string); + + // deriveInput = seed || I2OSP(len(info), 2) || info + let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?; + + let mut counter: usize = 0; + + loop { + if counter > 255 { + break Err(Error::DeriveKeyPair); + } + + // skS = G.HashToScalar(deriveInput || I2OSP(counter, 1), DST = "DeriveKeyPair" + // || contextString) + let counter_i2osp = i2osp_1(counter).map_err(|_| Error::DeriveKeyPair)?; + let sk_s = ::hash_to_scalar_with_dst::( + &[seed, info_len.as_slice(), info, counter_i2osp.as_slice()], + &dst, + ) + .map_err(|_| Error::DeriveKeyPair)?; + + if !bool::from(CS::Group::is_zero_scalar(sk_s)) { + let pk_s = CS::Group::base_elem() * &sk_s; + break Ok((sk_s, pk_s)); + } + counter += 1; + } +} + +// Inner function for blind that assumes that the blinding factor has already +// been chosen, and therefore takes it as input. Does not check if the blinding +// factor is non-zero. +// +// Can only fail with [`Error::Input`]. +pub(crate) fn deterministic_blind_unchecked( + input: &[u8], + blind: &::Scalar, + mode: Mode, +) -> Result<::Elem> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + let hashed_point = CS::Group::hash_to_curve::(&[input], mode).map_err(|_| Error::Input)?; + Ok(hashed_point * blind) +} + +/// Generates the contextString parameter as defined in +/// +pub(crate) fn create_context_string(mode: Mode) -> GenericArray +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + GenericArray::from(STR_VOPRF) + .concat([mode.to_u8()].into()) + .concat(CS::ID.to_be_bytes().into()) +} + +/////////////////////// +// Utility Functions // +// ================= // +/////////////////////// + +fn i2osp_1(input: usize) -> Result, InternalError> { + u8::try_from(input) + .map(|input| input.to_be_bytes().into()) + .map_err(|_| InternalError::I2osp) +} pub(crate) fn i2osp_2(input: usize) -> Result, InternalError> { u16::try_from(input) @@ -32,8 +447,8 @@ mod unit_tests { use proptest::prelude::*; use crate::{ - BlindedElement, EvaluationElement, NonVerifiableClient, NonVerifiableServer, Proof, - VerifiableClient, VerifiableServer, + BlindedElement, EvaluationElement, OprfClient, OprfServer, PoprfClient, PoprfServer, Proof, + VoprfClient, VoprfServer, }; macro_rules! test_deserialize { @@ -49,25 +464,36 @@ mod unit_tests { proptest! { #[test] - fn test_nocrash_nonverifiable_client(bytes in vec(any::(), 0..200)) { - test_deserialize!(NonVerifiableClient, bytes); + fn test_nocrash_oprf_client(bytes in vec(any::(), 0..200)) { + test_deserialize!(OprfClient, bytes); + } + + #[test] + fn test_nocrash_voprf_client(bytes in vec(any::(), 0..200)) { + test_deserialize!(VoprfClient, bytes); } #[test] - fn test_nocrash_verifiable_client(bytes in vec(any::(), 0..200)) { - test_deserialize!(VerifiableClient, bytes); + fn test_nocrash_poprf_client(bytes in vec(any::(), 0..200)) { + test_deserialize!(PoprfClient, bytes); } #[test] - fn test_nocrash_nonverifiable_server(bytes in vec(any::(), 0..200)) { - test_deserialize!(NonVerifiableServer, bytes); + fn test_nocrash_oprf_server(bytes in vec(any::(), 0..200)) { + test_deserialize!(OprfServer, bytes); } #[test] - fn test_nocrash_verifiable_server(bytes in vec(any::(), 0..200)) { - test_deserialize!(VerifiableServer, bytes); + fn test_nocrash_voprf_server(bytes in vec(any::(), 0..200)) { + test_deserialize!(VoprfServer, bytes); } + #[test] + fn test_nocrash_poprf_server(bytes in vec(any::(), 0..200)) { + test_deserialize!(PoprfServer, bytes); + } + + #[test] fn test_nocrash_blinded_element(bytes in vec(any::(), 0..200)) { test_deserialize!(BlindedElement, bytes); diff --git a/src/voprf.rs b/src/voprf.rs index c954ae2..e543ebb 100644 --- a/src/voprf.rs +++ b/src/voprf.rs @@ -14,73 +14,24 @@ use core::iter::{self, Map, Repeat, Zip}; use derive_where::derive_where; use digest::core_api::BlockSizeUser; use digest::{Digest, Output, OutputSizeUser}; -use generic_array::sequence::Concat; -use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U11, U20, U256}; +use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256, U8}; use generic_array::GenericArray; use rand_core::{CryptoRng, RngCore}; -use subtle::ConstantTimeEq; #[cfg(feature = "serde")] use crate::serialization::serde::{Element, Scalar}; -use crate::util::{i2osp_2, i2osp_2_array}; +use crate::util::{ + derive_keypair, generate_proof, i2osp_2, verify_proof, BlindedElement, EvaluationElement, Mode, + Proof, ProofElement, STR_FINALIZE, +}; use crate::{CipherSuite, Error, Group, Result}; -/////////////// -// Constants // -// ========= // -/////////////// - -const STR_FINALIZE: [u8; 9] = *b"Finalize-"; -const STR_SEED: [u8; 5] = *b"Seed-"; -const STR_CONTEXT: [u8; 8] = *b"Context-"; -const STR_COMPOSITE: [u8; 10] = *b"Composite-"; -const STR_CHALLENGE: [u8; 10] = *b"Challenge-"; -const STR_VOPRF: [u8; 8] = *b"VOPRF08-"; - -/// Determines the mode of operation (either base mode or verifiable mode). This -/// is only used for custom implementations for [`Group`]. -#[derive(Clone, Copy, Debug)] -pub enum Mode { - /// Non-verifiable mode. - Base, - /// Verifiable mode. - Verifiable, -} - -impl Mode { - /// Mode as it is represented in a context string. - pub fn to_u8(self) -> u8 { - match self { - Mode::Base => 0, - Mode::Verifiable => 1, - } - } -} - //////////////////////////// // High-level API Structs // // ====================== // //////////////////////////// -/// A client which engages with a [NonVerifiableServer] in base mode, meaning -/// that the OPRF outputs are not verifiable. -#[derive_where(Clone, ZeroizeOnDrop)] -#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar)] -#[cfg_attr( - feature = "serde", - derive(serde::Deserialize, serde::Serialize), - serde(crate = "serde", bound = "") -)] -pub struct NonVerifiableClient -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - #[cfg_attr(feature = "serde", serde(with = "Scalar::"))] - pub(crate) blind: ::Scalar, -} - -/// A client which engages with a [VerifiableServer] in verifiable mode, meaning +/// A client which engages with a [VoprfServer] in verifiable mode, meaning /// that the OPRF outputs can be checked against a server public key. #[derive_where(Clone, ZeroizeOnDrop)] #[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar, ::Elem)] @@ -89,7 +40,7 @@ where derive(serde::Deserialize, serde::Serialize), serde(crate = "serde", bound = "") )] -pub struct VerifiableClient +pub struct VoprfClient where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, @@ -100,25 +51,7 @@ where pub(crate) blinded_element: ::Elem, } -/// A server which engages with a [NonVerifiableClient] in base mode, meaning -/// that the OPRF outputs are not verifiable. -#[derive_where(Clone, ZeroizeOnDrop)] -#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar)] -#[cfg_attr( - feature = "serde", - derive(serde::Deserialize, serde::Serialize), - serde(crate = "serde", bound = "") -)] -pub struct NonVerifiableServer -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - #[cfg_attr(feature = "serde", serde(with = "Scalar::"))] - pub(crate) sk: ::Scalar, -} - -/// A server which engages with a [VerifiableClient] in verifiable mode, meaning +/// A server which engages with a [VoprfClient] in verifiable mode, meaning /// that the OPRF outputs can be checked against a server public key. #[derive_where(Clone, ZeroizeOnDrop)] #[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar, ::Elem)] @@ -127,7 +60,7 @@ where derive(serde::Deserialize, serde::Serialize), serde(crate = "serde", bound = "") )] -pub struct VerifiableServer +pub struct VoprfServer where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, @@ -138,66 +71,12 @@ where pub(crate) pk: ::Elem, } -/// A proof produced by a [VerifiableServer] that the OPRF output matches -/// against a server public key. -#[derive_where(Clone, ZeroizeOnDrop)] -#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar)] -#[cfg_attr( - feature = "serde", - derive(serde::Deserialize, serde::Serialize), - serde(crate = "serde", bound = "") -)] -pub struct Proof -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - #[cfg_attr(feature = "serde", serde(with = "Scalar::"))] - pub(crate) c_scalar: ::Scalar, - #[cfg_attr(feature = "serde", serde(with = "Scalar::"))] - pub(crate) s_scalar: ::Scalar, -} - -/// The first client message sent from a client (either verifiable or not) to a -/// server (either verifiable or not). -#[derive_where(Clone, ZeroizeOnDrop)] -#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Elem)] -#[cfg_attr( - feature = "serde", - derive(serde::Deserialize, serde::Serialize), - serde(crate = "serde", bound = "") -)] -pub struct BlindedElement( - #[cfg_attr(feature = "serde", serde(with = "Element::"))] - pub(crate) ::Elem, -) -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>; - -/// The server's response to the [BlindedElement] message from a client (either -/// verifiable or not) to a server (either verifiable or not). -#[derive_where(Clone, ZeroizeOnDrop)] -#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Elem)] -#[cfg_attr( - feature = "serde", - derive(serde::Deserialize, serde::Serialize), - serde(crate = "serde", bound = "") -)] -pub struct EvaluationElement( - #[cfg_attr(feature = "serde", serde(with = "Element::"))] - pub(crate) ::Elem, -) -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>; - ///////////////////////// // API Implementations // // =================== // ///////////////////////// -impl NonVerifiableClient +impl VoprfClient where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, @@ -210,12 +89,9 @@ where pub fn blind( input: &[u8], blinding_factor_rng: &mut R, - ) -> Result> { - let (blind, blinded_element) = blind::(input, blinding_factor_rng, Mode::Base)?; - Ok(NonVerifiableClientBlindResult { - state: Self { blind }, - message: BlindedElement(blinded_element), - }) + ) -> Result> { + let blind = CS::Group::random_scalar(blinding_factor_rng); + Self::deterministic_blind_unchecked_inner(input, blind) } #[cfg(any(feature = "danger", test))] @@ -233,91 +109,21 @@ where pub fn deterministic_blind_unchecked( input: &[u8], blind: ::Scalar, - ) -> Result> { - let blinded_element = deterministic_blind_unchecked::(input, &blind, Mode::Base)?; - Ok(NonVerifiableClientBlindResult { - state: Self { blind }, - message: BlindedElement(blinded_element), - }) + ) -> Result> { + Self::deterministic_blind_unchecked_inner(input, blind) } - /// Computes the third step for the multiplicative blinding version of - /// DH-OPRF, in which the client unblinds the server's message. - /// - /// # Errors - /// - [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`]. - /// - [`Error::Metadata`] if the `metadata` is longer then `u16::MAX - 21`. - pub fn finalize( - &self, - input: &[u8], - evaluation_element: &EvaluationElement, - metadata: Option<&[u8]>, - ) -> Result> { - let unblinded_element = evaluation_element.0 * &CS::Group::invert_scalar(self.blind); - let mut outputs = finalize_after_unblind::( - iter::once((input, unblinded_element)), - metadata.unwrap_or_default(), - Mode::Base, - ); - outputs.next().unwrap() - } - - #[cfg(test)] - /// Only used for test functions - pub fn from_blind(blind: ::Scalar) -> Self { - Self { blind } - } - - #[cfg(feature = "danger")] - /// Exposes the blind group element - pub fn get_blind(&self) -> ::Scalar { - self.blind - } -} - -impl VerifiableClient -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - /// Computes the first step for the multiplicative blinding version of - /// DH-OPRF. + /// Inner function for computing blind output /// /// # Errors /// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`]. - pub fn blind( - input: &[u8], - blinding_factor_rng: &mut R, - ) -> Result> { - let (blind, blinded_element) = - blind::(input, blinding_factor_rng, Mode::Verifiable)?; - Ok(VerifiableClientBlindResult { - state: Self { - blind, - blinded_element, - }, - message: BlindedElement(blinded_element), - }) - } - - #[cfg(any(feature = "danger", test))] - /// Computes the first step for the multiplicative blinding version of - /// DH-OPRF, taking a blinding factor scalar as input instead of sampling - /// from an RNG. - /// - /// # Caution - /// - /// This should be used with caution, since it does not perform any checks - /// on the validity of the blinding factor! - /// - /// # Errors - /// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`]. - pub fn deterministic_blind_unchecked( + fn deterministic_blind_unchecked_inner( input: &[u8], blind: ::Scalar, - ) -> Result> { - let blinded_element = deterministic_blind_unchecked::(input, &blind, Mode::Verifiable)?; - Ok(VerifiableClientBlindResult { + ) -> Result> { + let blinded_element = + crate::util::deterministic_blind_unchecked::(input, &blind, Mode::Voprf)?; + Ok(VoprfClientBlindResult { state: Self { blind, blinded_element, @@ -331,7 +137,6 @@ where /// /// # Errors /// - [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`]. - /// - [`Error::Metadata`] if the `metadata` is longer then `u16::MAX - 21`. /// - [`Error::ProofVerification`] if the `proof` failed to verify. pub fn finalize( &self, @@ -339,22 +144,19 @@ where evaluation_element: &EvaluationElement, proof: &Proof, pk: ::Elem, - metadata: Option<&[u8]>, ) -> Result> { let inputs = core::array::from_ref(&input); let clients = core::array::from_ref(self); let messages = core::array::from_ref(evaluation_element); - let mut batch_result = - Self::batch_finalize(inputs, clients, messages, proof, pk, metadata)?; + let mut batch_result = Self::batch_finalize(inputs, clients, messages, proof, pk)?; batch_result.next().unwrap() } - /// Allows for batching of the finalization of multiple [VerifiableClient] + /// Allows for batching of the finalization of multiple [VoprfClient] /// and [EvaluationElement] pairs /// /// # Errors - /// - [`Error::Metadata`] if the `metadata` is longer then `u16::MAX - 21`. /// - [`Error::Batch`] if the number of `clients` and `messages` don't match /// or is longer then [`u16::MAX`]. /// - [`Error::ProofVerification`] if the `proof` failed to verify. @@ -367,28 +169,21 @@ where messages: &'a IM, proof: &Proof, pk: ::Elem, - metadata: Option<&'a [u8]>, - ) -> Result> + ) -> Result> where CS: 'a, I: AsRef<[u8]>, &'a II: 'a + IntoIterator, <&'a II as IntoIterator>::IntoIter: ExactSizeIterator, - &'a IC: 'a + IntoIterator>, + &'a IC: 'a + IntoIterator>, <&'a IC as IntoIterator>::IntoIter: ExactSizeIterator, &'a IM: 'a + IntoIterator>, <&'a IM as IntoIterator>::IntoIter: ExactSizeIterator, { - let metadata = metadata.unwrap_or_default(); - - let unblinded_elements = verifiable_unblind(clients, messages, pk, proof, metadata)?; - + let unblinded_elements = verifiable_unblind(clients, messages, pk, proof)?; let inputs_and_unblinded_elements = inputs.into_iter().zip(unblinded_elements); - Ok(finalize_after_unblind::( inputs_and_unblinded_elements, - metadata, - Mode::Verifiable, )) } @@ -411,104 +206,20 @@ where } } -impl NonVerifiableServer +impl VoprfServer where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { - /// Produces a new instance of a [NonVerifiableServer] using a supplied RNG + /// Produces a new instance of a [VoprfServer] using a supplied RNG pub fn new(rng: &mut R) -> Self { let mut seed = Output::::default(); rng.fill_bytes(&mut seed); // This can't fail as the hash output is type constrained. - Self::new_from_seed(&seed).unwrap() - } - - /// Produces a new instance of a [NonVerifiableServer] using a supplied set - /// of bytes to represent the server's private key - /// - /// # Errors - /// [`Error::Deserialization`] if the private key is not a valid point on - /// the group or zero. - pub fn new_with_key(private_key_bytes: &[u8]) -> Result { - let sk = CS::Group::deserialize_scalar(private_key_bytes)?; - Ok(Self { sk }) + Self::new_from_seed(&seed, &[]).unwrap() } - /// Produces a new instance of a [NonVerifiableServer] using a supplied set - /// of bytes which are used as a seed to derive the server's private key. - /// - /// Corresponds to DeriveKeyPair() function from the VOPRF specification. - /// - /// # Errors - /// [`Error::Seed`] if the `seed` is empty or longer then [`u16::MAX`]. - pub fn new_from_seed(seed: &[u8]) -> Result { - let sk = CS::Group::hash_to_scalar::(&[seed], Mode::Base).map_err(|_| Error::Seed)?; - Ok(Self { sk }) - } - - // Only used for tests - #[cfg(test)] - pub fn get_private_key(&self) -> ::Scalar { - self.sk - } - - /// 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. - /// - /// # Errors - /// - [`Error::Metadata`] if the `metadata` is longer then `u16::MAX - 21`. - /// - [`Error::Protocol`] if the protocol fails and can't be completed. - pub fn evaluate( - &self, - blinded_element: &BlindedElement, - metadata: Option<&[u8]>, - ) -> Result> { - // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.1.1-1 - - let context_string = get_context_string::(Mode::Base); - let metadata = metadata.unwrap_or_default(); - - // context = "Context-" || contextString || I2OSP(len(info), 2) || info - let context = GenericArray::from(STR_CONTEXT) - .concat(context_string) - .concat(i2osp_2(metadata.len()).map_err(|_| Error::Metadata)?); - let context = [&context, metadata]; - - // m = GG.HashToScalar(context) - let m = - CS::Group::hash_to_scalar::(&context, Mode::Base).map_err(|_| Error::Metadata)?; - // t = skS + m - let t = self.sk + &m; - - // if t == 0: - if bool::from(CS::Group::is_zero_scalar(t)) { - // raise InverseError - return Err(Error::Protocol); - } - - // Z = t^(-1) * R - let z = blinded_element.0 * &CS::Group::invert_scalar(t); - - Ok(EvaluationElement(z)) - } -} - -impl VerifiableServer -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - /// Produces a new instance of a [VerifiableServer] using a supplied RNG - pub fn new(rng: &mut R) -> Self { - let mut seed = Output::::default(); - rng.fill_bytes(&mut seed); - // This can't fail as the hash output is type constrained. - Self::new_from_seed(&seed).unwrap() - } - - /// Produces a new instance of a [VerifiableServer] using a supplied set of + /// Produces a new instance of a [VoprfServer] using a supplied set of /// bytes to represent the server's private key /// /// # Errors @@ -520,17 +231,15 @@ where Ok(Self { sk, pk }) } - /// Produces a new instance of a [VerifiableServer] using a supplied set of + /// Produces a new instance of a [VoprfServer] using a supplied set of /// bytes which are used as a seed to derive the server's private key. /// /// Corresponds to DeriveKeyPair() function from the VOPRF specification. /// /// # Errors /// [`Error::Seed`] if the `seed` is empty or longer then [`u16::MAX`]. - pub fn new_from_seed(seed: &[u8]) -> Result { - let sk = - CS::Group::hash_to_scalar::(&[seed], Mode::Verifiable).map_err(|_| Error::Seed)?; - let pk = CS::Group::base_elem() * &sk; + pub fn new_from_seed(seed: &[u8], info: &[u8]) -> Result { + let (sk, pk) = derive_keypair::(seed, info, Mode::Voprf).map_err(|_| Error::Seed)?; Ok(Self { sk, pk }) } @@ -545,162 +254,55 @@ where /// to the client. /// /// # Errors - /// - [`Error::Metadata`] if the `metadata` is longer then `u16::MAX - 21`. /// - [`Error::Protocol`] if the protocol fails and can't be completed. pub fn evaluate( &self, rng: &mut R, blinded_element: &BlindedElement, - metadata: Option<&[u8]>, - ) -> Result> { - let VerifiableServerBatchEvaluatePrepareResult { - prepared_evaluation_elements: mut evaluation_elements, - t, - } = self.batch_evaluate_prepare(iter::once(blinded_element), metadata)?; - - let prepared_element = [evaluation_elements.next().unwrap()]; - - // This can't fail because we know the size of the inputs. - let VerifiableServerBatchEvaluateFinishResult { - mut messages, - proof, - } = Self::batch_evaluate_finish(rng, iter::once(blinded_element), &prepared_element, &t) - .unwrap(); - - let message = messages.next().unwrap(); - - //let batch_result = self.batch_evaluate(rng, blinded_elements, metadata)?; - Ok(VerifiableServerEvaluateResult { message, proof }) + ) -> Result> { + let batch_evaluate_result = self.batch_evaluate(rng, std::vec![blinded_element.clone()])?; + Ok(VoprfServerEvaluateResult { + message: batch_evaluate_result.messages.get(0).unwrap().clone(), + proof: batch_evaluate_result.proof, + }) } /// Allows for batching of the evaluation of multiple [BlindedElement] - /// messages from a [VerifiableClient] + /// messages from a [VoprfClient] /// /// # Errors - /// - [`Error::Metadata`] if the `metadata` is longer then `u16::MAX - 21`. /// - [`Error::Protocol`] if the protocol fails and can't be completed. - #[cfg(feature = "alloc")] - pub fn batch_evaluate<'a, R: RngCore + CryptoRng, I>( + pub fn batch_evaluate( &self, rng: &mut R, - blinded_elements: &'a I, - metadata: Option<&[u8]>, - ) -> Result> - where - CS: 'a, - &'a I: IntoIterator>, - <&'a I as IntoIterator>::IntoIter: ExactSizeIterator, - { - let VerifiableServerBatchEvaluatePrepareResult { - prepared_evaluation_elements: evaluation_elements, - t, - } = self.batch_evaluate_prepare(blinded_elements.into_iter(), metadata)?; - - let prepared_elements = evaluation_elements.collect(); - - // This can't fail because we know the size of the inputs. - let VerifiableServerBatchEvaluateFinishResult { messages, proof } = - Self::batch_evaluate_finish::<_, _, Vec<_>>( - rng, - blinded_elements.into_iter(), - &prepared_elements, - &t, - ) - .unwrap(); - - Ok(VerifiableServerBatchEvaluateResult { - messages: messages.collect(), - proof, - }) - } - - /// Alternative version of [`batch_evaluate`](Self::batch_evaluate) without - /// memory allocation. Returned [`PreparedEvaluationElement`] have to be - /// [`collect`](Iterator::collect)ed and passed into - /// [`batch_evaluate_finish`](Self::batch_evaluate_finish). - /// - /// # Errors - /// - [`Error::Metadata`] if the `metadata` is longer then `u16::MAX - 21`. - /// - [`Error::Protocol`] if the protocol fails and can't be completed. - pub fn batch_evaluate_prepare<'a, I: Iterator>>( - &self, - blinded_elements: I, - metadata: Option<&[u8]>, - ) -> Result> { - // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.2.1-1 - - let context_string = get_context_string::(Mode::Verifiable); - let metadata = metadata.unwrap_or_default(); - - // context = "Context-" || contextString || I2OSP(len(info), 2) || info - let context = GenericArray::from(STR_CONTEXT) - .concat(context_string) - .concat(i2osp_2(metadata.len()).map_err(|_| Error::Metadata)?); - let context = [&context, metadata]; - - let m = CS::Group::hash_to_scalar::(&context, Mode::Verifiable) - .map_err(|_| Error::Metadata)?; - let t = self.sk + &m; - - // if t == 0: - if bool::from(CS::Group::is_zero_scalar(t)) { - // raise InverseError - return Err(Error::Protocol); - } - - let evaluation_elements = blinded_elements - // To make a return type possible, we have to convert to a `fn` pointer, which isn't - // possible if we `move` from context. - .zip(iter::repeat(CS::Group::invert_scalar(t))) - .map(, _)) -> _>::from(|(x, t)| { - PreparedEvaluationElement(EvaluationElement(x.0 * &t)) - })); - - Ok(VerifiableServerBatchEvaluatePrepareResult { - prepared_evaluation_elements: evaluation_elements, - t: PreparedTscalar(t), - }) - } + blinded_elements: Vec>, + ) -> Result> { + let evaluation_elements: Vec<_> = blinded_elements + .iter() + .map(|blinded_element| blinded_element.0 * &self.sk) + .collect(); + + let messages = evaluation_elements + .iter() + .map(|element| EvaluationElement(*element)) + .collect(); - /// See [`batch_evaluate_prepare`](Self::batch_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<'a, 'b, R: RngCore + CryptoRng, IB, IE>( - rng: &mut R, - blinded_elements: IB, - evaluation_elements: &'b IE, - PreparedTscalar(t): &PreparedTscalar, - ) -> Result> - where - CS: 'a + 'b, - IB: Iterator> + ExactSizeIterator, - &'b IE: IntoIterator>, - <&'b IE as IntoIterator>::IntoIter: ExactSizeIterator, - { let g = CS::Group::base_elem(); - let u = g * t; - let proof = generate_proof( rng, - *t, + self.sk, g, - u, - evaluation_elements + self.pk, + blinded_elements .into_iter() - .map(|element| element.0.clone()), - blinded_elements.cloned(), - )?; - let messages = + .map(|element| ProofElement(element.0)), evaluation_elements .into_iter() - .map() -> _>::from(|element| { - element.0.clone() - })); + .map(|element| ProofElement(element)), + Mode::Voprf, + )?; - Ok(VerifiableServerBatchEvaluateFinishResult { messages, proof }) + Ok(VoprfServerBatchEvaluateResult { messages, proof }) } /// Retrieves the server's public key @@ -709,94 +311,35 @@ where } } -impl BlindedElement -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - #[cfg(feature = "danger")] - /// Creates a [BlindedElement] from a raw group element. - /// - /// # Caution - /// - /// This should be used with caution, since it does not perform any checks - /// on the validity of the value itself! - pub fn from_value_unchecked(value: ::Elem) -> Self { - Self(value) - } - - #[cfg(feature = "danger")] - /// Exposes the internal value - pub fn value(&self) -> ::Elem { - self.0 - } -} - -impl EvaluationElement -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - #[cfg(feature = "danger")] - /// Creates an [EvaluationElement] from a raw group element. - /// - /// # Caution - /// - /// This should be used with caution, since it does not perform any checks - /// on the validity of the value itself! - pub fn from_value_unchecked(value: ::Elem) -> Self { - Self(value) - } - - #[cfg(feature = "danger")] - /// Exposes the internal value - pub fn value(&self) -> ::Elem { - self.0 - } -} - ///////////////////////// // Convenience Structs // //==================== // ///////////////////////// -/// Contains the fields that are returned by a non-verifiable client blind -#[derive_where(Debug; ::Scalar, ::Elem)] -pub struct NonVerifiableClientBlindResult -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - /// The state to be persisted on the client - pub state: NonVerifiableClient, - /// The message to send to the server - pub message: BlindedElement, -} - /// Contains the fields that are returned by a verifiable client blind #[derive_where(Debug; ::Scalar, ::Elem)] -pub struct VerifiableClientBlindResult +pub struct VoprfClientBlindResult where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { /// The state to be persisted on the client - pub state: VerifiableClient, + pub state: VoprfClient, /// The message to send to the server pub message: BlindedElement, } -/// Concrete return type for [`VerifiableClient::batch_finalize`]. -pub type VerifiableClientBatchFinalizeResult<'a, C, I, II, IC, IM> = FinalizeAfterUnblindResult< +/// Concrete return type for [`VoprfClient::batch_finalize`]. +pub type VoprfClientBatchFinalizeResult<'a, C, I, II, IC, IM> = FinalizeAfterUnblindResult< 'a, C, I, - Zip<<&'a II as IntoIterator>::IntoIter, VerifiableUnblindResult<'a, C, IC, IM>>, + Zip<<&'a II as IntoIterator>::IntoIter, VoprfUnblindResult<'a, C, IC, IM>>, >; /// Contains the fields that are returned by a verifiable server evaluate #[derive_where(Debug; ::Scalar, ::Elem)] -pub struct VerifiableServerEvaluateResult +pub struct VoprfServerEvaluateResult where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, @@ -807,92 +350,10 @@ where pub proof: Proof, } -/// Contains prepared [`EvaluationElement`]s by a verifiable server batch -/// evaluate preparation. -#[derive_where(Clone, ZeroizeOnDrop)] -#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Elem)] -#[cfg_attr( - feature = "serde", - derive(serde::Deserialize, serde::Serialize), - serde(crate = "serde", bound = "") -)] -pub struct PreparedEvaluationElement(EvaluationElement) -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>; - -/// Contains the prepared `t` by a verifiable server batch evaluate preparation. -#[derive_where(Clone, ZeroizeOnDrop)] -#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar)] -#[cfg_attr( - feature = "serde", - derive(serde::Deserialize, serde::Serialize), - serde(crate = "serde", bound = "") -)] -pub struct PreparedTscalar( - #[cfg_attr(feature = "serde", serde(with = "Scalar::"))] - ::Scalar, -) -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>; - -/// Concrete type of [`EvaluationElement`]s in -/// [`VerifiableServerBatchEvaluatePrepareResult`]. -pub type VerifiableServerBatchEvaluatePreparedEvaluationElements = Map< - Zip::Group as Group>::Scalar>>, - fn( - ( - &BlindedElement, - <::Group as Group>::Scalar, - ), - ) -> PreparedEvaluationElement, ->; - -/// Contains the fields that are returned by a verifiable server batch evaluate -/// preparation. -#[derive_where(Debug; I, ::Scalar)] -pub struct VerifiableServerBatchEvaluatePrepareResult< - 'a, - CS: 'a + CipherSuite, - I: Iterator>, -> where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - /// Prepared [`EvaluationElement`]s that will become messages. - pub prepared_evaluation_elements: - VerifiableServerBatchEvaluatePreparedEvaluationElements, - /// Prepared `t` needed to finish the verifiable server batch evaluation. - pub t: PreparedTscalar, -} - -/// Concrete type of [`EvaluationElement`]s in -/// [`VerifiableServerBatchEvaluateFinishResult`]. -pub type VerifiableServerBatchEvaluateFinishedMessages<'a, CS, I> = Map< - <&'a I as IntoIterator>::IntoIter, - fn(&PreparedEvaluationElement) -> EvaluationElement, ->; - -/// Contains the fields that are returned by a verifiable server batch evaluate -/// finish. -#[derive_where(Debug; <&'a I as core::iter::IntoIterator>::IntoIter, ::Scalar)] -pub struct VerifiableServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I> -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, - &'a I: IntoIterator>, -{ - /// The [`EvaluationElement`]s to send to the client - pub messages: VerifiableServerBatchEvaluateFinishedMessages<'a, CS, I>, - /// The proof for the client to verify - pub proof: Proof, -} - /// Contains the fields that are returned by a verifiable server batch evaluate #[derive_where(Debug; ::Scalar, ::Elem)] #[cfg(feature = "alloc")] -pub struct VerifiableServerBatchEvaluateResult +pub struct VoprfServerBatchEvaluateResult where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, @@ -908,52 +369,11 @@ where // =============== // ///////////////////// -type BlindResult = ( - <::Group as Group>::Scalar, - <::Group as Group>::Elem, -); - -// Inner function for blind. Returns the blind scalar and the blinded element -// -// Can only fail with [`Error::Input`]. -fn blind( - input: &[u8], - blinding_factor_rng: &mut R, - mode: Mode, -) -> Result> -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - // Choose a random scalar that must be non-zero - let blind = CS::Group::random_scalar(blinding_factor_rng); - let blinded_element = deterministic_blind_unchecked::(input, &blind, mode)?; - Ok((blind, blinded_element)) -} - -// Inner function for blind that assumes that the blinding factor has already -// been chosen, and therefore takes it as input. Does not check if the blinding -// factor is non-zero. -// -// Can only fail with [`Error::Input`]. -fn deterministic_blind_unchecked( - input: &[u8], - blind: &::Scalar, - mode: Mode, -) -> Result<::Elem> -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - let hashed_point = CS::Group::hash_to_curve::(&[input], mode).map_err(|_| Error::Input)?; - Ok(hashed_point * blind) -} - -type VerifiableUnblindResult<'a, CS, IC, IM> = Map< +type VoprfUnblindResult<'a, CS, IC, IM> = Map< Zip< Map< <&'a IC as IntoIterator>::IntoIter, - fn(&VerifiableClient) -> <::Group as Group>::Scalar, + fn(&VoprfClient) -> <::Group as Group>::Scalar, >, <&'a IM as IntoIterator>::IntoIter, >, @@ -965,200 +385,58 @@ type VerifiableUnblindResult<'a, CS, IC, IM> = Map< ) -> <::Group as Group>::Elem, >; -// Can only fail with [`Error::Metadata`], [`Error::Batch] or +// Can only fail with [`Error::Info`], [`Error::Batch] or // [`Error::ProofVerification`]. fn verifiable_unblind<'a, CS: 'a + CipherSuite, IC, IM>( clients: &'a IC, messages: &'a IM, pk: ::Elem, proof: &Proof, - info: &[u8], -) -> Result> +) -> Result> where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, - &'a IC: 'a + IntoIterator>, + &'a IC: 'a + IntoIterator>, <&'a IC as IntoIterator>::IntoIter: ExactSizeIterator, &'a IM: 'a + IntoIterator>, <&'a IM as IntoIterator>::IntoIter: ExactSizeIterator, { - // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.4.2-2 - - let context_string = get_context_string::(Mode::Verifiable); - - // context = "Context-" || contextString || I2OSP(len(info), 2) || info - let context = GenericArray::from(STR_CONTEXT) - .concat(context_string) - .concat(i2osp_2(info.len()).map_err(|_| Error::Metadata)?); - let context = [&context, info]; - - // The `input` used here is the metadata. - let m = - CS::Group::hash_to_scalar::(&context, Mode::Verifiable).map_err(|_| Error::Metadata)?; - let g = CS::Group::base_elem(); - let t = g * &m; - let u = t + &pk; let blinds = clients .into_iter() // Convert to `fn` pointer to make a return type possible. - .map() -> _>::from(|x| x.blind)); - let evaluation_elements = messages.into_iter().cloned(); + .map() -> _>::from(|x| x.blind)); + let evaluation_elements = messages.into_iter().map(|element| ProofElement(element.0)); let blinded_elements = clients .into_iter() - .map(|client| BlindedElement(client.blinded_element)); + .map(|client| ProofElement(client.blinded_element)); - verify_proof(g, u, evaluation_elements, blinded_elements, proof)?; + verify_proof( + g, + pk, + blinded_elements, + evaluation_elements, + proof, + Mode::Voprf, + )?; Ok(blinds .zip(messages.into_iter()) .map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind))) } -// Can only fail with [`Error::Batch`]. -#[allow(clippy::many_single_char_names)] -fn generate_proof( - rng: &mut R, - k: ::Scalar, - a: ::Elem, - b: ::Elem, - cs: impl Iterator> + ExactSizeIterator, - ds: impl Iterator> + ExactSizeIterator, -) -> Result> -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.2.2-1 - - let (m, z) = compute_composites(Some(k), b, cs, ds)?; - - let r = CS::Group::random_scalar(rng); - let t2 = a * &r; - let t3 = m * &r; - - // Bm = GG.SerializeElement(B) - let bm = CS::Group::serialize_elem(b); - // a0 = GG.SerializeElement(M) - let a0 = CS::Group::serialize_elem(m); - // a1 = GG.SerializeElement(Z) - let a1 = CS::Group::serialize_elem(z); - // a2 = GG.SerializeElement(t2) - let a2 = CS::Group::serialize_elem(t2); - // a3 = GG.SerializeElement(t3) - let a3 = CS::Group::serialize_elem(t3); - - let elem_len = ::ElemLen::U16.to_be_bytes(); - - // challengeDST = "Challenge-" || contextString - let challenge_dst = - GenericArray::from(STR_CHALLENGE).concat(get_context_string::(Mode::Verifiable)); - let challenge_dst_len = i2osp_2_array(&challenge_dst); - // h2Input = I2OSP(len(Bm), 2) || Bm || - // I2OSP(len(a0), 2) || a0 || - // I2OSP(len(a1), 2) || a1 || - // I2OSP(len(a2), 2) || a2 || - // I2OSP(len(a3), 2) || a3 || - // I2OSP(len(challengeDST), 2) || challengeDST - let h2_input = [ - &elem_len, - bm.as_slice(), - &elem_len, - &a0, - &elem_len, - &a1, - &elem_len, - &a2, - &elem_len, - &a3, - &challenge_dst_len, - &challenge_dst, - ]; - - // This can't fail, the size of the `input` is known. - let c_scalar = CS::Group::hash_to_scalar::(&h2_input, Mode::Verifiable).unwrap(); - let s_scalar = r - &(c_scalar * &k); - - Ok(Proof { c_scalar, s_scalar }) -} - -// Can only fail with [`Error::ProofVerification`] or [`Error::Batch`]. -#[allow(clippy::many_single_char_names)] -fn verify_proof( - a: ::Elem, - b: ::Elem, - cs: impl Iterator> + ExactSizeIterator, - ds: impl Iterator> + ExactSizeIterator, - proof: &Proof, -) -> Result<()> -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.4.1-2 - let (m, z) = compute_composites(None, b, cs, ds)?; - let t2 = (a * &proof.s_scalar) + &(b * &proof.c_scalar); - let t3 = (m * &proof.s_scalar) + &(z * &proof.c_scalar); - - // Bm = GG.SerializeElement(B) - let bm = CS::Group::serialize_elem(b); - // a0 = GG.SerializeElement(M) - let a0 = CS::Group::serialize_elem(m); - // a1 = GG.SerializeElement(Z) - let a1 = CS::Group::serialize_elem(z); - // a2 = GG.SerializeElement(t2) - let a2 = CS::Group::serialize_elem(t2); - // a3 = GG.SerializeElement(t3) - let a3 = CS::Group::serialize_elem(t3); - - let elem_len = ::ElemLen::U16.to_be_bytes(); - - // challengeDST = "Challenge-" || contextString - let challenge_dst = - GenericArray::from(STR_CHALLENGE).concat(get_context_string::(Mode::Verifiable)); - let challenge_dst_len = i2osp_2_array(&challenge_dst); - // h2Input = I2OSP(len(Bm), 2) || Bm || - // I2OSP(len(a0), 2) || a0 || - // I2OSP(len(a1), 2) || a1 || - // I2OSP(len(a2), 2) || a2 || - // I2OSP(len(a3), 2) || a3 || - // I2OSP(len(challengeDST), 2) || challengeDST - let h2_input = [ - &elem_len, - bm.as_slice(), - &elem_len, - &a0, - &elem_len, - &a1, - &elem_len, - &a2, - &elem_len, - &a3, - &challenge_dst_len, - &challenge_dst, - ]; - - // This can't fail, the size of the `input` is known. - let c = CS::Group::hash_to_scalar::(&h2_input, Mode::Verifiable).unwrap(); - - match c.ct_eq(&proof.c_scalar).into() { - true => Ok(()), - false => Err(Error::ProofVerification), - } -} - type FinalizeAfterUnblindResult<'a, C, I, IE> = Map< - Zip)>>, + Zip>>, fn( ( (I, <::Group as Group>::Elem), - (&'a [u8], GenericArray), + GenericArray, ), ) -> Result::Hash>>, >; -// Returned values can only fail with [`Error::Input`] or [`Error::Metadata`]. +// Returned values can only fail with [`Error::Input`] or [`Error::Info`]. fn finalize_after_unblind< 'a, CS: CipherSuite, @@ -1166,136 +444,34 @@ fn finalize_after_unblind< IE: 'a + Iterator::Elem)>, >( inputs_and_unblinded_elements: IE, - info: &'a [u8], - mode: Mode, -) -> FinalizeAfterUnblindResult +) -> FinalizeAfterUnblindResult<'a, CS, I, IE> where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { - // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.3.2-2 - // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.4.3-1 - - // finalizeDST = "Finalize-" || contextString - let finalize_dst = GenericArray::from(STR_FINALIZE).concat(get_context_string::(mode)); + let finalize_dst = GenericArray::from(STR_FINALIZE); inputs_and_unblinded_elements // To make a return type possible, we have to convert to a `fn` pointer, // which isn't possible if we `move` from context. - .zip(iter::repeat((info, finalize_dst))) - .map(|((input, unblinded_element), (info, finalize_dst))| { - let finalize_dst_len = i2osp_2_array(&finalize_dst); + .zip(iter::repeat(finalize_dst)) + .map(|((input, unblinded_element), finalize_dst)| { let elem_len = ::ElemLen::U16.to_be_bytes(); // hashInput = I2OSP(len(input), 2) || input || - // I2OSP(len(info), 2) || info || // I2OSP(len(unblindedElement), 2) || unblindedElement || - // I2OSP(len(finalizeDST), 2) || finalizeDST + // "Finalize" // return Hash(hashInput) Ok(CS::Hash::new() .chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?) .chain_update(input.as_ref()) - .chain_update(i2osp_2(info.len()).map_err(|_| Error::Metadata)?) - .chain_update(info) .chain_update(elem_len) .chain_update(CS::Group::serialize_elem(unblinded_element)) - .chain_update(finalize_dst_len) .chain_update(finalize_dst) .finalize()) }) } -type ComputeCompositesResult = ( - <::Group as Group>::Elem, - <::Group as Group>::Elem, -); - -// Can only fail with [`Error::Batch`]. -fn compute_composites( - k_option: Option<::Scalar>, - b: ::Elem, - c_slice: impl Iterator> + ExactSizeIterator, - d_slice: impl Iterator> + ExactSizeIterator, -) -> Result> -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.2.3-2 - - let elem_len = ::ElemLen::U16.to_be_bytes(); - - if c_slice.len() != d_slice.len() { - return Err(Error::Batch); - } - - let len = u16::try_from(c_slice.len()).map_err(|_| Error::Batch)?; - - let seed_dst = GenericArray::from(STR_SEED).concat(get_context_string::(Mode::Verifiable)); - let composite_dst = - GenericArray::from(STR_COMPOSITE).concat(get_context_string::(Mode::Verifiable)); - let composite_dst_len = i2osp_2_array(&composite_dst); - - let seed = CS::Hash::new() - .chain_update(&elem_len) - .chain_update(CS::Group::serialize_elem(b)) - .chain_update(i2osp_2_array(&seed_dst)) - .chain_update(seed_dst) - .finalize(); - let seed_len = i2osp_2_array(&seed); - - let mut m = CS::Group::identity_elem(); - let mut z = CS::Group::identity_elem(); - - for (i, (c, d)) in (0..len).zip(c_slice.zip(d_slice)) { - // Ci = GG.SerializeElement(Cs[i]) - let ci = CS::Group::serialize_elem(c.0); - // Di = GG.SerializeElement(Ds[i]) - let di = CS::Group::serialize_elem(d.0); - // h2Input = I2OSP(len(seed), 2) || seed || I2OSP(i, 2) || - // I2OSP(len(Ci), 2) || Ci || - // I2OSP(len(Di), 2) || Di || - // I2OSP(len(compositeDST), 2) || compositeDST - let h2_input = [ - seed_len.as_slice(), - &seed, - &i.to_be_bytes(), - &elem_len, - &ci, - &elem_len, - &di, - &composite_dst_len, - &composite_dst, - ]; - // This can't fail, the size of the `input` is known. - let di = CS::Group::hash_to_scalar::(&h2_input, Mode::Verifiable).unwrap(); - m = c.0 * &di + &m; - z = match k_option { - Some(_) => z, - None => d.0 * &di + &z, - }; - } - - z = match k_option { - Some(k) => m * &k, - None => z, - }; - - Ok((m, z)) -} - -/// Generates the contextString parameter as defined in -/// -pub(crate) fn get_context_string(mode: Mode) -> GenericArray -where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, -{ - GenericArray::from(STR_VOPRF) - .concat([mode.to_u8()].into()) - .concat(CS::ID.to_be_bytes().into()) -} - /////////// // Tests // // ===== // @@ -1318,7 +494,6 @@ mod tests { fn prf( input: &[u8], key: ::Scalar, - info: &[u8], mode: Mode, ) -> Output where @@ -1327,53 +502,25 @@ mod tests { { let point = CS::Group::hash_to_curve::(&[input], mode).unwrap(); - let context_string = get_context_string::(mode); - let info_len = i2osp_2(info.len()).unwrap(); - let context = [&STR_CONTEXT, context_string.as_slice(), &info_len, info]; - - let m = CS::Group::hash_to_scalar::(&context, mode).unwrap(); + let res = point * &key; - let res = point * &CS::Group::invert_scalar(key + &m); - - finalize_after_unblind::(iter::once((input, res)), info, mode) + finalize_after_unblind::(iter::once((input, res))) .next() .unwrap() .unwrap() } - fn base_retrieval() - where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, - { - let input = b"input"; - let info = b"info"; - let mut rng = OsRng; - let client_blind_result = NonVerifiableClient::::blind(input, &mut rng).unwrap(); - let server = NonVerifiableServer::::new(&mut rng); - let message = server - .evaluate(&client_blind_result.message, Some(info)) - .unwrap(); - let client_finalize_result = client_blind_result - .state - .finalize(input, &message, Some(info)) - .unwrap(); - let res2 = prf::(input, server.get_private_key(), info, Mode::Base); - assert_eq!(client_finalize_result, res2); - } - fn verifiable_retrieval() where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { let input = b"input"; - let info = b"info"; let mut rng = OsRng; - let client_blind_result = VerifiableClient::::blind(input, &mut rng).unwrap(); - let server = VerifiableServer::::new(&mut rng); + let client_blind_result = VoprfClient::::blind(input, &mut rng).unwrap(); + let server = VoprfServer::::new(&mut rng); let server_result = server - .evaluate(&mut rng, &client_blind_result.message, Some(info)) + .evaluate(&mut rng, &client_blind_result.message) .unwrap(); let client_finalize_result = client_blind_result .state @@ -1382,46 +529,17 @@ mod tests { &server_result.message, &server_result.proof, server.get_public_key(), - Some(info), ) .unwrap(); - let res2 = prf::(input, server.get_private_key(), info, Mode::Verifiable); + let res2 = prf::(input, server.get_private_key(), Mode::Voprf); assert_eq!(client_finalize_result, res2); } - fn verifiable_bad_public_key() - where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, - { - let input = b"input"; - let info = b"info"; - let mut rng = OsRng; - let client_blind_result = VerifiableClient::::blind(input, &mut rng).unwrap(); - let server = VerifiableServer::::new(&mut rng); - let server_result = server - .evaluate(&mut rng, &client_blind_result.message, Some(info)) - .unwrap(); - let wrong_pk = { - // Choose a group element that is unlikely to be the right public key - CS::Group::hash_to_curve::(&[b"msg"], Mode::Base).unwrap() - }; - let client_finalize_result = client_blind_result.state.finalize( - input, - &server_result.message, - &server_result.proof, - wrong_pk, - Some(info), - ); - assert!(client_finalize_result.is_err()); - } - fn verifiable_batch_retrieval() where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { - let info = b"info"; let mut rng = OsRng; let mut inputs = vec![]; let mut client_states = vec![]; @@ -1430,42 +548,27 @@ mod tests { for _ in 0..num_iterations { let mut input = [0u8; 32]; rng.fill_bytes(&mut input); - let client_blind_result = VerifiableClient::::blind(&input, &mut rng).unwrap(); + let client_blind_result = VoprfClient::::blind(&input, &mut rng).unwrap(); inputs.push(input); client_states.push(client_blind_result.state); client_messages.push(client_blind_result.message); } - let server = VerifiableServer::::new(&mut rng); - let VerifiableServerBatchEvaluatePrepareResult { - prepared_evaluation_elements, - t, - } = server - .batch_evaluate_prepare(client_messages.iter(), Some(info)) - .unwrap(); - let prepared_elements: Vec<_> = prepared_evaluation_elements.collect(); - let VerifiableServerBatchEvaluateFinishResult { messages, proof } = - VerifiableServer::batch_evaluate_finish( - &mut rng, - client_messages.iter(), - &prepared_elements, - &t, - ) - .unwrap(); - let messages: Vec<_> = messages.collect(); - let client_finalize_result = VerifiableClient::batch_finalize( + let server = VoprfServer::::new(&mut rng); + let VoprfServerBatchEvaluateResult { messages, proof } = + server.batch_evaluate(&mut rng, client_messages).unwrap(); + let client_finalize_result = VoprfClient::batch_finalize( &inputs, &client_states, &messages, &proof, server.get_public_key(), - Some(info), ) .unwrap() .collect::>>() .unwrap(); let mut res2 = vec![]; for input in inputs.iter().take(num_iterations) { - let output = prf::(input, server.get_private_key(), info, Mode::Verifiable); + let output = prf::(input, server.get_private_key(), Mode::Voprf); res2.push(output); } assert_eq!(client_finalize_result, res2); @@ -1476,7 +579,6 @@ mod tests { ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { - let info = b"info"; let mut rng = OsRng; let mut inputs = vec![]; let mut client_states = vec![]; @@ -1485,94 +587,49 @@ mod tests { for _ in 0..num_iterations { let mut input = [0u8; 32]; rng.fill_bytes(&mut input); - let client_blind_result = VerifiableClient::::blind(&input, &mut rng).unwrap(); + let client_blind_result = VoprfClient::::blind(&input, &mut rng).unwrap(); inputs.push(input); client_states.push(client_blind_result.state); client_messages.push(client_blind_result.message); } - let server = VerifiableServer::::new(&mut rng); - let VerifiableServerBatchEvaluatePrepareResult { - prepared_evaluation_elements, - t, - } = server - .batch_evaluate_prepare(client_messages.iter(), Some(info)) - .unwrap(); - let prepared_elements: Vec<_> = prepared_evaluation_elements.collect(); - let VerifiableServerBatchEvaluateFinishResult { messages, proof } = - VerifiableServer::batch_evaluate_finish( - &mut rng, - client_messages.iter(), - &prepared_elements, - &t, - ) - .unwrap(); - let messages: Vec<_> = messages.collect(); + let server = VoprfServer::::new(&mut rng); + let VoprfServerBatchEvaluateResult { messages, proof } = + server.batch_evaluate(&mut rng, client_messages).unwrap(); let wrong_pk = { // Choose a group element that is unlikely to be the right public key - CS::Group::hash_to_curve::(&[b"msg"], Mode::Base).unwrap() + CS::Group::hash_to_curve::(&[b"msg"], Mode::Voprf).unwrap() }; - let client_finalize_result = VerifiableClient::batch_finalize( - &inputs, - &client_states, - &messages, - &proof, - wrong_pk, - Some(info), - ); + let client_finalize_result = + VoprfClient::batch_finalize(&inputs, &client_states, &messages, &proof, wrong_pk); assert!(client_finalize_result.is_err()); } - fn base_inversion_unsalted() - where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, - { - let mut rng = OsRng; - let mut input = [0u8; 64]; - rng.fill_bytes(&mut input); - let info = b"info"; - let client_blind_result = NonVerifiableClient::::blind(&input, &mut rng).unwrap(); - let client_finalize_result = client_blind_result - .state - .finalize( - &input, - &EvaluationElement(client_blind_result.message.0), - Some(info), - ) - .unwrap(); - - let point = CS::Group::hash_to_curve::(&[&input], Mode::Base).unwrap(); - let res2 = finalize_after_unblind::( - iter::once((input.as_ref(), point)), - info, - Mode::Base, - ) - .next() - .unwrap() - .unwrap(); - - assert_eq!(client_finalize_result, res2); - } - - fn zeroize_base_client() + fn verifiable_bad_public_key() where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, { let input = b"input"; let mut rng = OsRng; - let client_blind_result = NonVerifiableClient::::blind(input, &mut rng).unwrap(); - - let mut state = client_blind_result.state; - unsafe { ptr::drop_in_place(&mut state) }; - assert!(state.serialize().iter().all(|&x| x == 0)); - - let mut message = client_blind_result.message; - unsafe { ptr::drop_in_place(&mut message) }; - assert!(message.serialize().iter().all(|&x| x == 0)); + let client_blind_result = VoprfClient::::blind(input, &mut rng).unwrap(); + let server = VoprfServer::::new(&mut rng); + let server_result = server + .evaluate(&mut rng, &client_blind_result.message) + .unwrap(); + let wrong_pk = { + // Choose a group element that is unlikely to be the right public key + CS::Group::hash_to_curve::(&[b"msg"], Mode::Oprf).unwrap() + }; + let client_finalize_result = client_blind_result.state.finalize( + input, + &server_result.message, + &server_result.proof, + wrong_pk, + ); + assert!(client_finalize_result.is_err()); } - fn zeroize_verifiable_client() + fn zeroize_voprf_client() where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, @@ -1581,7 +638,7 @@ mod tests { { let input = b"input"; let mut rng = OsRng; - let client_blind_result = VerifiableClient::::blind(input, &mut rng).unwrap(); + let client_blind_result = VoprfClient::::blind(input, &mut rng).unwrap(); let mut state = client_blind_result.state; unsafe { ptr::drop_in_place(&mut state) }; @@ -1592,29 +649,7 @@ mod tests { assert!(message.serialize().iter().all(|&x| x == 0)); } - fn zeroize_base_server() - where - ::OutputSize: - IsLess + IsLessOrEqual<::BlockSize>, - { - let input = b"input"; - let info = b"info"; - let mut rng = OsRng; - let client_blind_result = NonVerifiableClient::::blind(input, &mut rng).unwrap(); - let server = NonVerifiableServer::::new(&mut rng); - let mut message = server - .evaluate(&client_blind_result.message, Some(info)) - .unwrap(); - - let mut state = server; - unsafe { ptr::drop_in_place(&mut state) }; - assert!(state.serialize().iter().all(|&x| x == 0)); - - unsafe { ptr::drop_in_place(&mut message) }; - assert!(message.serialize().iter().all(|&x| x == 0)); - } - - fn zeroize_verifiable_server() + fn zeroize_voprf_server() where ::OutputSize: IsLess + IsLessOrEqual<::BlockSize>, @@ -1624,12 +659,11 @@ mod tests { Sum<::ScalarLen, ::ScalarLen>: ArrayLength, { let input = b"input"; - let info = b"info"; let mut rng = OsRng; - let client_blind_result = VerifiableClient::::blind(input, &mut rng).unwrap(); - let server = VerifiableServer::::new(&mut rng); + let client_blind_result = VoprfClient::::blind(input, &mut rng).unwrap(); + let server = VoprfServer::::new(&mut rng); let server_result = server - .evaluate(&mut rng, &client_blind_result.message, Some(info)) + .evaluate(&mut rng, &client_blind_result.message) .unwrap(); let mut state = server; @@ -1653,30 +687,22 @@ mod tests { { use crate::Ristretto255; - base_retrieval::(); - base_inversion_unsalted::(); verifiable_retrieval::(); verifiable_batch_retrieval::(); verifiable_bad_public_key::(); verifiable_batch_bad_public_key::(); - zeroize_base_client::(); - zeroize_base_server::(); - zeroize_verifiable_client::(); - zeroize_verifiable_server::(); + zeroize_voprf_client::(); + zeroize_voprf_server::(); } - base_retrieval::(); - base_inversion_unsalted::(); verifiable_retrieval::(); verifiable_batch_retrieval::(); verifiable_bad_public_key::(); verifiable_batch_bad_public_key::(); - zeroize_base_client::(); - zeroize_base_server::(); - zeroize_verifiable_client::(); - zeroize_verifiable_server::(); + zeroize_voprf_client::(); + zeroize_voprf_server::(); Ok(()) }