General improvements (#56)

* Apply Rust traits to all public types and other improvements

* Move methods into appropriate section

* Check for zero scalars

* Change element and scalar de/serialization from `GenericArray` to slice

* Customize `serde` serialization
This commit is contained in:
daxpedda
2022-01-27 16:38:17 -08:00
committed by GitHub
parent b01b8ed409
commit b59b359aa3
9 changed files with 351 additions and 245 deletions
+2 -1
View File
@@ -22,6 +22,7 @@ ristretto255-fiat-u64 = ["curve25519-dalek/fiat_u64_backend", "ristretto255"]
ristretto255-simd = ["curve25519-dalek/simd_backend", "ristretto255"]
ristretto255-u32 = ["curve25519-dalek/u32_backend", "ristretto255"]
ristretto255-u64 = ["curve25519-dalek/u64_backend", "ristretto255"]
serde = ["generic-array/serde", "serde_"]
std = ["alloc"]
[dependencies]
@@ -36,7 +37,7 @@ elliptic-curve = { version = "0.12.0-pre.1", features = [
] }
generic-array = "0.14"
rand_core = { version = "0.6", default-features = false }
serde = { version = "1", default-features = false, features = [
serde_ = { version = "1", package = "serde", default-features = false, features = [
"derive",
], optional = true }
sha2 = { version = "0.10", default-features = false, optional = true }
+2
View File
@@ -27,6 +27,8 @@ pub enum Error {
ProofVerification,
/// Size of seed is longer then [`u16::MAX`].
Seed,
/// The protocol has failed and can't be completed.
Protocol,
}
/// Only used to implement [`Group`](crate::Group).
+6 -2
View File
@@ -89,7 +89,7 @@ where
result
}
fn deserialize_elem(element_bits: &GenericArray<u8, Self::ElemLen>) -> Result<Self::Elem> {
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem> {
PublicKey::<Self>::from_sec1_bytes(element_bits)
.map(|public_key| public_key.to_projective())
.map_err(|_| Error::Deserialization)
@@ -103,6 +103,10 @@ where
Option::from(scalar.invert()).unwrap()
}
fn is_zero_scalar(scalar: Self::Scalar) -> subtle::Choice {
scalar.is_zero()
}
#[cfg(test)]
fn zero_scalar() -> Self::Scalar {
Scalar::<Self>::zero()
@@ -112,7 +116,7 @@ where
scalar.into()
}
fn deserialize_scalar(scalar_bits: &GenericArray<u8, Self::ScalarLen>) -> Result<Self::Scalar> {
fn deserialize_scalar(scalar_bits: &[u8]) -> Result<Self::Scalar> {
SecretKey::<Self>::from_be_bytes(scalar_bits)
.map(|secret_key| *secret_key.to_nonzero_scalar())
.map_err(|_| Error::Deserialization)
+6 -3
View File
@@ -20,7 +20,7 @@ use generic_array::{ArrayLength, GenericArray};
use rand_core::{CryptoRng, RngCore};
#[cfg(feature = "ristretto255")]
pub use ristretto::Ristretto255;
use subtle::ConstantTimeEq;
use subtle::{Choice, ConstantTimeEq};
use zeroize::Zeroize;
use crate::voprf::Mode;
@@ -93,7 +93,7 @@ pub trait Group {
/// # Errors
/// [`Error::Deserialization`](crate::Error::Deserialization) if the element
/// is not a valid point on the group or the identity element.
fn deserialize_elem(element_bits: &GenericArray<u8, Self::ElemLen>) -> Result<Self::Elem>;
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem>;
/// picks a scalar at random
fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar;
@@ -101,6 +101,9 @@ pub trait Group {
/// The multiplicative inverse of this scalar
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar;
/// Returns `true` if the scalar is zero.
fn is_zero_scalar(scalar: Self::Scalar) -> Choice;
/// Returns the scalar representing zero
#[cfg(test)]
fn zero_scalar() -> Self::Scalar;
@@ -114,7 +117,7 @@ pub trait Group {
/// # Errors
/// [`Error::Deserialization`](crate::Error::Deserialization) if the scalar
/// is not a valid point on the group or zero.
fn deserialize_scalar(scalar_bits: &GenericArray<u8, Self::ScalarLen>) -> Result<Self::Scalar>;
fn deserialize_scalar(scalar_bits: &[u8]) -> Result<Self::Scalar>;
}
#[cfg(test)]
+14 -3
View File
@@ -16,12 +16,16 @@ 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 crate::{CipherSuite, Error, InternalError, Result};
/// [`Group`] implementation for Ristretto255.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
// `cfg` here is only needed because of a bug in Rust's crate feature documentation. See: https://github.com/rust-lang/rust/issues/83428
#[cfg(feature = "ristretto255")]
pub struct Ristretto255;
#[cfg(feature = "ristretto255-ciphersuite")]
@@ -99,7 +103,7 @@ impl Group for Ristretto255 {
elem.compress().to_bytes().into()
}
fn deserialize_elem(element_bits: &GenericArray<u8, Self::ElemLen>) -> Result<Self::Elem> {
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem> {
CompressedRistretto::from_slice(element_bits)
.decompress()
.filter(|point| point != &RistrettoPoint::identity())
@@ -124,6 +128,10 @@ impl Group for Ristretto255 {
scalar.invert()
}
fn is_zero_scalar(scalar: Self::Scalar) -> subtle::Choice {
scalar.ct_eq(&Scalar::zero())
}
#[cfg(test)]
fn zero_scalar() -> Self::Scalar {
Scalar::zero()
@@ -133,8 +141,11 @@ impl Group for Ristretto255 {
scalar.to_bytes().into()
}
fn deserialize_scalar(scalar_bits: &GenericArray<u8, Self::ScalarLen>) -> Result<Self::Scalar> {
Scalar::from_canonical_bytes((*scalar_bits).into())
fn deserialize_scalar(scalar_bits: &[u8]) -> Result<Self::Scalar> {
scalar_bits
.try_into()
.ok()
.and_then(Scalar::from_canonical_bytes)
.filter(|scalar| scalar != &Scalar::zero())
.ok_or(Error::Deserialization)
}
+18 -10
View File
@@ -88,9 +88,8 @@
//!
//! In the second step, the server takes as input the message from
//! [NonVerifiableClient::blind] (a [BlindedElement]), and runs
//! [NonVerifiableServer::evaluate] to produce a
//! [NonVerifiableServerEvaluateResult], which consists of an
//! [EvaluationElement] to be sent to the client.
//! [NonVerifiableServer::evaluate] to produce [EvaluationElement] to be sent to
//! the client.
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
@@ -135,13 +134,13 @@
//! # use voprf::NonVerifiableServer;
//! # let mut server_rng = OsRng;
//! # let server = NonVerifiableServer::<CipherSuite>::new(&mut server_rng);
//! # let server_evaluate_result = server.evaluate(
//! # let message = server.evaluate(
//! # &client_blind_result.message,
//! # None,
//! # ).expect("Unable to perform server evaluate");
//! let client_finalize_result = client_blind_result
//! .state
//! .finalize(b"input", &server_evaluate_result.message, None)
//! .finalize(b"input", &message, None)
//! .expect("Unable to perform client finalization");
//!
//! println!("VOPRF output: {:?}", client_finalize_result.to_vec());
@@ -479,7 +478,12 @@
#![deny(unsafe_code)]
#![no_std]
#![warn(clippy::cargo, clippy::missing_errors_doc, missing_docs)]
#![warn(
clippy::cargo,
clippy::missing_errors_doc,
missing_debug_implementations,
missing_docs
)]
#![allow(clippy::multiple_crate_versions)]
#[cfg(any(feature = "alloc", test))]
@@ -488,6 +492,9 @@ extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
#[cfg(feature = "serde")]
extern crate serde_ as serde;
mod ciphersuite;
mod error;
mod group;
@@ -513,8 +520,9 @@ pub use crate::serialization::{
pub use crate::voprf::VerifiableServerBatchEvaluateResult;
pub use crate::voprf::{
BlindedElement, EvaluationElement, Mode, NonVerifiableClient, NonVerifiableClientBlindResult,
NonVerifiableServer, NonVerifiableServerEvaluateResult, PreparedEvaluationElement,
PreparedTscalar, Proof, VerifiableClient, VerifiableClientBatchFinalizeResult,
VerifiableClientBlindResult, VerifiableServer, VerifiableServerBatchEvaluateFinishResult,
VerifiableServerBatchEvaluatePrepareResult, VerifiableServerEvaluateResult,
NonVerifiableServer, PreparedEvaluationElement, PreparedTscalar, Proof, VerifiableClient,
VerifiableClientBatchFinalizeResult, VerifiableClientBlindResult, VerifiableServer,
VerifiableServerBatchEvaluateFinishResult, VerifiableServerBatchEvaluateFinishedMessages,
VerifiableServerBatchEvaluatePrepareResult,
VerifiableServerBatchEvaluatePreparedEvaluationElements, VerifiableServerEvaluateResult,
};
+73 -16
View File
@@ -13,7 +13,7 @@ use core::ops::Add;
use digest::core_api::BlockSizeUser;
use digest::OutputSizeUser;
use generic_array::sequence::Concat;
use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256};
use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, Unsigned, U256};
use generic_array::{ArrayLength, GenericArray};
use crate::{
@@ -46,7 +46,7 @@ where
pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied();
let blind = CS::Group::deserialize_scalar(&deserialize(&mut input)?)?;
let blind = deserialize_scalar::<CS::Group, _>(&mut input)?;
Ok(Self { blind })
}
@@ -80,8 +80,8 @@ where
pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied();
let blind = CS::Group::deserialize_scalar(&deserialize(&mut input)?)?;
let blinded_element = CS::Group::deserialize_elem(&deserialize(&mut input)?)?;
let blind = deserialize_scalar::<CS::Group, _>(&mut input)?;
let blinded_element = deserialize_elem::<CS::Group, _>(&mut input)?;
Ok(Self {
blind,
@@ -110,7 +110,7 @@ where
pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied();
let sk = CS::Group::deserialize_scalar(&deserialize(&mut input)?)?;
let sk = deserialize_scalar::<CS::Group, _>(&mut input)?;
Ok(Self { sk })
}
@@ -143,8 +143,8 @@ where
pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied();
let sk = CS::Group::deserialize_scalar(&deserialize(&mut input)?)?;
let pk = CS::Group::deserialize_elem(&deserialize(&mut input)?)?;
let sk = deserialize_scalar::<CS::Group, _>(&mut input)?;
let pk = deserialize_elem::<CS::Group, _>(&mut input)?;
Ok(Self { sk, pk })
}
@@ -178,8 +178,8 @@ where
pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied();
let c_scalar = CS::Group::deserialize_scalar(&deserialize(&mut input)?)?;
let s_scalar = CS::Group::deserialize_scalar(&deserialize(&mut input)?)?;
let c_scalar = deserialize_scalar::<CS::Group, _>(&mut input)?;
let s_scalar = deserialize_scalar::<CS::Group, _>(&mut input)?;
Ok(Proof { c_scalar, s_scalar })
}
@@ -205,7 +205,7 @@ where
pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied();
let value = CS::Group::deserialize_elem(&deserialize(&mut input)?)?;
let value = deserialize_elem::<CS::Group, _>(&mut input)?;
Ok(Self(value))
}
@@ -231,15 +231,72 @@ where
pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied();
let value = CS::Group::deserialize_elem(&deserialize(&mut input)?)?;
let value = deserialize_elem::<CS::Group, _>(&mut input)?;
Ok(Self(value))
}
}
fn deserialize<L: ArrayLength<u8>>(
input: &mut impl Iterator<Item = u8>,
) -> Result<GenericArray<u8, L>> {
let input = input.by_ref().take(L::USIZE);
GenericArray::from_exact_iter(input).ok_or(Error::Deserialization)
fn deserialize_elem<G: Group, I: Iterator<Item = u8>>(input: &mut I) -> Result<G::Elem> {
let input = input.by_ref().take(G::ElemLen::USIZE);
GenericArray::<_, G::ElemLen>::from_exact_iter(input)
.ok_or(Error::Deserialization)
.and_then(|bytes| G::deserialize_elem(&bytes))
}
fn deserialize_scalar<G: Group, I: Iterator<Item = u8>>(input: &mut I) -> Result<G::Scalar> {
let input = input.by_ref().take(G::ScalarLen::USIZE);
GenericArray::<_, G::ScalarLen>::from_exact_iter(input)
.ok_or(Error::Deserialization)
.and_then(|bytes| G::deserialize_scalar(&bytes))
}
#[cfg(feature = "serde")]
pub(crate) mod serde {
use core::marker::PhantomData;
use generic_array::GenericArray;
use serde::de::{Deserializer, Error};
use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
use crate::Group;
pub(crate) struct Element<G: Group>(PhantomData<G>);
impl<'de, G: Group> Element<G> {
pub(crate) fn deserialize<D>(deserializer: D) -> Result<G::Elem, D::Error>
where
D: Deserializer<'de>,
{
GenericArray::<_, G::ElemLen>::deserialize(deserializer)
.and_then(|bytes| G::deserialize_elem(&bytes).map_err(D::Error::custom))
}
pub(crate) fn serialize<S>(self_: &G::Elem, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
G::serialize_elem(*self_).serialize(serializer)
}
}
pub(crate) struct Scalar<G: Group>(PhantomData<G>);
impl<'de, G: Group> Scalar<G> {
pub(crate) fn deserialize<D>(deserializer: D) -> Result<G::Scalar, D::Error>
where
D: Deserializer<'de>,
{
GenericArray::<_, G::ScalarLen>::deserialize(deserializer)
.and_then(|bytes| G::deserialize_scalar(&bytes).map_err(D::Error::custom))
}
pub(crate) fn serialize<S>(self_: &G::Scalar, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
G::serialize_scalar(*self_).serialize(serializer)
}
}
}
+9 -17
View File
@@ -13,7 +13,7 @@ use core::ops::Add;
use digest::core_api::BlockSizeUser;
use digest::OutputSizeUser;
use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256};
use generic_array::{ArrayLength, GenericArray};
use generic_array::ArrayLength;
use json::JsonValue;
use crate::tests::mock_rng::CycleRng;
@@ -183,9 +183,7 @@ where
{
for parameters in tvs {
for i in 0..parameters.input.len() {
let blind = CS::Group::deserialize_scalar(&GenericArray::clone_from_slice(
&parameters.blind[i],
))?;
let blind = CS::Group::deserialize_scalar(&parameters.blind[i])?;
let client_result = NonVerifiableClient::<CS>::deterministic_blind_unchecked(
&parameters.input[i],
blind,
@@ -212,9 +210,7 @@ where
{
for parameters in tvs {
for i in 0..parameters.input.len() {
let blind = CS::Group::deserialize_scalar(&GenericArray::clone_from_slice(
&parameters.blind[i],
))?;
let blind = CS::Group::deserialize_scalar(&parameters.blind[i])?;
let client_blind_result =
VerifiableClient::<CS>::deterministic_blind_unchecked(&parameters.input[i], blind)?;
@@ -240,14 +236,14 @@ where
for parameters in tvs {
for i in 0..parameters.input.len() {
let server = NonVerifiableServer::<CS>::new_with_key(&parameters.sksm)?;
let server_result = server.evaluate(
let message = server.evaluate(
&BlindedElement::deserialize(&parameters.blinded_element[i])?,
Some(&parameters.info),
)?;
assert_eq!(
&parameters.evaluation_element[i],
&server_result.message.serialize().as_slice()
&message.serialize().as_slice()
);
}
}
@@ -306,7 +302,7 @@ where
for parameters in tvs {
for i in 0..parameters.input.len() {
let client = NonVerifiableClient::<CS>::from_blind(CS::Group::deserialize_scalar(
&GenericArray::clone_from_slice(&parameters.blind[i]),
&parameters.blind[i],
)?);
let client_finalize_result = client.finalize(
@@ -330,12 +326,8 @@ where
let mut clients = vec![];
for i in 0..parameters.input.len() {
let client = VerifiableClient::<CS>::from_blind_and_element(
CS::Group::deserialize_scalar(&GenericArray::clone_from_slice(
&parameters.blind[i],
))?,
CS::Group::deserialize_elem(&GenericArray::clone_from_slice(
&parameters.blinded_element[i],
))?,
CS::Group::deserialize_scalar(&parameters.blind[i])?,
CS::Group::deserialize_elem(&parameters.blinded_element[i])?,
);
clients.push(client.clone());
}
@@ -351,7 +343,7 @@ where
&clients,
&messages,
&Proof::deserialize(&parameters.proof)?,
CS::Group::deserialize_elem(GenericArray::from_slice(&parameters.pksm))?,
CS::Group::deserialize_elem(&parameters.pksm)?,
Some(&parameters.info),
)?;
+221 -193
View File
@@ -20,6 +20,8 @@ use generic_array::GenericArray;
use rand_core::{CryptoRng, RngCore};
use subtle::ConstantTimeEq;
#[cfg(feature = "serde")]
use crate::serialization::serde::{Element, Scalar};
use crate::util::{i2osp_2, i2osp_2_array};
use crate::{CipherSuite, Error, Group, Result};
@@ -37,7 +39,7 @@ const STR_VOPRF: [u8; 8] = *b"VOPRF08-";
/// Determines the mode of operation (either base mode or verifiable mode). This
/// is only used for custom implementations for [`Group`].
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Debug)]
pub enum Mode {
/// Non-verifiable mode.
Base,
@@ -68,16 +70,14 @@ impl Mode {
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound(
deserialize = "<CS::Group as Group>::Scalar: serde::Deserialize<'de>",
serialize = "<CS::Group as Group>::Scalar: serde::Serialize"
))
serde(crate = "serde", bound = "")
)]
pub struct NonVerifiableClient<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,
}
@@ -89,19 +89,16 @@ where
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound(
deserialize = "<CS::Group as Group>::Scalar: serde::Deserialize<'de>, <CS::Group as \
Group>::Elem: serde::Deserialize<'de>",
serialize = "<CS::Group as Group>::Scalar: serde::Serialize, <CS::Group as Group>::Elem: \
serde::Serialize"
))
serde(crate = "serde", bound = "")
)]
pub struct VerifiableClient<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,
}
@@ -113,16 +110,14 @@ where
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound(
deserialize = "<CS::Group as Group>::Scalar: serde::Deserialize<'de>",
serialize = "<CS::Group as Group>::Scalar: serde::Serialize"
))
serde(crate = "serde", bound = "")
)]
pub struct NonVerifiableServer<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,
}
@@ -134,19 +129,16 @@ where
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound(
deserialize = "<CS::Group as Group>::Scalar: serde::Deserialize<'de>, <CS::Group as \
Group>::Elem: serde::Deserialize<'de>",
serialize = "<CS::Group as Group>::Scalar: serde::Serialize, <CS::Group as Group>::Elem: \
serde::Serialize"
))
serde(crate = "serde", bound = "")
)]
pub struct VerifiableServer<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,
}
@@ -158,17 +150,16 @@ where
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound(
deserialize = "<CS::Group as Group>::Scalar: serde::Deserialize<'de>",
serialize = "<CS::Group as Group>::Scalar: 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,
}
@@ -180,12 +171,12 @@ where
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound(
deserialize = "<CS::Group as Group>::Elem: serde::Deserialize<'de>",
serialize = "<CS::Group as Group>::Elem: serde::Serialize"
))
serde(crate = "serde", bound = "")
)]
pub struct BlindedElement<CS: CipherSuite>(pub(crate) <CS::Group as Group>::Elem)
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>;
@@ -198,12 +189,12 @@ where
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound(
deserialize = "<CS::Group as Group>::Elem: serde::Deserialize<'de>",
serialize = "<CS::Group as Group>::Elem: serde::Serialize"
))
serde(crate = "serde", bound = "")
)]
pub struct EvaluationElement<CS: CipherSuite>(pub(crate) <CS::Group as Group>::Elem)
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>;
@@ -447,7 +438,7 @@ where
/// [`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.into())?;
let sk = CS::Group::deserialize_scalar(private_key_bytes)?;
Ok(Self { sk })
}
@@ -474,12 +465,13 @@ where
/// to the client.
///
/// # Errors
/// [`Error::Metadata`] if the `metadata` is longer then `u16::MAX - 21`.
/// - [`Error::Metadata`] if the `metadata` is longer then `u16::MAX - 21`.
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
pub fn evaluate(
&self,
blinded_element: &BlindedElement<CS>,
metadata: Option<&[u8]>,
) -> Result<NonVerifiableServerEvaluateResult<CS>> {
) -> Result<EvaluationElement<CS>> {
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.1.1-1
let context_string = get_context_string::<CS>(Mode::Base);
@@ -496,12 +488,17 @@ where
CS::Group::hash_to_scalar::<CS>(&context, Mode::Base).map_err(|_| Error::Metadata)?;
// t = skS + m
let t = self.sk + &m;
// if t == 0:
if bool::from(CS::Group::is_zero_scalar(t)) {
// raise InverseError
return Err(Error::Protocol);
}
// Z = t^(-1) * R
let z = blinded_element.0 * &CS::Group::invert_scalar(t);
Ok(NonVerifiableServerEvaluateResult {
message: EvaluationElement(z),
})
Ok(EvaluationElement(z))
}
}
@@ -525,7 +522,7 @@ where
/// [`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.into())?;
let sk = CS::Group::deserialize_scalar(key)?;
let pk = CS::Group::base_elem() * &sk;
Ok(Self { sk, pk })
}
@@ -555,7 +552,8 @@ where
/// to the client.
///
/// # Errors
/// [`Error::Metadata`] if the `metadata` is longer then `u16::MAX - 21`.
/// - [`Error::Metadata`] if the `metadata` is longer then `u16::MAX - 21`.
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
pub fn evaluate<R: RngCore + CryptoRng>(
&self,
rng: &mut R,
@@ -586,7 +584,8 @@ where
/// messages from a [VerifiableClient]
///
/// # Errors
/// [`Error::Metadata`] if the `metadata` is longer then `u16::MAX - 21`.
/// - [`Error::Metadata`] if the `metadata` is longer then `u16::MAX - 21`.
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
#[cfg(feature = "alloc")]
pub fn batch_evaluate<'a, R: RngCore + CryptoRng, I>(
&self,
@@ -628,7 +627,8 @@ where
/// [`batch_evaluate_finish`](Self::batch_evaluate_finish).
///
/// # Errors
/// [`Error::Metadata`] if the `metadata` is longer then `u16::MAX - 21`.
/// - [`Error::Metadata`] if the `metadata` is longer then `u16::MAX - 21`.
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
pub fn batch_evaluate_prepare<'a, I: Iterator<Item = &'a BlindedElement<CS>>>(
&self,
blinded_elements: I,
@@ -648,6 +648,13 @@ where
let m = CS::Group::hash_to_scalar::<CS>(&context, Mode::Verifiable)
.map_err(|_| Error::Metadata)?;
let t = self.sk + &m;
// if t == 0:
if bool::from(CS::Group::is_zero_scalar(t)) {
// raise InverseError
return Err(Error::Protocol);
}
let evaluation_elements = blinded_elements
// To make a return type possible, we have to convert to a `fn` pointer, which isn't
// possible if we `move` from context.
@@ -690,14 +697,14 @@ where
u,
evaluation_elements
.into_iter()
.map(|element| element.0.copy()),
blinded_elements.map(BlindedElement::copy),
.map(|element| element.0.clone()),
blinded_elements.cloned(),
)?;
let messages =
evaluation_elements
.into_iter()
.map(<fn(&PreparedEvaluationElement<CS>) -> _>::from(|element| {
element.0.copy()
element.0.clone()
}));
Ok(VerifiableServerBatchEvaluateFinishResult { messages, proof })
@@ -709,146 +716,11 @@ where
}
}
/////////////////////////
// Convenience Structs //
//==================== //
/////////////////////////
/// Contains the fields that are returned by a non-verifiable client blind
pub struct NonVerifiableClientBlindResult<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: NonVerifiableClient<CS>,
/// The message to send to the server
pub message: BlindedElement<CS>,
}
/// Contains the fields that are returned by a non-verifiable server evaluate
pub struct NonVerifiableServerEvaluateResult<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>,
}
/// Contains the fields that are returned by a verifiable client blind
pub struct VerifiableClientBlindResult<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: VerifiableClient<CS>,
/// The message to send to the server
pub message: BlindedElement<CS>,
}
/// Concrete return type for [`VerifiableClient::batch_finalize`].
pub type VerifiableClientBatchFinalizeResult<'a, C, I, II, IC, IM> = FinalizeAfterUnblindResult<
'a,
C,
I,
Zip<<&'a II as IntoIterator>::IntoIter, VerifiableUnblindResult<'a, C, IC, IM>>,
>;
/// Contains the fields that are returned by a verifiable server evaluate
pub struct VerifiableServerEvaluateResult<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 prepared [`EvaluationElement`]s by a verifiable server batch
/// evaluate preparation.
pub struct PreparedEvaluationElement<CS: CipherSuite>(EvaluationElement<CS>)
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>;
/// Contains the prepared `t` by a verifiable server batch evaluate preparation.
#[derive(DeriveWhere)]
#[derive_where(Zeroize(drop))]
pub struct PreparedTscalar<CS: CipherSuite>(<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 verifiable server batch evaluate
/// preparation.
pub struct VerifiableServerBatchEvaluatePrepareResult<
'a,
CS: 'a + CipherSuite,
I: Iterator<Item = &'a BlindedElement<CS>>,
> where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Prepared [`EvaluationElement`]s that will become messages.
#[allow(clippy::type_complexity)]
pub prepared_evaluation_elements: Map<
Zip<I, Repeat<<CS::Group as Group>::Scalar>>,
fn((&BlindedElement<CS>, <CS::Group as Group>::Scalar)) -> PreparedEvaluationElement<CS>,
>,
/// Prepared `t` needed to finish the verifiable server batch evaluation.
pub t: PreparedTscalar<CS>,
}
/// Contains the fields that are returned by a verifiable server batch evaluate
/// finish.
pub struct VerifiableServerBatchEvaluateFinishResult<'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 messages to send to the client
#[allow(clippy::type_complexity)]
pub messages: Map<
<&'a I as IntoIterator>::IntoIter,
fn(&PreparedEvaluationElement<CS>) -> 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
#[cfg(feature = "alloc")]
pub struct VerifiableServerBatchEvaluateResult<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: alloc::vec::Vec<EvaluationElement<CS>>,
/// The proof for the client to verify
pub proof: Proof<CS>,
}
///////////////////////////////////////////////
// Inner functions and Trait Implementations //
// ========================================= //
///////////////////////////////////////////////
impl<CS: CipherSuite> BlindedElement<CS>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Only used to easier validate allocation
fn copy(&self) -> Self {
Self(self.0)
}
#[cfg(feature = "danger")]
/// Creates a [BlindedElement] from a raw group element.
///
@@ -872,11 +744,6 @@ where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Only used to easier validate allocation
fn copy(&self) -> Self {
Self(self.0)
}
#[cfg(feature = "danger")]
/// Creates an [EvaluationElement] from a raw group element.
///
@@ -895,6 +762,167 @@ where
}
}
/////////////////////////
// Convenience Structs //
//==================== //
/////////////////////////
/// Contains the fields that are returned by a non-verifiable client blind
#[derive(DeriveWhere)]
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
pub struct NonVerifiableClientBlindResult<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: NonVerifiableClient<CS>,
/// The message to send to the server
pub message: BlindedElement<CS>,
}
/// Contains the fields that are returned by a verifiable client blind
#[derive(DeriveWhere)]
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
pub struct VerifiableClientBlindResult<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: VerifiableClient<CS>,
/// The message to send to the server
pub message: BlindedElement<CS>,
}
/// Concrete return type for [`VerifiableClient::batch_finalize`].
pub type VerifiableClientBatchFinalizeResult<'a, C, I, II, IC, IM> = FinalizeAfterUnblindResult<
'a,
C,
I,
Zip<<&'a II as IntoIterator>::IntoIter, VerifiableUnblindResult<'a, C, IC, IM>>,
>;
/// Contains the fields that are returned by a verifiable server evaluate
#[derive(DeriveWhere)]
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
pub struct VerifiableServerEvaluateResult<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 prepared [`EvaluationElement`]s by a verifiable server batch
/// evaluate preparation.
#[derive(DeriveWhere)]
#[derive_where(Clone, Zeroize(drop))]
#[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>(EvaluationElement<CS>)
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>;
/// Contains the prepared `t` by a verifiable server batch evaluate preparation.
#[derive(DeriveWhere)]
#[derive_where(Clone, Zeroize(drop))]
#[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 PreparedTscalar<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>;
/// Concrete type of [`EvaluationElement`]s in
/// [`VerifiableServerBatchEvaluatePrepareResult`].
pub type VerifiableServerBatchEvaluatePreparedEvaluationElements<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>,
>;
/// Contains the fields that are returned by a verifiable server batch evaluate
/// preparation.
#[derive(DeriveWhere)]
#[derive_where(Debug; I, <CS::Group as Group>::Scalar)]
pub struct VerifiableServerBatchEvaluatePrepareResult<
'a,
CS: 'a + CipherSuite,
I: Iterator<Item = &'a BlindedElement<CS>>,
> where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Prepared [`EvaluationElement`]s that will become messages.
pub prepared_evaluation_elements:
VerifiableServerBatchEvaluatePreparedEvaluationElements<CS, I>,
/// Prepared `t` needed to finish the verifiable server batch evaluation.
pub t: PreparedTscalar<CS>,
}
/// Concrete type of [`EvaluationElement`]s in
/// [`VerifiableServerBatchEvaluateFinishResult`].
pub type VerifiableServerBatchEvaluateFinishedMessages<'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(DeriveWhere)]
#[derive_where(Debug; <&'a I as core::iter::IntoIterator>::IntoIter, <CS::Group as Group>::Scalar)]
pub struct VerifiableServerBatchEvaluateFinishResult<'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: VerifiableServerBatchEvaluateFinishedMessages<'a, CS, I>,
/// The proof for the client to verify
pub proof: Proof<CS>,
}
/// Contains the fields that are returned by a verifiable server batch evaluate
#[derive(DeriveWhere)]
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
#[cfg(feature = "alloc")]
pub struct VerifiableServerBatchEvaluateResult<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: alloc::vec::Vec<EvaluationElement<CS>>,
/// The proof for the client to verify
pub proof: Proof<CS>,
}
/////////////////////
// Inner functions //
// =============== //
/////////////////////
type BlindResult<C> = (
<<C as CipherSuite>::Group as Group>::Scalar,
<<C as CipherSuite>::Group as Group>::Elem,
@@ -991,7 +1019,7 @@ where
.into_iter()
// Convert to `fn` pointer to make a return type possible.
.map(<fn(&VerifiableClient<CS>) -> _>::from(|x| x.blind));
let evaluation_elements = messages.into_iter().map(EvaluationElement::copy);
let evaluation_elements = messages.into_iter().cloned();
let blinded_elements = clients
.into_iter()
.map(|client| BlindedElement(client.blinded_element));
@@ -1338,12 +1366,12 @@ mod tests {
let mut rng = OsRng;
let client_blind_result = NonVerifiableClient::<CS>::blind(input, &mut rng).unwrap();
let server = NonVerifiableServer::<CS>::new(&mut rng);
let server_result = server
let message = server
.evaluate(&client_blind_result.message, Some(info))
.unwrap();
let client_finalize_result = client_blind_result
.state
.finalize(input, &server_result.message, Some(info))
.finalize(input, &message, Some(info))
.unwrap();
let res2 = prf::<CS>(input, server.get_private_key(), info, Mode::Base);
assert_eq!(client_finalize_result, res2);
@@ -1589,7 +1617,7 @@ mod tests {
let mut rng = OsRng;
let client_blind_result = NonVerifiableClient::<CS>::blind(input, &mut rng).unwrap();
let server = NonVerifiableServer::<CS>::new(&mut rng);
let server_result = server
let message = server
.evaluate(&client_blind_result.message, Some(info))
.unwrap();
@@ -1597,7 +1625,7 @@ mod tests {
Zeroize::zeroize(&mut state);
assert!(state.serialize().iter().all(|&x| x == 0));
let mut message = server_result.message;
let mut message = message;
Zeroize::zeroize(&mut message);
assert!(message.serialize().iter().all(|&x| x == 0));
}