Merging Version 09 changes into main (#60)
* Syncing new test vectors and base mode * Working set of test vectors for VOPRF mode * Adding POPRF * POPRF test vectors in sync * Address review (#59) Co-authored-by: daxpedda <[email protected]>
This commit is contained in:
+4
-4
@@ -15,18 +15,18 @@ pub type Result<T, E = Error> = core::result::Result<T, E>;
|
||||
/// Represents an error in the manipulation of internal cryptographic data
|
||||
#[derive(Clone, Copy, Debug, Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub enum Error {
|
||||
/// Size of info is longer then [`u16::MAX`].
|
||||
Info,
|
||||
/// Size of input is empty or longer then [`u16::MAX`].
|
||||
Input,
|
||||
/// Size of metadata is longer then `u16::MAX - 21`.
|
||||
Metadata,
|
||||
/// Size of info and seed together are longer then `u16::MAX - 3`.
|
||||
DeriveKeyPair,
|
||||
/// Failure to deserialize bytes
|
||||
Deserialization,
|
||||
/// Batched items are more then [`u16::MAX`] or length don't match.
|
||||
Batch,
|
||||
/// In verifiable mode, occurs when the proof failed to verify
|
||||
ProofVerification,
|
||||
/// Size of seed is longer then [`u16::MAX`].
|
||||
Seed,
|
||||
/// The protocol has failed and can't be completed.
|
||||
Protocol,
|
||||
}
|
||||
|
||||
@@ -13,14 +13,11 @@ use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint};
|
||||
use elliptic_curve::{
|
||||
AffinePoint, Field, FieldSize, Group as _, ProjectivePoint, PublicKey, Scalar, SecretKey,
|
||||
};
|
||||
use generic_array::sequence::Concat;
|
||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
|
||||
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};
|
||||
|
||||
impl<C> Group for C
|
||||
@@ -43,32 +40,26 @@ where
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
||||
fn hash_to_curve<CS: CipherSuite>(
|
||||
input: &[&[u8]],
|
||||
mode: Mode,
|
||||
dst: &[u8],
|
||||
) -> Result<Self::Elem, InternalError>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let dst =
|
||||
GenericArray::from(STR_HASH_TO_GROUP).concat(voprf::get_context_string::<CS>(mode));
|
||||
|
||||
Self::hash_from_bytes::<ExpandMsgXmd<CS::Hash>>(input, &dst)
|
||||
Self::hash_from_bytes::<ExpandMsgXmd<CS::Hash>>(input, dst)
|
||||
.map_err(|_| InternalError::Input)
|
||||
}
|
||||
|
||||
// Implements the `HashToScalar()` function
|
||||
fn hash_to_scalar<CS: CipherSuite>(
|
||||
input: &[&[u8]],
|
||||
mode: Mode,
|
||||
dst: &[u8],
|
||||
) -> Result<Self::Scalar, InternalError>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let dst =
|
||||
GenericArray::from(STR_HASH_TO_SCALAR).concat(voprf::get_context_string::<CS>(mode));
|
||||
|
||||
<Self as GroupDigest>::hash_to_scalar::<ExpandMsgXmd<CS::Hash>>(input, &dst)
|
||||
<Self as GroupDigest>::hash_to_scalar::<ExpandMsgXmd<CS::Hash>>(input, dst)
|
||||
.map_err(|_| InternalError::Input)
|
||||
}
|
||||
|
||||
|
||||
+9
-4
@@ -23,7 +23,6 @@ pub use ristretto::Ristretto255;
|
||||
use subtle::{Choice, ConstantTimeEq};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::voprf::Mode;
|
||||
use crate::{CipherSuite, InternalError, Result};
|
||||
|
||||
pub(crate) const STR_HASH_TO_SCALAR: [u8; 13] = *b"HashToScalar-";
|
||||
@@ -33,7 +32,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>;
|
||||
@@ -59,7 +59,7 @@ pub trait Group {
|
||||
/// then [`u16::MAX`].
|
||||
fn hash_to_curve<CS: CipherSuite>(
|
||||
input: &[&[u8]],
|
||||
mode: Mode,
|
||||
dst: &[u8],
|
||||
) -> Result<Self::Elem, InternalError>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
@@ -72,7 +72,7 @@ pub trait Group {
|
||||
/// then [`u16::MAX`].
|
||||
fn hash_to_scalar<CS: CipherSuite>(
|
||||
input: &[&[u8]],
|
||||
mode: Mode,
|
||||
dst: &[u8],
|
||||
) -> Result<Self::Scalar, InternalError>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
@@ -84,6 +84,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<u8, Self::ElemLen>;
|
||||
|
||||
|
||||
+6
-14
@@ -12,14 +12,12 @@ use curve25519_dalek::traits::Identity;
|
||||
use digest::core_api::BlockSizeUser;
|
||||
use digest::OutputSizeUser;
|
||||
use elliptic_curve::hash2curve::{ExpandMsg, ExpandMsgXmd, Expander};
|
||||
use generic_array::sequence::Concat;
|
||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256, U32, U64};
|
||||
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 super::Group;
|
||||
use crate::{CipherSuite, Error, InternalError, Result};
|
||||
|
||||
/// [`Group`] implementation for Ristretto255.
|
||||
@@ -52,17 +50,14 @@ impl Group for Ristretto255 {
|
||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt
|
||||
fn hash_to_curve<CS: CipherSuite>(
|
||||
input: &[&[u8]],
|
||||
mode: Mode,
|
||||
dst: &[u8],
|
||||
) -> Result<Self::Elem, InternalError>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let dst =
|
||||
GenericArray::from(STR_HASH_TO_GROUP).concat(voprf::get_context_string::<Self>(mode));
|
||||
|
||||
let mut uniform_bytes = GenericArray::<_, U64>::default();
|
||||
ExpandMsgXmd::<CS::Hash>::expand_message(input, &dst, 64)
|
||||
ExpandMsgXmd::<CS::Hash>::expand_message(input, dst, 64)
|
||||
.map_err(|_| InternalError::Input)?
|
||||
.fill_bytes(&mut uniform_bytes);
|
||||
|
||||
@@ -71,19 +66,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<CS: CipherSuite>(
|
||||
input: &[&[u8]],
|
||||
mode: Mode,
|
||||
dst: &[u8],
|
||||
) -> Result<Self::Scalar, InternalError>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let dst =
|
||||
GenericArray::from(STR_HASH_TO_SCALAR).concat(voprf::get_context_string::<Self>(mode));
|
||||
|
||||
let mut uniform_bytes = GenericArray::<_, U64>::default();
|
||||
ExpandMsgXmd::<CS::Hash>::expand_message(input, &dst, 64)
|
||||
ExpandMsgXmd::<CS::Hash>::expand_message(input, dst, 64)
|
||||
.map_err(|_| InternalError::Input)?
|
||||
.fill_bytes(&mut uniform_bytes);
|
||||
|
||||
|
||||
+93
-98
@@ -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::<CipherSuite>::new(&mut server_rng);
|
||||
//! let server = OprfServer::<CipherSuite>::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::<CipherSuite>::blind(b"input", &mut client_rng)
|
||||
//! let client_blind_result = OprfClient::<CipherSuite>::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,51 +96,46 @@
|
||||
//! # 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::<CipherSuite>::blind(
|
||||
//! # let client_blind_result = OprfClient::<CipherSuite>::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::<CipherSuite>::new(&mut server_rng);
|
||||
//! let server_evaluate_result = server
|
||||
//! .evaluate(&client_blind_result.message, None)
|
||||
//! .expect("Unable to perform server evaluate");
|
||||
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! let server_evaluate_result = server.evaluate(&client_blind_result.message);
|
||||
//! ```
|
||||
//!
|
||||
//! ### 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::<CipherSuite>::blind(
|
||||
//! # let client_blind_result = OprfClient::<CipherSuite>::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::<CipherSuite>::new(&mut server_rng);
|
||||
//! # let message = server.evaluate(
|
||||
//! # &client_blind_result.message,
|
||||
//! # None,
|
||||
//! # ).expect("Unable to perform server evaluate");
|
||||
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! # let message = server.evaluate(&client_blind_result.message);
|
||||
//! let client_finalize_result = client_blind_result
|
||||
//! .state
|
||||
//! .finalize(b"input", &message, None)
|
||||
//! .finalize(b"input", &message)
|
||||
//! .expect("Unable to perform client finalization");
|
||||
//!
|
||||
//! println!("VOPRF output: {:?}", client_finalize_result.to_vec());
|
||||
@@ -148,7 +143,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 +156,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 +166,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::<CipherSuite>::new(&mut server_rng);
|
||||
//! let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//!
|
||||
//! // To be sent to the client
|
||||
//! println!("Server public key: {:?}", server.get_public_key());
|
||||
@@ -187,9 +182,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 +194,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::<CipherSuite>::blind(b"input", &mut client_rng)
|
||||
//! let client_blind_result = VoprfClient::<CipherSuite>::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,27 +214,26 @@
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf::VerifiableClient;
|
||||
//! # use voprf::{VoprfServerEvaluateResult, VoprfClient};
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_blind_result = VerifiableClient::<CipherSuite>::blind(
|
||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::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::<CipherSuite>::new(&mut server_rng);
|
||||
//! let server_evaluate_result = server
|
||||
//! .evaluate(&mut server_rng, &client_blind_result.message, None)
|
||||
//! .expect("Unable to perform server evaluate");
|
||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! let VoprfServerEvaluateResult { message, proof } =
|
||||
//! server.evaluate(&mut server_rng, &client_blind_result.message);
|
||||
//! ```
|
||||
//!
|
||||
//! ### 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,22 +241,21 @@
|
||||
//! # 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::<CipherSuite>::blind(
|
||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::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::<CipherSuite>::new(&mut server_rng);
|
||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! # let server_evaluate_result = server.evaluate(
|
||||
//! # &mut server_rng,
|
||||
//! # &client_blind_result.message,
|
||||
//! # None,
|
||||
//! # ).expect("Unable to perform server evaluate");
|
||||
//! # );
|
||||
//! let client_finalize_result = client_blind_result
|
||||
//! .state
|
||||
//! .finalize(
|
||||
@@ -270,7 +263,6 @@
|
||||
//! &server_evaluate_result.message,
|
||||
//! &server_evaluate_result.proof,
|
||||
//! server.get_public_key(),
|
||||
//! None,
|
||||
//! )
|
||||
//! .expect("Unable to perform client finalization");
|
||||
//!
|
||||
@@ -287,7 +279,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 +291,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::<CipherSuite>::blind(b"input", &mut client_rng)
|
||||
//! let client_blind_result = VoprfClient::<CipherSuite>::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 +315,32 @@
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf::{VerifiableServerBatchEvaluatePrepareResult, VerifiableServerBatchEvaluateFinishResult, VerifiableClient};
|
||||
//! # use voprf::{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::<CipherSuite>::blind(
|
||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::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::<CipherSuite>::new(&mut server_rng);
|
||||
//! let VerifiableServerBatchEvaluatePrepareResult {
|
||||
//! prepared_evaluation_elements,
|
||||
//! t,
|
||||
//! } = server
|
||||
//! .batch_evaluate_prepare(client_messages.iter(), None)
|
||||
//! .expect("Unable to perform server batch evaluate");
|
||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! let prepared_evaluation_elements = server.batch_evaluate_prepare(client_messages.iter());
|
||||
//! 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 } = server
|
||||
//! .batch_evaluate_finish(&mut server_rng, client_messages.iter(), &prepared_elements)
|
||||
//! .expect("Unable to perform server batch evaluate");
|
||||
//! let messages: Vec<_> = messages.collect();
|
||||
//! ```
|
||||
//!
|
||||
//! If [`alloc`] is available, [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 +349,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::<CipherSuite>::blind(
|
||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::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::<CipherSuite>::new(&mut server_rng);
|
||||
//! let VerifiableServerBatchEvaluateResult { messages, proof } = server
|
||||
//! .batch_evaluate(&mut server_rng, &client_messages, None)
|
||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! let VoprfServerBatchEvaluateResult { messages, proof } = server
|
||||
//! .batch_evaluate(&mut server_rng, &client_messages)
|
||||
//! .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,33 +383,32 @@
|
||||
//! # 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::<CipherSuite>::blind(
|
||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::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::<CipherSuite>::new(&mut server_rng);
|
||||
//! # let VerifiableServerBatchEvaluateResult { messages, proof } = server
|
||||
//! # .batch_evaluate(&mut server_rng, &client_messages, None)
|
||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! # let VoprfServerBatchEvaluateResult { messages, proof } = server
|
||||
//! # .batch_evaluate(&mut server_rng, &client_messages)
|
||||
//! # .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,
|
||||
//! &proof,
|
||||
//! server.get_public_key(),
|
||||
//! None,
|
||||
//! )
|
||||
//! .expect("Unable to perform client batch finalization")
|
||||
//! .collect::<Vec<_>>();
|
||||
@@ -498,6 +485,8 @@ extern crate serde_ as serde;
|
||||
mod ciphersuite;
|
||||
mod error;
|
||||
mod group;
|
||||
mod oprf;
|
||||
mod poprf;
|
||||
mod serialization;
|
||||
mod util;
|
||||
mod voprf;
|
||||
@@ -512,17 +501,23 @@ pub use crate::error::{Error, InternalError, Result};
|
||||
pub use crate::group::Group;
|
||||
#[cfg(feature = "ristretto255")]
|
||||
pub use crate::group::Ristretto255;
|
||||
pub use crate::serialization::{
|
||||
BlindedElementLen, EvaluationElementLen, NonVerifiableClientLen, NonVerifiableServerLen,
|
||||
ProofLen, VerifiableClientLen, VerifiableServerLen,
|
||||
};
|
||||
pub use crate::oprf::{OprfClient, OprfClientBlindResult, OprfServer};
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use crate::voprf::VerifiableServerBatchEvaluateResult;
|
||||
pub use crate::voprf::{
|
||||
BlindedElement, EvaluationElement, Mode, NonVerifiableClient, NonVerifiableClientBlindResult,
|
||||
NonVerifiableServer, PreparedEvaluationElement, PreparedTscalar, Proof, VerifiableClient,
|
||||
VerifiableClientBatchFinalizeResult, VerifiableClientBlindResult, VerifiableServer,
|
||||
VerifiableServerBatchEvaluateFinishResult, VerifiableServerBatchEvaluateFinishedMessages,
|
||||
VerifiableServerBatchEvaluatePrepareResult,
|
||||
VerifiableServerBatchEvaluatePreparedEvaluationElements, VerifiableServerEvaluateResult,
|
||||
pub use crate::poprf::PoprfServerBatchEvaluateResult;
|
||||
pub use crate::poprf::{
|
||||
PoprfClient, PoprfClientBatchFinalizeResult, PoprfPreparedTweak, PoprfServer,
|
||||
PoprfServerBatchEvaluateFinishResult, PoprfServerBatchEvaluateFinishedMessages,
|
||||
PoprfServerBatchEvaluatePrepareResult, PoprfServerBatchEvaluatePreparedEvaluationElements,
|
||||
};
|
||||
pub use crate::serialization::{
|
||||
BlindedElementLen, EvaluationElementLen, OprfClientLen, OprfServerLen, PoprfClientLen,
|
||||
PoprfServerLen, ProofLen, VoprfClientLen, VoprfServerLen,
|
||||
};
|
||||
pub use crate::util::{BlindedElement, EvaluationElement, Mode, PreparedEvaluationElement, Proof};
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use crate::voprf::VoprfServerBatchEvaluateResult;
|
||||
pub use crate::voprf::{
|
||||
VoprfClient, VoprfClientBatchFinalizeResult, VoprfClientBlindResult, VoprfServer,
|
||||
VoprfServerBatchEvaluateFinishResult, VoprfServerBatchEvaluateFinishedMessages,
|
||||
VoprfServerBatchEvaluatePreparedEvaluationElements, VoprfServerEvaluateResult,
|
||||
};
|
||||
|
||||
+407
@@ -0,0 +1,407 @@
|
||||
// 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};
|
||||
|
||||
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::Scalar;
|
||||
use crate::util::{
|
||||
derive_keypair, deterministic_blind_unchecked, 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; <CS::Group as Group>::Scalar)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(crate = "serde", bound = "")
|
||||
)]
|
||||
pub struct OprfClient<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) blind: <CS::Group as Group>::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; <CS::Group as Group>::Scalar)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(crate = "serde", bound = "")
|
||||
)]
|
||||
pub struct OprfServer<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) sk: <CS::Group as Group>::Scalar,
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// API Implementations //
|
||||
// =================== //
|
||||
/////////////////////////
|
||||
|
||||
impl<CS: CipherSuite> OprfClient<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::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<R: RngCore + CryptoRng>(
|
||||
input: &[u8],
|
||||
blinding_factor_rng: &mut R,
|
||||
) -> Result<OprfClientBlindResult<CS>> {
|
||||
let blind = CS::Group::random_scalar(blinding_factor_rng);
|
||||
Self::deterministic_blind_unchecked_inner(input, blind)
|
||||
}
|
||||
|
||||
/// 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`].
|
||||
#[cfg(any(feature = "danger", test))]
|
||||
pub fn deterministic_blind_unchecked(
|
||||
input: &[u8],
|
||||
blind: <CS::Group as Group>::Scalar,
|
||||
) -> Result<OprfClientBlindResult<CS>> {
|
||||
Self::deterministic_blind_unchecked_inner(input, blind)
|
||||
}
|
||||
|
||||
/// Can only fail with [`Error::Input`].
|
||||
fn deterministic_blind_unchecked_inner(
|
||||
input: &[u8],
|
||||
blind: <CS::Group as Group>::Scalar,
|
||||
) -> Result<OprfClientBlindResult<CS>> {
|
||||
let blinded_element = deterministic_blind_unchecked::<CS>(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<CS>,
|
||||
) -> Result<Output<CS::Hash>> {
|
||||
let unblinded_element = evaluation_element.0 * &CS::Group::invert_scalar(self.blind);
|
||||
let mut outputs =
|
||||
finalize_after_unblind::<CS, _, _>(iter::once((input, unblinded_element)), &[]);
|
||||
outputs.next().unwrap()
|
||||
}
|
||||
|
||||
/// Only used for test functions
|
||||
#[cfg(test)]
|
||||
pub fn from_blind(blind: <CS::Group as Group>::Scalar) -> Self {
|
||||
Self { blind }
|
||||
}
|
||||
|
||||
/// Exposes the blind group element
|
||||
#[cfg(feature = "danger")]
|
||||
pub fn get_blind(&self) -> <CS::Group as Group>::Scalar {
|
||||
self.blind
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> OprfServer<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Produces a new instance of a [OprfServer] using a supplied RNG
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self> {
|
||||
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
|
||||
rng.fill_bytes(&mut seed);
|
||||
Self::new_from_seed(&seed, &[])
|
||||
}
|
||||
|
||||
/// 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<Self> {
|
||||
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::DeriveKeyPair`] if the `input` and `seed` together are longer
|
||||
/// then `u16::MAX - 3`.
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
pub fn new_from_seed(seed: &[u8], info: &[u8]) -> Result<Self> {
|
||||
let (sk, _) = derive_keypair::<CS>(seed, info, Mode::Oprf)?;
|
||||
Ok(Self { sk })
|
||||
}
|
||||
|
||||
// Only used for tests
|
||||
#[cfg(test)]
|
||||
pub fn get_private_key(&self) -> <CS::Group as Group>::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.
|
||||
pub fn evaluate(&self, blinded_element: &BlindedElement<CS>) -> EvaluationElement<CS> {
|
||||
EvaluationElement(blinded_element.0 * &self.sk)
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// Convenience Structs //
|
||||
//==================== //
|
||||
/////////////////////////
|
||||
|
||||
/// Contains the fields that are returned by a non-verifiable client blind
|
||||
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||
pub struct OprfClientBlindResult<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// The state to be persisted on the client
|
||||
pub state: OprfClient<CS>,
|
||||
/// The message to send to the server
|
||||
pub message: BlindedElement<CS>,
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// Inner functions //
|
||||
// =============== //
|
||||
/////////////////////
|
||||
|
||||
type FinalizeAfterUnblindResult<'a, C, I, IE> = Map<
|
||||
IE,
|
||||
fn((I, <<C as CipherSuite>::Group as Group>::Elem)) -> Result<Output<<C as CipherSuite>::Hash>>,
|
||||
>;
|
||||
|
||||
/// Returned values can only fail with [`Error::Input`].
|
||||
fn finalize_after_unblind<
|
||||
'a,
|
||||
CS: CipherSuite,
|
||||
I: AsRef<[u8]>,
|
||||
IE: 'a + Iterator<Item = (I, <CS::Group as Group>::Elem)>,
|
||||
>(
|
||||
inputs_and_unblinded_elements: IE,
|
||||
_unused: &'a [u8],
|
||||
) -> FinalizeAfterUnblindResult<CS, I, IE>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
inputs_and_unblinded_elements.map(|(input, unblinded_element)| {
|
||||
let elem_len = <CS::Group as Group>::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(&STR_FINALIZE)
|
||||
.finalize())
|
||||
})
|
||||
}
|
||||
|
||||
///////////
|
||||
// Tests //
|
||||
// ===== //
|
||||
///////////
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::ptr;
|
||||
|
||||
use generic_array::sequence::Concat;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use super::*;
|
||||
use crate::group::STR_HASH_TO_GROUP;
|
||||
use crate::util::create_context_string;
|
||||
use crate::Group;
|
||||
|
||||
fn prf<CS: CipherSuite>(
|
||||
input: &[u8],
|
||||
key: <CS::Group as Group>::Scalar,
|
||||
info: &[u8],
|
||||
mode: Mode,
|
||||
) -> Output<CS::Hash>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
|
||||
let point = CS::Group::hash_to_curve::<CS>(&[input], &dst).unwrap();
|
||||
|
||||
let res = point * &key;
|
||||
|
||||
finalize_after_unblind::<CS, _, _>(iter::once((input, res)), info)
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn base_retrieval<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let input = b"input";
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
let server = OprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let message = server.evaluate(&client_blind_result.message);
|
||||
let client_finalize_result = client_blind_result.state.finalize(input, &message).unwrap();
|
||||
let res2 = prf::<CS>(input, server.get_private_key(), &[], Mode::Oprf);
|
||||
assert_eq!(client_finalize_result, res2);
|
||||
}
|
||||
|
||||
fn base_inversion_unsalted<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let mut rng = OsRng;
|
||||
let mut input = [0u8; 64];
|
||||
rng.fill_bytes(&mut input);
|
||||
let client_blind_result = OprfClient::<CS>::blind(&input, &mut rng).unwrap();
|
||||
let client_finalize_result = client_blind_result
|
||||
.state
|
||||
.finalize(&input, &EvaluationElement(client_blind_result.message.0))
|
||||
.unwrap();
|
||||
|
||||
let dst =
|
||||
GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(Mode::Oprf));
|
||||
let point = CS::Group::hash_to_curve::<CS>(&[&input], &dst).unwrap();
|
||||
let res2 = finalize_after_unblind::<CS, _, _>(iter::once((input.as_ref(), point)), &[])
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(client_finalize_result, res2);
|
||||
}
|
||||
|
||||
fn zeroize_oprf_client<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let input = b"input";
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
|
||||
let 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<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let input = b"input";
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
let server = OprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let mut message = server.evaluate(&client_blind_result.message);
|
||||
|
||||
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::<Ristretto255>();
|
||||
base_inversion_unsalted::<Ristretto255>();
|
||||
|
||||
zeroize_oprf_client::<Ristretto255>();
|
||||
zeroize_oprf_server::<Ristretto255>();
|
||||
}
|
||||
|
||||
base_retrieval::<NistP256>();
|
||||
base_inversion_unsalted::<NistP256>();
|
||||
|
||||
zeroize_oprf_client::<NistP256>();
|
||||
zeroize_oprf_server::<NistP256>();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+933
@@ -0,0 +1,933 @@
|
||||
// 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 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, U256};
|
||||
use generic_array::GenericArray;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use crate::group::STR_HASH_TO_SCALAR;
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::serialization::serde::{Element, Scalar};
|
||||
use crate::util::{
|
||||
create_context_string, derive_keypair, deterministic_blind_unchecked, generate_proof, i2osp_2,
|
||||
verify_proof, BlindedElement, EvaluationElement, Mode, PreparedEvaluationElement, Proof,
|
||||
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; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(crate = "serde", bound = "")
|
||||
)]
|
||||
pub struct PoprfClient<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) blind: <CS::Group as Group>::Scalar,
|
||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||
pub(crate) blinded_element: <CS::Group as Group>::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; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(crate = "serde", bound = "")
|
||||
)]
|
||||
pub struct PoprfServer<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) sk: <CS::Group as Group>::Scalar,
|
||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||
pub(crate) pk: <CS::Group as Group>::Elem,
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// API Implementations //
|
||||
// =================== //
|
||||
/////////////////////////
|
||||
|
||||
impl<CS: CipherSuite> PoprfClient<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::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<R: RngCore + CryptoRng>(
|
||||
blinding_factor_rng: &mut R,
|
||||
input: &[u8],
|
||||
) -> Result<PoprfClientBlindResult<CS>> {
|
||||
let blind = CS::Group::random_scalar(blinding_factor_rng);
|
||||
Self::deterministic_blind_unchecked_inner(input, blind)
|
||||
}
|
||||
|
||||
/// 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`].
|
||||
#[cfg(any(feature = "danger", test))]
|
||||
pub fn deterministic_blind_unchecked(
|
||||
input: &[u8],
|
||||
blind: <CS::Group as Group>::Scalar,
|
||||
) -> Result<PoprfClientBlindResult<CS>> {
|
||||
Self::deterministic_blind_unchecked_inner(input, blind)
|
||||
}
|
||||
|
||||
/// Can only fail with [`Error::Input`].
|
||||
fn deterministic_blind_unchecked_inner(
|
||||
input: &[u8],
|
||||
blind: <CS::Group as Group>::Scalar,
|
||||
) -> Result<PoprfClientBlindResult<CS>> {
|
||||
let blinded_element = deterministic_blind_unchecked::<CS>(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::Info`] if the `info` is longer than `u16::MAX`.
|
||||
/// - [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`].
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
/// - [`Error::ProofVerification`] if the `proof` failed to verify.
|
||||
pub fn finalize(
|
||||
&self,
|
||||
input: &[u8],
|
||||
evaluation_element: &EvaluationElement<CS>,
|
||||
proof: &Proof<CS>,
|
||||
pk: <CS::Group as Group>::Elem,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<Output<CS::Hash>> {
|
||||
let clients = core::array::from_ref(self);
|
||||
let messages = core::array::from_ref(evaluation_element);
|
||||
|
||||
let mut batch_result =
|
||||
Self::batch_finalize(iter::once(input), clients, messages, proof, pk, info)?;
|
||||
batch_result.next().unwrap()
|
||||
}
|
||||
|
||||
/// Allows for batching of the finalization of multiple [PoprfClient]
|
||||
/// and [EvaluationElement] pairs
|
||||
///
|
||||
/// # Errors
|
||||
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
/// - [`Error::Batch`] if the number of `inputs`, `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<'a, II: 'a + Iterator<Item = &'a [u8]> + ExactSizeIterator, IC, IM>(
|
||||
inputs: II,
|
||||
clients: &'a IC,
|
||||
messages: &'a IM,
|
||||
proof: &Proof<CS>,
|
||||
pk: <CS::Group as Group>::Elem,
|
||||
info: Option<&'a [u8]>,
|
||||
) -> Result<PoprfClientBatchFinalizeResult<'a, CS, II, IC, IM>>
|
||||
where
|
||||
CS: 'a,
|
||||
&'a IC: 'a + IntoIterator<Item = &'a PoprfClient<CS>>,
|
||||
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
|
||||
<&'a IM as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let unblinded_elements = poprf_unblind(clients, messages, pk, proof, info)?;
|
||||
|
||||
finalize_after_unblind::<'a, CS, _, _>(unblinded_elements, inputs, info)
|
||||
}
|
||||
|
||||
/// Only used for test functions
|
||||
#[cfg(test)]
|
||||
pub fn get_blind(&self) -> <CS::Group as Group>::Scalar {
|
||||
self.blind
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> PoprfServer<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Produces a new instance of a [PoprfServer] using a supplied RNG
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self> {
|
||||
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
|
||||
rng.fill_bytes(&mut seed);
|
||||
|
||||
Self::new_from_seed(&seed, &[])
|
||||
}
|
||||
|
||||
/// 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<Self> {
|
||||
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::DeriveKeyPair`] if the `input` and `seed` together are longer
|
||||
/// then `u16::MAX - 3`.
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
pub fn new_from_seed(seed: &[u8], info: &[u8]) -> Result<Self> {
|
||||
let (sk, pk) = derive_keypair::<CS>(seed, info, Mode::Poprf)?;
|
||||
Ok(Self { sk, pk })
|
||||
}
|
||||
|
||||
// Only used for tests
|
||||
#[cfg(test)]
|
||||
pub fn get_private_key(&self) -> <CS::Group as Group>::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<R: RngCore + CryptoRng>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
blinded_element: &BlindedElement<CS>,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<PoprfServerEvaluateResult<CS>> {
|
||||
let PoprfServerBatchEvaluatePrepareResult {
|
||||
mut prepared_evaluation_elements,
|
||||
prepared_tweak,
|
||||
} = self.batch_evaluate_prepare(iter::once(blinded_element), info)?;
|
||||
|
||||
let prepared_evaluation_element = prepared_evaluation_elements.next().unwrap();
|
||||
let prepared_evaluation_elements = core::array::from_ref(&prepared_evaluation_element);
|
||||
|
||||
let PoprfServerBatchEvaluateFinishResult {
|
||||
mut messages,
|
||||
proof,
|
||||
} = Self::batch_evaluate_finish(
|
||||
rng,
|
||||
iter::once(blinded_element),
|
||||
prepared_evaluation_elements,
|
||||
&prepared_tweak,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Ok(PoprfServerEvaluateResult {
|
||||
message: messages.next().unwrap(),
|
||||
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.
|
||||
#[cfg(feature = "alloc")]
|
||||
pub fn batch_evaluate<'a, R: RngCore + CryptoRng, IE>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
blinded_elements: &'a IE,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<PoprfServerBatchEvaluateResult<CS>>
|
||||
where
|
||||
CS: 'a,
|
||||
&'a IE: 'a + IntoIterator<Item = &'a BlindedElement<CS>>,
|
||||
<&'a IE as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let PoprfServerBatchEvaluatePrepareResult {
|
||||
prepared_evaluation_elements,
|
||||
prepared_tweak,
|
||||
} = self.batch_evaluate_prepare(blinded_elements.into_iter(), info)?;
|
||||
|
||||
let prepared_evaluation_elements: Vec<_> = prepared_evaluation_elements.collect();
|
||||
|
||||
// This can't fail because we know the size of the inputs.
|
||||
let PoprfServerBatchEvaluateFinishResult { messages, proof } =
|
||||
Self::batch_evaluate_finish::<_, _, Vec<_>>(
|
||||
rng,
|
||||
blinded_elements.into_iter(),
|
||||
&prepared_evaluation_elements,
|
||||
&prepared_tweak,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let messages: Vec<_> = messages.collect();
|
||||
|
||||
Ok(PoprfServerBatchEvaluateResult { messages, 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::Info`] if the `info` is longer than `u16::MAX`.
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
pub fn batch_evaluate_prepare<'a, I: Iterator<Item = &'a BlindedElement<CS>>>(
|
||||
&self,
|
||||
blinded_elements: I,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<PoprfServerBatchEvaluatePrepareResult<CS, I>>
|
||||
where
|
||||
CS: 'a,
|
||||
{
|
||||
let tweak = compute_tweak::<CS>(self.sk, info)?;
|
||||
|
||||
Ok(PoprfServerBatchEvaluatePrepareResult {
|
||||
prepared_evaluation_elements: blinded_elements.zip(iter::repeat(tweak)).map(
|
||||
|(blinded_element, tweak)| {
|
||||
PreparedEvaluationElement(EvaluationElement(
|
||||
blinded_element.0 * &CS::Group::invert_scalar(tweak),
|
||||
))
|
||||
},
|
||||
),
|
||||
prepared_tweak: PoprfPreparedTweak(tweak),
|
||||
})
|
||||
}
|
||||
|
||||
/// See [`batch_evaluate_prepare`](Self::batch_evaluate_prepare) for more
|
||||
/// details.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Batch`] if the number of `blinded_elements` and
|
||||
/// `prepared_evaluation_elements` don't match or is longer then
|
||||
/// [`u16::MAX`]
|
||||
pub fn batch_evaluate_finish<
|
||||
'a,
|
||||
'b,
|
||||
R: RngCore + CryptoRng,
|
||||
IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator,
|
||||
IE,
|
||||
>(
|
||||
rng: &mut R,
|
||||
blinded_elements: IB,
|
||||
prepared_evaluation_elements: &'b IE,
|
||||
prepared_tweak: &PoprfPreparedTweak<CS>,
|
||||
) -> Result<PoprfServerBatchEvaluateFinishResult<'b, CS, IE>>
|
||||
where
|
||||
CS: 'a,
|
||||
&'b IE: IntoIterator<Item = &'b PreparedEvaluationElement<CS>>,
|
||||
<&'b IE as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let g = CS::Group::base_elem();
|
||||
let tweak = prepared_tweak.0;
|
||||
let tweaked_key = g * &tweak;
|
||||
|
||||
let proof = generate_proof(
|
||||
rng,
|
||||
tweak,
|
||||
g,
|
||||
tweaked_key,
|
||||
prepared_evaluation_elements
|
||||
.into_iter()
|
||||
.map(|element| element.0 .0),
|
||||
blinded_elements.map(|element| element.0),
|
||||
Mode::Poprf,
|
||||
)?;
|
||||
|
||||
let messages = prepared_evaluation_elements.into_iter().map(<fn(
|
||||
&PreparedEvaluationElement<CS>,
|
||||
) -> _>::from(
|
||||
|element| EvaluationElement(element.0 .0),
|
||||
));
|
||||
|
||||
Ok(PoprfServerBatchEvaluateFinishResult { messages, proof })
|
||||
}
|
||||
|
||||
/// Retrieves the server's public key
|
||||
pub fn get_public_key(&self) -> <CS::Group as Group>::Elem {
|
||||
self.pk
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> BlindedElement<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// 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!
|
||||
#[cfg(feature = "danger")]
|
||||
pub fn from_value_unchecked(value: <CS::Group as Group>::Elem) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
|
||||
/// Exposes the internal value
|
||||
#[cfg(feature = "danger")]
|
||||
pub fn value(&self) -> <CS::Group as Group>::Elem {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> EvaluationElement<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// 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!
|
||||
#[cfg(feature = "danger")]
|
||||
pub fn from_value_unchecked(value: <CS::Group as Group>::Elem) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
|
||||
/// Exposes the internal value
|
||||
#[cfg(feature = "danger")]
|
||||
pub fn value(&self) -> <CS::Group as Group>::Elem {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// Convenience Structs //
|
||||
//==================== //
|
||||
/////////////////////////
|
||||
|
||||
/// Contains the fields that are returned by a verifiable client blind
|
||||
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||
pub struct PoprfClientBlindResult<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// The state to be persisted on the client
|
||||
pub state: PoprfClient<CS>,
|
||||
/// The message to send to the server
|
||||
pub message: BlindedElement<CS>,
|
||||
}
|
||||
|
||||
/// Concrete return type for [`PoprfClient::batch_finalize`].
|
||||
pub type PoprfClientBatchFinalizeResult<'a, CS, II, IC, IM> =
|
||||
FinalizeAfterUnblindResult<'a, CS, PoprfUnblindResult<'a, CS, IC, IM>, II>;
|
||||
|
||||
/// Contains the fields that are returned by a verifiable server evaluate
|
||||
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||
pub struct PoprfServerEvaluateResult<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// The message to send to the client
|
||||
pub message: EvaluationElement<CS>,
|
||||
/// The proof for the client to verify
|
||||
pub proof: Proof<CS>,
|
||||
}
|
||||
|
||||
/// Contains the fields that are returned by a verifiable server batch evaluate
|
||||
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||
#[cfg(feature = "alloc")]
|
||||
pub struct PoprfServerBatchEvaluateResult<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// The messages to send to the client
|
||||
pub messages: Vec<EvaluationElement<CS>>,
|
||||
/// The proof for the client to verify
|
||||
pub proof: Proof<CS>,
|
||||
}
|
||||
|
||||
/// Concrete type of [`EvaluationElement`]s in
|
||||
/// [`PoprfServerBatchEvaluatePrepareResult`].
|
||||
pub type PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I> = Map<
|
||||
Zip<I, Repeat<<<CS as CipherSuite>::Group as Group>::Scalar>>,
|
||||
fn(
|
||||
(
|
||||
&BlindedElement<CS>,
|
||||
<<CS as CipherSuite>::Group as Group>::Scalar,
|
||||
),
|
||||
) -> PreparedEvaluationElement<CS>,
|
||||
>;
|
||||
|
||||
/// Prepared tweak by a partially verifiable server batch evaluate prepare.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(crate = "serde", bound = "")
|
||||
)]
|
||||
pub struct PoprfPreparedTweak<CS: CipherSuite>(
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
<CS::Group as Group>::Scalar,
|
||||
)
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>;
|
||||
|
||||
/// Contains the fields that are returned by a partially verifiable server batch
|
||||
/// evaluate prepare
|
||||
#[derive_where(Debug; I, <CS::Group as Group>::Scalar)]
|
||||
pub struct PoprfServerBatchEvaluatePrepareResult<CS: CipherSuite, I>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Prepared [`EvaluationElement`].
|
||||
pub prepared_evaluation_elements: PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I>,
|
||||
/// Prepared tweak.
|
||||
pub prepared_tweak: PoprfPreparedTweak<CS>,
|
||||
}
|
||||
|
||||
/// Concrete type of [`EvaluationElement`]s in
|
||||
/// [`PoprfServerBatchEvaluateFinishResult`].
|
||||
pub type PoprfServerBatchEvaluateFinishedMessages<'a, CS, I> = Map<
|
||||
<&'a I as IntoIterator>::IntoIter,
|
||||
fn(&PreparedEvaluationElement<CS>) -> EvaluationElement<CS>,
|
||||
>;
|
||||
|
||||
/// Contains the fields that are returned by a verifiable server batch evaluate
|
||||
/// finish.
|
||||
#[derive_where(Debug; <&'a I as IntoIterator>::IntoIter, <CS::Group as Group>::Scalar)]
|
||||
pub struct PoprfServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
&'a I: IntoIterator<Item = &'a PreparedEvaluationElement<CS>>,
|
||||
{
|
||||
/// The [`EvaluationElement`]s to send to the client
|
||||
pub messages: PoprfServerBatchEvaluateFinishedMessages<'a, CS, I>,
|
||||
/// The proof for the client to verify
|
||||
pub proof: Proof<CS>,
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// Inner functions //
|
||||
// =============== //
|
||||
/////////////////////
|
||||
|
||||
/// Inner function for POPRF blind. Computes the tweaked key from the server
|
||||
/// public key and info.
|
||||
///
|
||||
/// Can only fail with [`Error::Info`] or [`Error::Protocol`]
|
||||
fn compute_tweaked_key<CS: CipherSuite>(
|
||||
pk: <CS::Group as Group>::Elem,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<<CS::Group as Group>::Elem>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::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::Info)?;
|
||||
let framed_info = [STR_INFO.as_slice(), &info_len, info];
|
||||
|
||||
let dst =
|
||||
GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(Mode::Poprf));
|
||||
// This can't fail, the size of the `input` is known.
|
||||
let m = CS::Group::hash_to_scalar::<CS>(&framed_info, &dst).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::Protocol),
|
||||
false => Ok(tweaked_key),
|
||||
}
|
||||
}
|
||||
|
||||
/// Inner function for POPRF evaluate. Computes the tweak from the server
|
||||
/// private key and info.
|
||||
///
|
||||
/// Can only fail with [`Error::Info`] and [`Error::Protocol`].
|
||||
fn compute_tweak<CS: CipherSuite>(
|
||||
sk: <CS::Group as Group>::Scalar,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<<CS::Group as Group>::Scalar>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::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::Info)?;
|
||||
let framed_info = [STR_INFO.as_slice(), &info_len, info];
|
||||
|
||||
let dst =
|
||||
GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(Mode::Poprf));
|
||||
// This can't fail, the size of the `input` is known.
|
||||
let m = CS::Group::hash_to_scalar::<CS>(&framed_info, &dst).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::Protocol),
|
||||
false => Ok(t),
|
||||
}
|
||||
}
|
||||
|
||||
type PoprfUnblindResult<'a, CS, IC, IM> = Map<
|
||||
Zip<
|
||||
Map<
|
||||
<&'a IC as IntoIterator>::IntoIter,
|
||||
fn(&PoprfClient<CS>) -> <<CS as CipherSuite>::Group as Group>::Scalar,
|
||||
>,
|
||||
<&'a IM as IntoIterator>::IntoIter,
|
||||
>,
|
||||
fn(
|
||||
(
|
||||
<<CS as CipherSuite>::Group as Group>::Scalar,
|
||||
&'a EvaluationElement<CS>,
|
||||
),
|
||||
) -> <<CS as CipherSuite>::Group as Group>::Elem,
|
||||
>;
|
||||
|
||||
/// Can only fail with [`Error::Info`], [`Error::Protocol`], [`Error::Batch] or
|
||||
/// [`Error::ProofVerification`].
|
||||
fn poprf_unblind<'a, CS: 'a + CipherSuite, IC, IM>(
|
||||
clients: &'a IC,
|
||||
messages: &'a IM,
|
||||
pk: <CS::Group as Group>::Elem,
|
||||
proof: &Proof<CS>,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<PoprfUnblindResult<'a, CS, IC, IM>>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
&'a IC: 'a + IntoIterator<Item = &'a PoprfClient<CS>>,
|
||||
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
|
||||
<&'a IM as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let info = info.unwrap_or_default();
|
||||
let tweaked_key = compute_tweaked_key::<CS>(pk, Some(info))?;
|
||||
|
||||
let g = CS::Group::base_elem();
|
||||
|
||||
let blinds = clients
|
||||
.into_iter()
|
||||
// Convert to `fn` pointer to make a return type possible.
|
||||
.map(<fn(&PoprfClient<CS>) -> _>::from(|x| x.blind));
|
||||
let evaluation_elements = messages.into_iter().map(|element| element.0);
|
||||
let blinded_elements = clients.into_iter().map(|client| client.blinded_element);
|
||||
|
||||
verify_proof(
|
||||
g,
|
||||
tweaked_key,
|
||||
evaluation_elements,
|
||||
blinded_elements,
|
||||
proof,
|
||||
Mode::Poprf,
|
||||
)?;
|
||||
|
||||
Ok(blinds
|
||||
.zip(messages.into_iter())
|
||||
.map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
|
||||
}
|
||||
|
||||
type FinalizeAfterUnblindResult<'a, CS, IE, II> = Map<
|
||||
Zip<Zip<IE, II>, Repeat<&'a [u8]>>,
|
||||
fn(
|
||||
((<<CS as CipherSuite>::Group as Group>::Elem, &[u8]), &[u8]),
|
||||
) -> Result<GenericArray<u8, <<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize>>,
|
||||
>;
|
||||
|
||||
/// Can only fail with [`Error::Batch`] and returned values can only fail with
|
||||
/// [`Error::Info`] or [`Error::Input`] individually.
|
||||
fn finalize_after_unblind<
|
||||
'a,
|
||||
CS: CipherSuite,
|
||||
IE: 'a + Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
||||
II: 'a + Iterator<Item = &'a [u8]> + ExactSizeIterator,
|
||||
>(
|
||||
unblinded_elements: IE,
|
||||
inputs: II,
|
||||
info: Option<&'a [u8]>,
|
||||
) -> Result<FinalizeAfterUnblindResult<'a, CS, IE, II>>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
if unblinded_elements.len() != inputs.len() {
|
||||
return Err(Error::Batch);
|
||||
}
|
||||
|
||||
let info = info.unwrap_or_default();
|
||||
|
||||
Ok(unblinded_elements.zip(inputs).zip(iter::repeat(info)).map(
|
||||
|((unblinded_element, input), info)| {
|
||||
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
|
||||
|
||||
// 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::Info)?)
|
||||
.chain_update(info.as_ref())
|
||||
.chain_update(elem_len)
|
||||
.chain_update(CS::Group::serialize_elem(unblinded_element))
|
||||
.chain_update(STR_FINALIZE)
|
||||
.finalize();
|
||||
|
||||
Ok(output)
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
///////////
|
||||
// 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::STR_HASH_TO_GROUP;
|
||||
use crate::Group;
|
||||
|
||||
fn prf<CS: CipherSuite>(
|
||||
input: &[u8],
|
||||
key: <CS::Group as Group>::Scalar,
|
||||
info: &[u8],
|
||||
mode: Mode,
|
||||
) -> Output<CS::Hash>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let t = compute_tweak::<CS>(key, Some(info)).unwrap();
|
||||
|
||||
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
|
||||
let point = CS::Group::hash_to_curve::<CS>(&[input], &dst).unwrap();
|
||||
|
||||
// evaluatedElement = G.ScalarInverse(t) * blindedElement
|
||||
let res = point * &CS::Group::invert_scalar(t);
|
||||
|
||||
finalize_after_unblind::<CS, _, _>(iter::once(res), iter::once(input), Some(info))
|
||||
.unwrap()
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn verifiable_retrieval<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let input = b"input";
|
||||
let info = b"info";
|
||||
let mut rng = OsRng;
|
||||
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let client_blind_result = PoprfClient::<CS>::blind(&mut rng, input).unwrap();
|
||||
let 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::<CS>(input, server.get_private_key(), info, Mode::Poprf);
|
||||
assert_eq!(client_finalize_result, res2);
|
||||
}
|
||||
|
||||
fn verifiable_bad_public_key<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let input = b"input";
|
||||
let info = b"info";
|
||||
let mut rng = OsRng;
|
||||
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let client_blind_result = PoprfClient::<CS>::blind(&mut rng, input).unwrap();
|
||||
let server_result = server
|
||||
.evaluate(&mut rng, &client_blind_result.message, Some(info))
|
||||
.unwrap();
|
||||
let wrong_pk = {
|
||||
let dst = GenericArray::from(STR_HASH_TO_GROUP)
|
||||
.concat(create_context_string::<CS>(Mode::Oprf));
|
||||
// Choose a group element that is unlikely to be the right public key
|
||||
CS::Group::hash_to_curve::<CS>(&[b"msg"], &dst).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<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
|
||||
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ElemLen>: ArrayLength<u8>,
|
||||
{
|
||||
let input = b"input";
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = PoprfClient::<CS>::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<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
|
||||
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ElemLen>: ArrayLength<u8>,
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ScalarLen>,
|
||||
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ScalarLen>: ArrayLength<u8>,
|
||||
{
|
||||
let input = b"input";
|
||||
let info = b"info";
|
||||
let mut rng = OsRng;
|
||||
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let client_blind_result = PoprfClient::<CS>::blind(&mut rng, input).unwrap();
|
||||
let 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::<Ristretto255>();
|
||||
verifiable_bad_public_key::<Ristretto255>();
|
||||
|
||||
zeroize_verifiable_client::<Ristretto255>();
|
||||
zeroize_verifiable_server::<Ristretto255>();
|
||||
}
|
||||
|
||||
verifiable_retrieval::<NistP256>();
|
||||
verifiable_bad_public_key::<NistP256>();
|
||||
|
||||
zeroize_verifiable_client::<NistP256>();
|
||||
zeroize_verifiable_server::<NistP256>();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+92
-20
@@ -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<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
|
||||
/// Length of [`OprfClient`] in bytes for serialization.
|
||||
pub type OprfClientLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
|
||||
|
||||
impl<CS: CipherSuite> NonVerifiableClient<CS>
|
||||
impl<CS: CipherSuite> OprfClient<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> GenericArray<u8, NonVerifiableClientLen<CS>> {
|
||||
pub fn serialize(&self) -> GenericArray<u8, OprfClientLen<CS>> {
|
||||
CS::Group::serialize_scalar(self.blind)
|
||||
}
|
||||
|
||||
@@ -52,22 +52,22 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Length of [`VerifiableClient`] in bytes for serialization.
|
||||
pub type VerifiableClientLen<CS> = Sum<
|
||||
/// Length of [`VoprfClient`] in bytes for serialization.
|
||||
pub type VoprfClientLen<CS> = Sum<
|
||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
||||
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
||||
>;
|
||||
|
||||
impl<CS: CipherSuite> VerifiableClient<CS>
|
||||
impl<CS: CipherSuite> VoprfClient<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> GenericArray<u8, VerifiableClientLen<CS>>
|
||||
pub fn serialize(&self) -> GenericArray<u8, VoprfClientLen<CS>>
|
||||
where
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
|
||||
VerifiableClientLen<CS>: ArrayLength<u8>,
|
||||
VoprfClientLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
<CS::Group as Group>::serialize_scalar(self.blind)
|
||||
.concat(<CS::Group as Group>::serialize_elem(self.blinded_element))
|
||||
@@ -90,16 +90,54 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Length of [`NonVerifiableServer`] in bytes for serialization.
|
||||
pub type NonVerifiableServerLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
|
||||
/// Length of [`PoprfClient`] in bytes for serialization.
|
||||
pub type PoprfClientLen<CS> = Sum<
|
||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
||||
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
||||
>;
|
||||
|
||||
impl<CS: CipherSuite> NonVerifiableServer<CS>
|
||||
impl<CS: CipherSuite> PoprfClient<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> GenericArray<u8, NonVerifiableServerLen<CS>> {
|
||||
pub fn serialize(&self) -> GenericArray<u8, PoprfClientLen<CS>>
|
||||
where
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
|
||||
PoprfClientLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
<CS::Group as Group>::serialize_scalar(self.blind)
|
||||
.concat(<CS::Group as Group>::serialize_elem(self.blinded_element))
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self> {
|
||||
let mut input = input.iter().copied();
|
||||
|
||||
let blind = deserialize_scalar::<CS::Group, _>(&mut input)?;
|
||||
let blinded_element = deserialize_elem::<CS::Group, _>(&mut input)?;
|
||||
|
||||
Ok(Self {
|
||||
blind,
|
||||
blinded_element,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Length of [`OprfServer`] in bytes for serialization.
|
||||
pub type OprfServerLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
|
||||
|
||||
impl<CS: CipherSuite> OprfServer<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> GenericArray<u8, OprfServerLen<CS>> {
|
||||
CS::Group::serialize_scalar(self.sk)
|
||||
}
|
||||
|
||||
@@ -116,22 +154,56 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Length of [`VerifiableServer`] in bytes for serialization.
|
||||
pub type VerifiableServerLen<CS> = Sum<
|
||||
/// Length of [`VoprfServer`] in bytes for serialization.
|
||||
pub type VoprfServerLen<CS> = Sum<
|
||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
||||
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
||||
>;
|
||||
|
||||
impl<CS: CipherSuite> VerifiableServer<CS>
|
||||
impl<CS: CipherSuite> VoprfServer<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> GenericArray<u8, VerifiableServerLen<CS>>
|
||||
pub fn serialize(&self) -> GenericArray<u8, VoprfServerLen<CS>>
|
||||
where
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
|
||||
VerifiableServerLen<CS>: ArrayLength<u8>,
|
||||
VoprfServerLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
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<Self> {
|
||||
let mut input = input.iter().copied();
|
||||
|
||||
let sk = deserialize_scalar::<CS::Group, _>(&mut input)?;
|
||||
let pk = deserialize_elem::<CS::Group, _>(&mut input)?;
|
||||
|
||||
Ok(Self { sk, pk })
|
||||
}
|
||||
}
|
||||
|
||||
/// Length of [`PoprfServer`] in bytes for serialization.
|
||||
pub type PoprfServerLen<CS> = Sum<
|
||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
||||
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
||||
>;
|
||||
|
||||
impl<CS: CipherSuite> PoprfServer<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> GenericArray<u8, PoprfServerLen<CS>>
|
||||
where
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
|
||||
PoprfServerLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
CS::Group::serialize_scalar(self.sk).concat(CS::Group::serialize_elem(self.pk))
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -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;
|
||||
|
||||
@@ -0,0 +1,476 @@
|
||||
// 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, PoprfServerBatchEvaluateFinishResult, PoprfServerBatchEvaluatePrepareResult,
|
||||
Proof, Result, VoprfClient, VoprfServer, VoprfServerBatchEvaluateFinishResult,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct VOPRFTestVectorParameters {
|
||||
seed: Vec<u8>,
|
||||
sksm: Vec<u8>,
|
||||
pksm: Vec<u8>,
|
||||
input: Vec<Vec<u8>>,
|
||||
info: Vec<u8>,
|
||||
key_info: Vec<u8>,
|
||||
blind: Vec<Vec<u8>>,
|
||||
blinded_element: Vec<Vec<u8>>,
|
||||
evaluation_element: Vec<Vec<u8>>,
|
||||
proof: Vec<u8>,
|
||||
proof_random_scalar: Vec<u8>,
|
||||
output: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
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<u8> {
|
||||
values[key]
|
||||
.as_str()
|
||||
.and_then(|s| hex::decode(&s.to_string()).ok())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn decode_vec(values: &JsonValue, key: &str) -> Vec<Vec<u8>> {
|
||||
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::<Vec<VOPRFTestVectorParameters>>()
|
||||
};
|
||||
}
|
||||
|
||||
#[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::<Ristretto255>(&ristretto_oprf_tvs)?;
|
||||
test_oprf_blind::<Ristretto255>(&ristretto_oprf_tvs)?;
|
||||
test_oprf_evaluate::<Ristretto255>(&ristretto_oprf_tvs)?;
|
||||
test_oprf_finalize::<Ristretto255>(&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::<Ristretto255>(&ristretto_voprf_tvs)?;
|
||||
test_voprf_blind::<Ristretto255>(&ristretto_voprf_tvs)?;
|
||||
test_voprf_evaluate::<Ristretto255>(&ristretto_voprf_tvs)?;
|
||||
test_voprf_finalize::<Ristretto255>(&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::<Ristretto255>(&ristretto_poprf_tvs)?;
|
||||
test_poprf_blind::<Ristretto255>(&ristretto_poprf_tvs)?;
|
||||
test_poprf_evaluate::<Ristretto255>(&ristretto_poprf_tvs)?;
|
||||
test_poprf_finalize::<Ristretto255>(&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::<NistP256>(&p256base_tvs)?;
|
||||
test_oprf_blind::<NistP256>(&p256base_tvs)?;
|
||||
test_oprf_evaluate::<NistP256>(&p256base_tvs)?;
|
||||
test_oprf_finalize::<NistP256>(&p256base_tvs)?;
|
||||
|
||||
test_voprf_seed_to_key::<NistP256>(&p256verifiable_tvs)?;
|
||||
test_voprf_blind::<NistP256>(&p256verifiable_tvs)?;
|
||||
test_voprf_evaluate::<NistP256>(&p256verifiable_tvs)?;
|
||||
test_voprf_finalize::<NistP256>(&p256verifiable_tvs)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_oprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let server = OprfServer::<CS>::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<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let server = VoprfServer::<CS>::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<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let server = PoprfServer::<CS>::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<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||
let client_result =
|
||||
OprfClient::<CS>::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<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||
let client_blind_result =
|
||||
VoprfClient::<CS>::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<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||
let client_blind_result =
|
||||
PoprfClient::<CS>::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<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let server = OprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
let message = server.evaluate(&BlindedElement::deserialize(
|
||||
¶meters.blinded_element[i],
|
||||
)?);
|
||||
|
||||
assert_eq!(
|
||||
¶meters.evaluation_element[i],
|
||||
&message.serialize().as_slice()
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_voprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ScalarLen>,
|
||||
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ScalarLen>: ArrayLength<u8>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
|
||||
let server = VoprfServer::<CS>::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 prepared_evaluation_elements = server.batch_evaluate_prepare(blinded_elements.iter());
|
||||
let prepared_elements: Vec<_> = prepared_evaluation_elements.collect();
|
||||
let VoprfServerBatchEvaluateFinishResult { messages, proof } =
|
||||
server.batch_evaluate_finish(&mut rng, blinded_elements.iter(), &prepared_elements)?;
|
||||
let 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(())
|
||||
}
|
||||
|
||||
fn test_poprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ScalarLen>,
|
||||
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ScalarLen>: ArrayLength<u8>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
|
||||
let server = PoprfServer::<CS>::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 PoprfServerBatchEvaluatePrepareResult {
|
||||
prepared_evaluation_elements,
|
||||
prepared_tweak,
|
||||
} = server.batch_evaluate_prepare(blinded_elements.iter(), Some(¶meters.info))?;
|
||||
let prepared_evaluation_elements: Vec<_> = prepared_evaluation_elements.collect();
|
||||
let PoprfServerBatchEvaluateFinishResult { messages, proof } =
|
||||
PoprfServer::batch_evaluate_finish::<_, _, Vec<_>>(
|
||||
&mut rng,
|
||||
blinded_elements.iter(),
|
||||
&prepared_evaluation_elements,
|
||||
&prepared_tweak,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
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_oprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let client =
|
||||
OprfClient::<CS>::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<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let mut clients = vec![];
|
||||
for i in 0..parameters.input.len() {
|
||||
let client = VoprfClient::<CS>::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::<Result<Vec<_>>>()?
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_poprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::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::<CS>::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()),
|
||||
&clients,
|
||||
&messages,
|
||||
&Proof::deserialize(¶meters.proof)?,
|
||||
CS::Group::deserialize_elem(¶meters.pksm)?,
|
||||
Some(¶meters.info),
|
||||
)?;
|
||||
|
||||
let result: Vec<Vec<u8>> = batch_result.map(|arr| arr.unwrap().to_vec()).collect();
|
||||
|
||||
assert_eq!(parameters.output, result);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -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<u8>,
|
||||
sksm: Vec<u8>,
|
||||
pksm: Vec<u8>,
|
||||
input: Vec<Vec<u8>>,
|
||||
info: Vec<u8>,
|
||||
blind: Vec<Vec<u8>>,
|
||||
blinded_element: Vec<Vec<u8>>,
|
||||
evaluation_element: Vec<Vec<u8>>,
|
||||
proof: Vec<u8>,
|
||||
proof_random_scalar: Vec<u8>,
|
||||
output: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
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<u8> {
|
||||
values[key]
|
||||
.as_str()
|
||||
.and_then(|s| hex::decode(&s.to_string()).ok())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn decode_vec(values: &JsonValue, key: &str) -> Vec<Vec<u8>> {
|
||||
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::<Vec<VOPRFTestVectorParameters>>()
|
||||
};
|
||||
}
|
||||
|
||||
#[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::<Ristretto255>(&ristretto_base_tvs)?;
|
||||
test_base_blind::<Ristretto255>(&ristretto_base_tvs)?;
|
||||
test_base_evaluate::<Ristretto255>(&ristretto_base_tvs)?;
|
||||
test_base_finalize::<Ristretto255>(&ristretto_base_tvs)?;
|
||||
|
||||
test_verifiable_seed_to_key::<Ristretto255>(&ristretto_verifiable_tvs)?;
|
||||
test_verifiable_blind::<Ristretto255>(&ristretto_verifiable_tvs)?;
|
||||
test_verifiable_evaluate::<Ristretto255>(&ristretto_verifiable_tvs)?;
|
||||
test_verifiable_finalize::<Ristretto255>(&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::<NistP256>(&p256base_tvs)?;
|
||||
test_base_blind::<NistP256>(&p256base_tvs)?;
|
||||
test_base_evaluate::<NistP256>(&p256base_tvs)?;
|
||||
test_base_finalize::<NistP256>(&p256base_tvs)?;
|
||||
|
||||
test_verifiable_seed_to_key::<NistP256>(&p256verifiable_tvs)?;
|
||||
test_verifiable_blind::<NistP256>(&p256verifiable_tvs)?;
|
||||
test_verifiable_evaluate::<NistP256>(&p256verifiable_tvs)?;
|
||||
test_verifiable_finalize::<NistP256>(&p256verifiable_tvs)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_base_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let server = NonVerifiableServer::<CS>::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<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let server = VerifiableServer::<CS>::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<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||
let client_result = NonVerifiableClient::<CS>::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<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||
let client_blind_result =
|
||||
VerifiableClient::<CS>::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<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let server = NonVerifiableServer::<CS>::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<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ScalarLen>,
|
||||
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ScalarLen>: ArrayLength<u8>,
|
||||
{
|
||||
use crate::{
|
||||
VerifiableServerBatchEvaluateFinishResult, VerifiableServerBatchEvaluatePrepareResult,
|
||||
};
|
||||
|
||||
for parameters in tvs {
|
||||
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
|
||||
let server = VerifiableServer::<CS>::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<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let client = NonVerifiableClient::<CS>::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<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let mut clients = vec![];
|
||||
for i in 0..parameters.input.len() {
|
||||
let client = VerifiableClient::<CS>::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::<Result<Vec<_>>>()?
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -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
|
||||
~~~
|
||||
"#;
|
||||
+440
-14
@@ -9,14 +9,429 @@
|
||||
|
||||
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, U11, U2, U256};
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use subtle::ConstantTimeEq;
|
||||
|
||||
use crate::InternalError;
|
||||
use crate::group::{STR_HASH_TO_GROUP, STR_HASH_TO_SCALAR};
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::serialization::serde::{Element, Scalar};
|
||||
use crate::{CipherSuite, Error, Group, InternalError, Result};
|
||||
|
||||
pub(crate) fn i2osp_2(input: usize) -> Result<GenericArray<u8, U2>, InternalError> {
|
||||
///////////////
|
||||
// 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; <CS::Group as Group>::Elem)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(crate = "serde", bound = "")
|
||||
)]
|
||||
pub struct BlindedElement<CS: CipherSuite>(
|
||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||
pub(crate) <CS::Group as Group>::Elem,
|
||||
)
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::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; <CS::Group as Group>::Elem)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(crate = "serde", bound = "")
|
||||
)]
|
||||
pub struct EvaluationElement<CS: CipherSuite>(
|
||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||
pub(crate) <CS::Group as Group>::Elem,
|
||||
)
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>;
|
||||
|
||||
/// Contains prepared [`EvaluationElement`]s by a server batch evaluate
|
||||
/// preparation.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Elem)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(crate = "serde", bound = "")
|
||||
)]
|
||||
pub struct PreparedEvaluationElement<CS: CipherSuite>(pub(crate) EvaluationElement<CS>)
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>;
|
||||
|
||||
/// A proof produced by a server that the OPRF output matches against a server
|
||||
/// public key.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(crate = "serde", bound = "")
|
||||
)]
|
||||
pub struct Proof<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) c_scalar: <CS::Group as Group>::Scalar,
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) s_scalar: <CS::Group as Group>::Scalar,
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// Proof Functions //
|
||||
// =============== //
|
||||
/////////////////////
|
||||
|
||||
/// Can only fail with [`Error::Batch`].
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
pub(crate) fn generate_proof<CS: CipherSuite, R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
k: <CS::Group as Group>::Scalar,
|
||||
a: <CS::Group as Group>::Elem,
|
||||
b: <CS::Group as Group>::Elem,
|
||||
cs: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
||||
ds: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
||||
mode: Mode,
|
||||
) -> Result<Proof<CS>>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.2.2-1
|
||||
|
||||
let (m, z) = compute_composites::<CS, _, _>(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 = <CS::Group as Group>::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,
|
||||
&STR_CHALLENGE,
|
||||
];
|
||||
|
||||
let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(mode));
|
||||
// This can't fail, the size of the `input` is known.
|
||||
let c_scalar = CS::Group::hash_to_scalar::<CS>(&h2_input, &dst).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<CS: CipherSuite>(
|
||||
a: <CS::Group as Group>::Elem,
|
||||
b: <CS::Group as Group>::Elem,
|
||||
cs: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
||||
ds: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
||||
proof: &Proof<CS>,
|
||||
mode: Mode,
|
||||
) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.4.1-2
|
||||
let (m, z) = compute_composites::<CS, _, _>(None, b, cs, ds, mode)?;
|
||||
let t2 = (a * &proof.s_scalar) + &(b * &proof.c_scalar);
|
||||
let t3 = (m * &proof.s_scalar) + &(z * &proof.c_scalar);
|
||||
|
||||
// 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 = <CS::Group as Group>::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,
|
||||
&STR_CHALLENGE,
|
||||
];
|
||||
|
||||
let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(mode));
|
||||
// This can't fail, the size of the `input` is known.
|
||||
let c = CS::Group::hash_to_scalar::<CS>(&h2_input, &dst).unwrap();
|
||||
|
||||
match c.ct_eq(&proof.c_scalar).into() {
|
||||
true => Ok(()),
|
||||
false => Err(Error::ProofVerification),
|
||||
}
|
||||
}
|
||||
|
||||
type ComputeCompositesResult<CS> = (
|
||||
<<CS as CipherSuite>::Group as Group>::Elem,
|
||||
<<CS as CipherSuite>::Group as Group>::Elem,
|
||||
);
|
||||
|
||||
/// Can only fail with [`Error::Batch`].
|
||||
fn compute_composites<
|
||||
CS: CipherSuite,
|
||||
IC: Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
||||
ID: Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
||||
>(
|
||||
k_option: Option<<CS::Group as Group>::Scalar>,
|
||||
b: <CS::Group as Group>::Elem,
|
||||
c_slice: IC,
|
||||
d_slice: ID,
|
||||
mode: Mode,
|
||||
) -> Result<ComputeCompositesResult<CS>>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.2.3-2
|
||||
|
||||
let elem_len = <CS::Group as Group>::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::<CS>(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);
|
||||
// Di = GG.SerializeElement(Ds[i])
|
||||
let di = CS::Group::serialize_elem(d);
|
||||
// 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,
|
||||
&STR_COMPOSITE,
|
||||
];
|
||||
|
||||
let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(mode));
|
||||
// This can't fail, the size of the `input` is known.
|
||||
let di = CS::Group::hash_to_scalar::<CS>(&h2_input, &dst).unwrap();
|
||||
m = c * &di + &m;
|
||||
z = match k_option {
|
||||
Some(_) => z,
|
||||
None => d * &di + &z,
|
||||
};
|
||||
}
|
||||
|
||||
z = match k_option {
|
||||
Some(k) => m * &k,
|
||||
None => z,
|
||||
};
|
||||
|
||||
Ok((m, z))
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// Inner Functions //
|
||||
// =============== //
|
||||
/////////////////////
|
||||
|
||||
type DeriveKeypairResult<CS> = (
|
||||
<<CS as CipherSuite>::Group as Group>::Scalar,
|
||||
<<CS as CipherSuite>::Group as Group>::Elem,
|
||||
);
|
||||
|
||||
/// Can only fail with [`Error::DeriveKeyPair`] and [`Error::Protocol`].
|
||||
pub(crate) fn derive_keypair<CS: CipherSuite>(
|
||||
seed: &[u8],
|
||||
info: &[u8],
|
||||
mode: Mode,
|
||||
) -> Result<DeriveKeypairResult<CS>, Error>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let context_string = create_context_string::<CS>(mode);
|
||||
let dst = GenericArray::from(STR_DERIVE_KEYPAIR).concat(context_string);
|
||||
|
||||
let info_len = i2osp_2(info.len()).map_err(|_| Error::DeriveKeyPair)?;
|
||||
|
||||
for counter in 0_u8..=u8::MAX {
|
||||
// deriveInput = seed || I2OSP(len(info), 2) || info
|
||||
// skS = G.HashToScalar(deriveInput || I2OSP(counter, 1), DST = "DeriveKeyPair"
|
||||
// || contextString)
|
||||
let sk_s = <CS::Group as Group>::hash_to_scalar::<CS>(
|
||||
&[seed, &info_len, info, &counter.to_be_bytes()],
|
||||
&dst,
|
||||
)
|
||||
.map_err(|_| Error::DeriveKeyPair)?;
|
||||
|
||||
if !bool::from(CS::Group::is_zero_scalar(sk_s)) {
|
||||
let pk_s = CS::Group::base_elem() * &sk_s;
|
||||
return Ok((sk_s, pk_s));
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::Protocol)
|
||||
}
|
||||
|
||||
/// 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<CS: CipherSuite>(
|
||||
input: &[u8],
|
||||
blind: &<CS::Group as Group>::Scalar,
|
||||
mode: Mode,
|
||||
) -> Result<<CS::Group as Group>::Elem>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
|
||||
let hashed_point = CS::Group::hash_to_curve::<CS>(&[input], &dst).map_err(|_| Error::Input)?;
|
||||
Ok(hashed_point * blind)
|
||||
}
|
||||
|
||||
/// Generates the contextString parameter as defined in
|
||||
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html>
|
||||
pub(crate) fn create_context_string<CS: CipherSuite>(mode: Mode) -> GenericArray<u8, U11>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
GenericArray::from(STR_VOPRF)
|
||||
.concat([mode.to_u8()].into())
|
||||
.concat(CS::ID.to_be_bytes().into())
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// Utility Functions //
|
||||
// ================= //
|
||||
///////////////////////
|
||||
|
||||
pub(crate) fn i2osp_2(input: usize) -> Result<[u8; 2], InternalError> {
|
||||
u16::try_from(input)
|
||||
.map(|input| input.to_be_bytes().into())
|
||||
.map(|input| input.to_be_bytes())
|
||||
.map_err(|_| InternalError::I2osp)
|
||||
}
|
||||
|
||||
@@ -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::<u8>(), 0..200)) {
|
||||
test_deserialize!(NonVerifiableClient, bytes);
|
||||
fn test_nocrash_oprf_client(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(OprfClient, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_verifiable_client(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(VerifiableClient, bytes);
|
||||
fn test_nocrash_voprf_client(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(VoprfClient, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_nonverifiable_server(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(NonVerifiableServer, bytes);
|
||||
fn test_nocrash_poprf_client(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(PoprfClient, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_verifiable_server(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(VerifiableServer, bytes);
|
||||
fn test_nocrash_oprf_server(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(OprfServer, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_voprf_server(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(VoprfServer, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_poprf_server(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(PoprfServer, bytes);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_blinded_element(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(BlindedElement, bytes);
|
||||
|
||||
+222
-1090
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user