General Improvements (#47)

* Introduce `Result` shorthand, re-export and rename `InternalError`

* Re-export some public API relevant types

* Move `deserialize`

* Remove branch in `i2osp`

* Make `serialize` and `serialize_owned` methods

* Update p256
This commit is contained in:
daxpedda
2021-12-25 16:54:27 -05:00
committed by GitHub
parent b2f6d5eac8
commit 55ef981a3f
13 changed files with 184 additions and 194 deletions
+5
View File
@@ -46,6 +46,11 @@ jobs:
toolchain: toolchain:
- stable - stable
- 1.51.0 - 1.51.0
exclude:
- backend_feature: p256
toolchain: 1.51.0
- backend_feature: ristretto255_u64,p256
toolchain: 1.51.0
name: test name: test
steps: steps:
- name: Checkout sources - name: Checkout sources
+1 -2
View File
@@ -42,9 +42,8 @@ num-bigint = { version = "0.4", default-features = false, optional = true }
num-integer = { version = "0.1", default-features = false, optional = true } num-integer = { version = "0.1", default-features = false, optional = true }
num-traits = { version = "0.2", default-features = false, optional = true } num-traits = { version = "0.2", default-features = false, optional = true }
once_cell = { version = "1", default-features = false, optional = true } once_cell = { version = "1", default-features = false, optional = true }
p256_ = { package = "p256", version = "0.9", default-features = false, features = [ p256_ = { package = "p256", version = "0.10", default-features = false, features = [
"arithmetic", "arithmetic",
"zeroize",
], optional = true } ], optional = true }
rand_core = { version = "0.6", default-features = false } rand_core = { version = "0.6", default-features = false }
serde = { version = "1", default-features = false, features = [ serde = { version = "1", default-features = false, features = [
+6 -5
View File
@@ -5,15 +5,16 @@
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory // License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. // of this source tree.
//! A list of error types which are produced during an execution of the protocol //! Errors which are produced during an execution of the protocol
#[cfg(feature = "std")]
use std::error::Error;
use displaydoc::Display; use displaydoc::Display;
/// [`Result`](core::result::Result) shorthand that uses [`Error`].
pub type Result<T> = core::result::Result<T, Error>;
/// Represents an error in the manipulation of internal cryptographic data /// Represents an error in the manipulation of internal cryptographic data
#[derive(Clone, Copy, Debug, Display, Eq, Hash, Ord, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum InternalError { pub enum Error {
/// Could not parse byte sequence for key /// Could not parse byte sequence for key
InvalidByteSequence, InvalidByteSequence,
/// Could not deserialize element, or deserialized to the identity element /// Could not deserialize element, or deserialized to the identity element
@@ -38,4 +39,4 @@ pub enum InternalError {
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl Error for InternalError {} impl std::error::Error for Error {}
+3 -3
View File
@@ -13,8 +13,8 @@ use generic_array::sequence::Concat;
use generic_array::typenum::{Unsigned, U1, U2}; use generic_array::typenum::{Unsigned, U1, U2};
use generic_array::{ArrayLength, GenericArray}; use generic_array::{ArrayLength, GenericArray};
use crate::errors::InternalError;
use crate::util::i2osp; use crate::util::i2osp;
use crate::{Error, Result};
// Computes ceil(x / y) // Computes ceil(x / y)
fn div_ceil(x: usize, y: usize) -> usize { fn div_ceil(x: usize, y: usize) -> usize {
@@ -37,14 +37,14 @@ pub fn expand_message_xmd<
>( >(
msg: M, msg: M,
dst: GenericArray<u8, D>, dst: GenericArray<u8, D>,
) -> Result<GenericArray<u8, L>, InternalError> ) -> Result<GenericArray<u8, L>>
where where
<D as Add<U1>>::Output: ArrayLength<u8>, <D as Add<U1>>::Output: ArrayLength<u8>,
{ {
let digest_len = H::OutputSize::USIZE; let digest_len = H::OutputSize::USIZE;
let ell = div_ceil(L::USIZE, digest_len); let ell = div_ceil(L::USIZE, digest_len);
if ell > 255 { if ell > 255 {
return Err(InternalError::HashToCurveError); return Err(Error::HashToCurveError);
} }
let dst_prime = dst.concat(i2osp::<U1>(D::USIZE)?); let dst_prime = dst.concat(i2osp::<U1>(D::USIZE)?);
let z_pad = i2osp::<H::BlockSize>(0)?; let z_pad = i2osp::<H::BlockSize>(0)?;
+10 -11
View File
@@ -24,7 +24,7 @@ use rand_core::{CryptoRng, RngCore};
use subtle::ConstantTimeEq; use subtle::ConstantTimeEq;
use zeroize::Zeroize; use zeroize::Zeroize;
use crate::errors::InternalError; use crate::{Error, Result};
/// A prime-order subgroup of a base field (EC, prime-order field ...). This /// A prime-order subgroup of a base field (EC, prime-order field ...). This
/// subgroup is noted additively — as in the draft RFC — in this trait. /// subgroup is noted additively — as in the draft RFC — in this trait.
@@ -43,7 +43,7 @@ pub trait Group:
fn hash_to_curve<H: BlockSizeUser + Digest + FixedOutputReset, D: ArrayLength<u8> + Add<U1>>( fn hash_to_curve<H: BlockSizeUser + Digest + FixedOutputReset, D: ArrayLength<u8> + Add<U1>>(
msg: &[u8], msg: &[u8],
dst: GenericArray<u8, D>, dst: GenericArray<u8, D>,
) -> Result<Self, InternalError> ) -> Result<Self>
where where
<D as Add<U1>>::Output: ArrayLength<u8>; <D as Add<U1>>::Output: ArrayLength<u8>;
@@ -56,7 +56,7 @@ pub trait Group:
>( >(
input: I, input: I,
dst: GenericArray<u8, D>, dst: GenericArray<u8, D>,
) -> Result<Self::Scalar, InternalError> ) -> Result<Self::Scalar>
where where
<D as Add<U1>>::Output: ArrayLength<u8>; <D as Add<U1>>::Output: ArrayLength<u8>;
@@ -74,16 +74,16 @@ pub trait Group:
/// checking if the scalar is zero. /// checking if the scalar is zero.
fn from_scalar_slice_unchecked( fn from_scalar_slice_unchecked(
scalar_bits: &GenericArray<u8, Self::ScalarLen>, scalar_bits: &GenericArray<u8, Self::ScalarLen>,
) -> Result<Self::Scalar, InternalError>; ) -> Result<Self::Scalar>;
/// Return a scalar from its fixed-length bytes representation. If the /// Return a scalar from its fixed-length bytes representation. If the
/// scalar is zero, then return an error. /// scalar is zero, then return an error.
fn from_scalar_slice<'a>( fn from_scalar_slice<'a>(
scalar_bits: impl Into<&'a GenericArray<u8, Self::ScalarLen>>, scalar_bits: impl Into<&'a GenericArray<u8, Self::ScalarLen>>,
) -> Result<Self::Scalar, InternalError> { ) -> Result<Self::Scalar> {
let scalar = Self::from_scalar_slice_unchecked(scalar_bits.into())?; let scalar = Self::from_scalar_slice_unchecked(scalar_bits.into())?;
if scalar.ct_eq(&Self::scalar_zero()).into() { if scalar.ct_eq(&Self::scalar_zero()).into() {
return Err(InternalError::ZeroScalarError); return Err(Error::ZeroScalarError);
} }
Ok(scalar) Ok(scalar)
} }
@@ -101,20 +101,19 @@ pub trait Group:
/// Return an element from its fixed-length bytes representation. This is /// Return an element from its fixed-length bytes representation. This is
/// the unchecked version, which does not check for deserializing the /// the unchecked version, which does not check for deserializing the
/// identity element /// identity element
fn from_element_slice_unchecked( fn from_element_slice_unchecked(element_bits: &GenericArray<u8, Self::ElemLen>)
element_bits: &GenericArray<u8, Self::ElemLen>, -> Result<Self>;
) -> Result<Self, InternalError>;
/// Return an element from its fixed-length bytes representation. If the /// Return an element from its fixed-length bytes representation. If the
/// element is the identity element, return an error. /// element is the identity element, return an error.
fn from_element_slice<'a>( fn from_element_slice<'a>(
element_bits: impl Into<&'a GenericArray<u8, Self::ElemLen>>, element_bits: impl Into<&'a GenericArray<u8, Self::ElemLen>>,
) -> Result<Self, InternalError> { ) -> Result<Self> {
let elem = Self::from_element_slice_unchecked(element_bits.into())?; let elem = Self::from_element_slice_unchecked(element_bits.into())?;
if Self::ct_eq(&elem, &<Self as Group>::identity()).into() { if Self::ct_eq(&elem, &<Self as Group>::identity()).into() {
// found the identity element // found the identity element
return Err(InternalError::PointError); return Err(Error::PointError);
} }
Ok(elem) Ok(elem)
+15 -14
View File
@@ -26,6 +26,7 @@ use num_traits::{One, ToPrimitive, Zero};
use once_cell::unsync::Lazy; use once_cell::unsync::Lazy;
use p256_::elliptic_curve::group::prime::PrimeCurveAffine; use p256_::elliptic_curve::group::prime::PrimeCurveAffine;
use p256_::elliptic_curve::group::GroupEncoding; use p256_::elliptic_curve::group::GroupEncoding;
use p256_::elliptic_curve::ops::Reduce;
use p256_::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint}; use p256_::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint};
use p256_::elliptic_curve::Field; use p256_::elliptic_curve::Field;
use p256_::{AffinePoint, EncodedPoint, ProjectivePoint}; use p256_::{AffinePoint, EncodedPoint, ProjectivePoint};
@@ -33,7 +34,7 @@ use rand_core::{CryptoRng, RngCore};
use subtle::{Choice, ConditionallySelectable}; use subtle::{Choice, ConditionallySelectable};
use super::Group; use super::Group;
use crate::errors::InternalError; use crate::{Error, Result};
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.2 // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.2
// `L: 48` // `L: 48`
@@ -48,7 +49,7 @@ impl Group for ProjectivePoint {
fn hash_to_curve<H: BlockSizeUser + Digest + FixedOutputReset, D: ArrayLength<u8> + Add<U1>>( fn hash_to_curve<H: BlockSizeUser + Digest + FixedOutputReset, D: ArrayLength<u8> + Add<U1>>(
msg: &[u8], msg: &[u8],
dst: GenericArray<u8, D>, dst: GenericArray<u8, D>,
) -> Result<Self, InternalError> ) -> Result<Self>
where where
<D as Add<U1>>::Output: ArrayLength<u8>, <D as Add<U1>>::Output: ArrayLength<u8>,
{ {
@@ -85,15 +86,15 @@ impl Group for ProjectivePoint {
let (q1x, q1y) = hash_to_curve_simple_swu(&uniform_bytes[L::USIZE..], &A, &B, &P, &Z); let (q1x, q1y) = hash_to_curve_simple_swu(&uniform_bytes[L::USIZE..], &A, &B, &P, &Z);
// convert to `p256` types // convert to `p256` types
let p0 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates( let p0 = Option::<AffinePoint>::from(AffinePoint::from_encoded_point(
&q0x, &q0y, false, &EncodedPoint::from_affine_coordinates(&q0x, &q0y, false),
)) ))
.ok_or(InternalError::PointError)? .ok_or(Error::PointError)?
.to_curve(); .to_curve();
let p1 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates( let p1 = Option::<AffinePoint>::from(AffinePoint::from_encoded_point(
&q1x, &q1y, false, &EncodedPoint::from_affine_coordinates(&q1x, &q1y, false),
)) ))
.ok_or(InternalError::PointError)?; .ok_or(Error::PointError)?;
Ok(p0 + p1) Ok(p0 + p1)
} }
@@ -107,7 +108,7 @@ impl Group for ProjectivePoint {
>( >(
input: I, input: I,
dst: GenericArray<u8, D>, dst: GenericArray<u8, D>,
) -> Result<Self::Scalar, InternalError> ) -> Result<Self::Scalar>
where where
<D as Add<U1>>::Output: ArrayLength<u8>, <D as Add<U1>>::Output: ArrayLength<u8>,
{ {
@@ -132,7 +133,7 @@ impl Group for ProjectivePoint {
let mut result = GenericArray::default(); let mut result = GenericArray::default();
result[..bytes.len()].copy_from_slice(&bytes); result[..bytes.len()].copy_from_slice(&bytes);
Ok(p256_::Scalar::from_bytes_reduced(&result)) Ok(p256_::Scalar::from_be_bytes_reduced(result))
} }
type ElemLen = U33; type ElemLen = U33;
@@ -141,8 +142,8 @@ impl Group for ProjectivePoint {
fn from_scalar_slice_unchecked( fn from_scalar_slice_unchecked(
scalar_bits: &GenericArray<u8, Self::ScalarLen>, scalar_bits: &GenericArray<u8, Self::ScalarLen>,
) -> Result<Self::Scalar, InternalError> { ) -> Result<Self::Scalar> {
Ok(Self::Scalar::from_bytes_reduced(scalar_bits)) Ok(Self::Scalar::from_be_bytes_reduced(*scalar_bits))
} }
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar { fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
@@ -159,8 +160,8 @@ impl Group for ProjectivePoint {
fn from_element_slice_unchecked( fn from_element_slice_unchecked(
element_bits: &GenericArray<u8, Self::ElemLen>, element_bits: &GenericArray<u8, Self::ElemLen>,
) -> Result<Self, InternalError> { ) -> Result<Self> {
Option::from(Self::from_bytes(element_bits)).ok_or(InternalError::PointError) Option::from(Self::from_bytes(element_bits)).ok_or(Error::PointError)
} }
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> { fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
+8 -8
View File
@@ -19,7 +19,7 @@ use generic_array::{ArrayLength, GenericArray};
use rand_core::{CryptoRng, RngCore}; use rand_core::{CryptoRng, RngCore};
use super::Group; use super::Group;
use crate::errors::InternalError; use crate::{Error, Result};
// `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` 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")] #[cfg(feature = "ristretto255")]
@@ -32,7 +32,7 @@ impl Group for RistrettoPoint {
fn hash_to_curve<H: BlockSizeUser + Digest + FixedOutputReset, D: ArrayLength<u8> + Add<U1>>( fn hash_to_curve<H: BlockSizeUser + Digest + FixedOutputReset, D: ArrayLength<u8> + Add<U1>>(
msg: &[u8], msg: &[u8],
dst: GenericArray<u8, D>, dst: GenericArray<u8, D>,
) -> Result<Self, InternalError> ) -> Result<Self>
where where
<D as Add<U1>>::Output: ArrayLength<u8>, <D as Add<U1>>::Output: ArrayLength<u8>,
{ {
@@ -42,7 +42,7 @@ impl Group for RistrettoPoint {
uniform_bytes uniform_bytes
.as_slice() .as_slice()
.try_into() .try_into()
.map_err(|_| InternalError::HashToCurveError)?, .map_err(|_| Error::HashToCurveError)?,
)) ))
} }
@@ -56,7 +56,7 @@ impl Group for RistrettoPoint {
>( >(
input: I, input: I,
dst: GenericArray<u8, D>, dst: GenericArray<u8, D>,
) -> Result<Self::Scalar, InternalError> ) -> Result<Self::Scalar>
where where
<D as Add<U1>>::Output: ArrayLength<u8>, <D as Add<U1>>::Output: ArrayLength<u8>,
{ {
@@ -66,7 +66,7 @@ impl Group for RistrettoPoint {
uniform_bytes uniform_bytes
.as_slice() .as_slice()
.try_into() .try_into()
.map_err(|_| InternalError::HashToCurveError)?, .map_err(|_| Error::HashToCurveError)?,
)) ))
} }
@@ -74,7 +74,7 @@ impl Group for RistrettoPoint {
type ScalarLen = U32; type ScalarLen = U32;
fn from_scalar_slice_unchecked( fn from_scalar_slice_unchecked(
scalar_bits: &GenericArray<u8, Self::ScalarLen>, scalar_bits: &GenericArray<u8, Self::ScalarLen>,
) -> Result<Self::Scalar, InternalError> { ) -> Result<Self::Scalar> {
Ok(Scalar::from_bytes_mod_order(*scalar_bits.as_ref())) Ok(Scalar::from_bytes_mod_order(*scalar_bits.as_ref()))
} }
@@ -104,10 +104,10 @@ impl Group for RistrettoPoint {
type ElemLen = U32; type ElemLen = U32;
fn from_element_slice_unchecked( fn from_element_slice_unchecked(
element_bits: &GenericArray<u8, Self::ElemLen>, element_bits: &GenericArray<u8, Self::ElemLen>,
) -> Result<Self, InternalError> { ) -> Result<Self> {
CompressedRistretto::from_slice(element_bits) CompressedRistretto::from_slice(element_bits)
.decompress() .decompress()
.ok_or(InternalError::PointError) .ok_or(Error::PointError)
} }
// serialization of a group element // serialization of a group element
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> { fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
+6 -7
View File
@@ -7,14 +7,13 @@
//! Includes a series of tests for the group implementations //! Includes a series of tests for the group implementations
use crate::errors::InternalError; use crate::{Error, Group, Result};
use crate::group::Group;
// Test that the deserialization of a group element should throw an error if the // Test that the deserialization of a group element should throw an error if the
// identity element can be deserialized properly // identity element can be deserialized properly
#[test] #[test]
fn test_group_properties() -> Result<(), InternalError> { fn test_group_properties() -> Result<()> {
#[cfg(feature = "ristretto255")] #[cfg(feature = "ristretto255")]
{ {
use curve25519_dalek::ristretto::RistrettoPoint; use curve25519_dalek::ristretto::RistrettoPoint;
@@ -35,19 +34,19 @@ fn test_group_properties() -> Result<(), InternalError> {
} }
// Checks that the identity element cannot be deserialized // Checks that the identity element cannot be deserialized
fn test_identity_element_error<G: Group>() -> Result<(), InternalError> { fn test_identity_element_error<G: Group>() -> Result<()> {
let identity = G::identity(); let identity = G::identity();
let result = G::from_element_slice(&identity.to_arr()); let result = G::from_element_slice(&identity.to_arr());
assert!(matches!(result, Err(InternalError::PointError))); assert!(matches!(result, Err(Error::PointError)));
Ok(()) Ok(())
} }
// Checks that the zero scalar cannot be deserialized // Checks that the zero scalar cannot be deserialized
fn test_zero_scalar_error<G: Group>() -> Result<(), InternalError> { fn test_zero_scalar_error<G: Group>() -> Result<()> {
let zero_scalar = G::scalar_zero(); let zero_scalar = G::scalar_zero();
let result = G::from_scalar_slice(&G::scalar_as_bytes(zero_scalar)); let result = G::from_scalar_slice(&G::scalar_as_bytes(zero_scalar));
assert!(matches!(result, Err(InternalError::ZeroScalarError))); assert!(matches!(result, Err(Error::ZeroScalarError)));
Ok(()) Ok(())
} }
+11 -6
View File
@@ -472,8 +472,8 @@
//! VOPRF evaluations. //! VOPRF evaluations.
//! //!
//! - The `p256` feature enables using p256 as the underlying group for the //! - The `p256` feature enables using p256 as the underlying group for the
//! [Group](group::Group) choice. Note that this is currently an experimental //! [Group] choice and increases the MSRV to 1.56. Note that this is currently
//! feature ⚠️, and is not yet ready for production use. //! an experimental feature ⚠️, and is not yet ready for production use.
//! //!
//! - The `serde` feature, enabled by default, provides convenience functions //! - The `serde` feature, enabled by default, provides convenience functions
//! for serializing and deserializing with [serde](https://serde.rs/). //! for serializing and deserializing with [serde](https://serde.rs/).
@@ -512,8 +512,8 @@ extern crate std;
mod util; mod util;
#[macro_use] #[macro_use]
mod serialization; mod serialization;
pub mod errors; mod error;
pub mod group; mod group;
mod voprf; mod voprf;
#[cfg(test)] #[cfg(test)]
@@ -521,8 +521,13 @@ mod tests;
// Exports // Exports
pub use crate::error::{Error, Result};
pub use crate::group::Group;
#[cfg(feature = "alloc")]
pub use crate::voprf::VerifiableServerBatchEvaluateResult;
pub use crate::voprf::{ pub use crate::voprf::{
BlindedElement, EvaluationElement, NonVerifiableClient, NonVerifiableClientBlindResult, BlindedElement, EvaluationElement, NonVerifiableClient, NonVerifiableClientBlindResult,
NonVerifiableServer, NonVerifiableServerEvaluateResult, VerifiableClient, NonVerifiableServer, NonVerifiableServerEvaluateResult, Proof, VerifiableClient,
VerifiableClientBlindResult, VerifiableServer, VerifiableServerEvaluateResult, VerifiableClientBatchFinalizeResult, VerifiableClientBlindResult, VerifiableServer,
VerifiableServerEvaluateResult,
}; };
+17 -13
View File
@@ -17,12 +17,9 @@ use generic_array::sequence::Concat;
use generic_array::typenum::Sum; use generic_array::typenum::Sum;
use generic_array::{ArrayLength, GenericArray}; use generic_array::{ArrayLength, GenericArray};
use crate::errors::InternalError; use crate::{
use crate::group::Group; BlindedElement, Error, EvaluationElement, Group, NonVerifiableClient, NonVerifiableServer,
use crate::util::deserialize; Proof, Result, VerifiableClient, VerifiableServer,
use crate::voprf::{
BlindedElement, EvaluationElement, NonVerifiableClient, NonVerifiableServer, Proof,
VerifiableClient, VerifiableServer,
}; };
////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////
@@ -37,7 +34,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> NonVerifiableClient
} }
/// Deserialization from bytes /// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> { pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied(); let mut input = input.iter().copied();
let blind = G::from_scalar_slice(&deserialize(&mut input)?)?; let blind = G::from_scalar_slice(&deserialize(&mut input)?)?;
@@ -60,7 +57,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> VerifiableClient<G,
} }
/// Deserialization from bytes /// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> { pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied(); let mut input = input.iter().copied();
let blind = G::from_scalar_slice(&deserialize(&mut input)?)?; let blind = G::from_scalar_slice(&deserialize(&mut input)?)?;
@@ -81,7 +78,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> NonVerifiableServer
} }
/// Deserialization from bytes /// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> { pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied(); let mut input = input.iter().copied();
let sk = G::from_scalar_slice(&deserialize(&mut input)?)?; let sk = G::from_scalar_slice(&deserialize(&mut input)?)?;
@@ -104,7 +101,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> VerifiableServer<G,
} }
/// Deserialization from bytes /// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> { pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied(); let mut input = input.iter().copied();
let sk = G::from_scalar_slice(&deserialize(&mut input)?)?; let sk = G::from_scalar_slice(&deserialize(&mut input)?)?;
@@ -129,7 +126,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> Proof<G, H> {
} }
/// Deserialization from bytes /// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> { pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied(); let mut input = input.iter().copied();
let c_scalar = G::from_scalar_slice(&deserialize(&mut input)?)?; let c_scalar = G::from_scalar_slice(&deserialize(&mut input)?)?;
@@ -150,7 +147,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> BlindedElement<G, H
} }
/// Deserialization from bytes /// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> { pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied(); let mut input = input.iter().copied();
let value = G::from_element_slice(&deserialize(&mut input)?)?; let value = G::from_element_slice(&deserialize(&mut input)?)?;
@@ -169,7 +166,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> EvaluationElement<G
} }
/// Deserialization from bytes /// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> { pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied(); let mut input = input.iter().copied();
let value = G::from_element_slice(&deserialize(&mut input)?)?; let value = G::from_element_slice(&deserialize(&mut input)?)?;
@@ -180,3 +177,10 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> EvaluationElement<G
}) })
} }
} }
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::SizeError)
}
+13 -15
View File
@@ -19,14 +19,12 @@ use ::{
generic_array::{typenum::Sum, ArrayLength}, generic_array::{typenum::Sum, ArrayLength},
}; };
use crate::errors::InternalError;
use crate::group::Group;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use crate::tests::mock_rng::CycleRng; use crate::tests::mock_rng::CycleRng;
use crate::tests::parser::*; use crate::tests::parser::*;
use crate::voprf::{ use crate::{
BlindedElement, EvaluationElement, NonVerifiableClient, NonVerifiableServer, Proof, BlindedElement, EvaluationElement, Group, NonVerifiableClient, NonVerifiableServer, Proof,
VerifiableClient, VerifiableServer, Result, VerifiableClient, VerifiableServer,
}; };
#[derive(Debug)] #[derive(Debug)]
@@ -92,7 +90,7 @@ macro_rules! json_to_test_vectors {
} }
#[test] #[test]
fn test_vectors() -> Result<(), InternalError> { fn test_vectors() -> Result<()> {
let rfc = json::parse(rfc_to_json(super::voprf_vectors::VECTORS).as_str()) let rfc = json::parse(rfc_to_json(super::voprf_vectors::VECTORS).as_str())
.expect("Could not parse json"); .expect("Could not parse json");
@@ -155,7 +153,7 @@ fn test_vectors() -> Result<(), InternalError> {
fn test_base_seed_to_key<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>( fn test_base_seed_to_key<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
tvs: &[VOPRFTestVectorParameters], tvs: &[VOPRFTestVectorParameters],
) -> Result<(), InternalError> { ) -> Result<()> {
for parameters in tvs { for parameters in tvs {
let server = NonVerifiableServer::<G, H>::new_from_seed(&parameters.seed)?; let server = NonVerifiableServer::<G, H>::new_from_seed(&parameters.seed)?;
@@ -169,7 +167,7 @@ fn test_base_seed_to_key<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>
fn test_verifiable_seed_to_key<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>( fn test_verifiable_seed_to_key<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
tvs: &[VOPRFTestVectorParameters], tvs: &[VOPRFTestVectorParameters],
) -> Result<(), InternalError> { ) -> Result<()> {
for parameters in tvs { for parameters in tvs {
let server = VerifiableServer::<G, H>::new_from_seed(&parameters.seed)?; let server = VerifiableServer::<G, H>::new_from_seed(&parameters.seed)?;
@@ -185,7 +183,7 @@ fn test_verifiable_seed_to_key<G: Group, H: BlockSizeUser + Digest + FixedOutput
// Tests input -> blind, blinded_element // Tests input -> blind, blinded_element
fn test_base_blind<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>( fn test_base_blind<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
tvs: &[VOPRFTestVectorParameters], tvs: &[VOPRFTestVectorParameters],
) -> Result<(), InternalError> { ) -> Result<()> {
for parameters in tvs { for parameters in tvs {
for i in 0..parameters.input.len() { for i in 0..parameters.input.len() {
let blind = let blind =
@@ -211,7 +209,7 @@ fn test_base_blind<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
// Tests input -> blind, blinded_element // Tests input -> blind, blinded_element
fn test_verifiable_blind<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>( fn test_verifiable_blind<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
tvs: &[VOPRFTestVectorParameters], tvs: &[VOPRFTestVectorParameters],
) -> Result<(), InternalError> { ) -> Result<()> {
for parameters in tvs { for parameters in tvs {
for i in 0..parameters.input.len() { for i in 0..parameters.input.len() {
let blind = let blind =
@@ -237,7 +235,7 @@ fn test_verifiable_blind<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>
// Tests sksm, blinded_element -> evaluation_element // Tests sksm, blinded_element -> evaluation_element
fn test_base_evaluate<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>( fn test_base_evaluate<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
tvs: &[VOPRFTestVectorParameters], tvs: &[VOPRFTestVectorParameters],
) -> Result<(), InternalError> { ) -> Result<()> {
for parameters in tvs { for parameters in tvs {
for i in 0..parameters.input.len() { for i in 0..parameters.input.len() {
let server = NonVerifiableServer::<G, H>::new_with_key(&parameters.sksm)?; let server = NonVerifiableServer::<G, H>::new_with_key(&parameters.sksm)?;
@@ -258,7 +256,7 @@ fn test_base_evaluate<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn test_verifiable_evaluate<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>( fn test_verifiable_evaluate<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
tvs: &[VOPRFTestVectorParameters], tvs: &[VOPRFTestVectorParameters],
) -> Result<(), InternalError> ) -> Result<()>
where where
G::ScalarLen: Add<G::ScalarLen>, G::ScalarLen: Add<G::ScalarLen>,
Sum<G::ScalarLen, G::ScalarLen>: ArrayLength<u8>, Sum<G::ScalarLen, G::ScalarLen>: ArrayLength<u8>,
@@ -293,7 +291,7 @@ where
// Tests input, blind, evaluation_element -> output // Tests input, blind, evaluation_element -> output
fn test_base_finalize<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>( fn test_base_finalize<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
tvs: &[VOPRFTestVectorParameters], tvs: &[VOPRFTestVectorParameters],
) -> Result<(), InternalError> { ) -> Result<()> {
for parameters in tvs { for parameters in tvs {
for i in 0..parameters.input.len() { for i in 0..parameters.input.len() {
let client = NonVerifiableClient::<G, H>::from_blind(G::from_scalar_slice( let client = NonVerifiableClient::<G, H>::from_blind(G::from_scalar_slice(
@@ -314,7 +312,7 @@ fn test_base_finalize<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
fn test_verifiable_finalize<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>( fn test_verifiable_finalize<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
tvs: &[VOPRFTestVectorParameters], tvs: &[VOPRFTestVectorParameters],
) -> Result<(), InternalError> { ) -> Result<()> {
for parameters in tvs { for parameters in tvs {
let mut clients = vec![]; let mut clients = vec![];
for i in 0..parameters.input.len() { for i in 0..parameters.input.len() {
@@ -346,7 +344,7 @@ fn test_verifiable_finalize<G: Group, H: BlockSizeUser + Digest + FixedOutputRes
parameters.output, parameters.output,
batch_result batch_result
.map(|arr| arr.map(|message| message.to_vec())) .map(|arr| arr.map(|message| message.to_vec()))
.collect::<Result<Vec<_>, _>>()? .collect::<Result<Vec<_>>>()?
); );
} }
Ok(()) Ok(())
+25 -41
View File
@@ -12,32 +12,26 @@ use core::array::IntoIter;
use generic_array::typenum::U0; use generic_array::typenum::U0;
use generic_array::{ArrayLength, GenericArray}; use generic_array::{ArrayLength, GenericArray};
use crate::errors::InternalError; use crate::{Error, Result};
// Corresponds to the I2OSP() function from RFC8017 // Corresponds to the I2OSP() function from RFC8017
pub(crate) fn i2osp<L: ArrayLength<u8>>( pub(crate) fn i2osp<L: ArrayLength<u8>>(input: usize) -> Result<GenericArray<u8, L>> {
input: usize,
) -> Result<GenericArray<u8, L>, InternalError> {
const SIZEOF_USIZE: usize = core::mem::size_of::<usize>(); const SIZEOF_USIZE: usize = core::mem::size_of::<usize>();
// Check if input >= 256^length // Make sure input fits in output.
if (SIZEOF_USIZE as u32 - input.leading_zeros() / 8) > L::U32 { if (SIZEOF_USIZE as u32 - input.leading_zeros() / 8) > L::U32 {
return Err(InternalError::SerializationError); return Err(Error::SerializationError);
}
if L::USIZE <= SIZEOF_USIZE {
return Ok(GenericArray::clone_from_slice(
&input.to_be_bytes()[SIZEOF_USIZE - L::USIZE..],
));
} }
let mut output = GenericArray::default(); let mut output = GenericArray::default();
output[L::USIZE - SIZEOF_USIZE..].copy_from_slice(&input.to_be_bytes()); output[L::USIZE.saturating_sub(SIZEOF_USIZE)..]
.copy_from_slice(&input.to_be_bytes()[SIZEOF_USIZE.saturating_sub(L::USIZE)..]);
Ok(output) Ok(output)
} }
/// Simplifies handling of [`serialize()`] output and implements [`Iterator`]. /// Computes `I2OSP(len(input), max_bytes) || input` and helps hold output
pub(crate) struct Serialized<'a, L1: ArrayLength<u8>, L2: ArrayLength<u8>> { /// without allocation.
pub(crate) struct Serialize<'a, L1: ArrayLength<u8>, L2: ArrayLength<u8> = U0> {
octet: GenericArray<u8, L1>, octet: GenericArray<u8, L1>,
input: Input<'a, L2>, input: Input<'a, L2>,
} }
@@ -47,7 +41,7 @@ enum Input<'a, L: ArrayLength<u8>> {
Borrowed(&'a [u8]), Borrowed(&'a [u8]),
} }
impl<'a, L1: ArrayLength<u8>, L2: ArrayLength<u8>> IntoIterator for &'a Serialized<'a, L1, L2> { impl<'a, L1: ArrayLength<u8>, L2: ArrayLength<u8>> IntoIterator for &'a Serialize<'a, L1, L2> {
type Item = &'a [u8]; type Item = &'a [u8];
type IntoIter = IntoIter<&'a [u8], 2>; type IntoIter = IntoIter<&'a [u8], 2>;
@@ -65,31 +59,21 @@ impl<'a, L1: ArrayLength<u8>, L2: ArrayLength<u8>> IntoIterator for &'a Serializ
} }
} }
// Computes I2OSP(len(input), max_bytes) || input impl<'a, L1: ArrayLength<u8>, L2: ArrayLength<u8>> Serialize<'a, L1, L2> {
pub(crate) fn serialize<L: ArrayLength<u8>>( // Variation of `serialize` that takes a borrowed `input.
input: &[u8], pub(crate) fn from(input: &[u8]) -> Result<Serialize<L1>> {
) -> Result<Serialized<L, U0>, InternalError> { Ok(Serialize {
Ok(Serialized { octet: i2osp::<L1>(input.len())?,
octet: i2osp::<L>(input.len())?, input: Input::Borrowed(input),
input: Input::Borrowed(input), })
}) }
}
// Variation of `serialize` that takes an owned `input` pub(crate) fn from_owned(input: GenericArray<u8, L2>) -> Result<Serialize<'static, L1, L2>> {
pub(crate) fn serialize_owned<L1: ArrayLength<u8>, L2: ArrayLength<u8>>( Ok(Serialize {
input: GenericArray<u8, L2>, octet: i2osp::<L1>(input.len())?,
) -> Result<Serialized<'static, L1, L2>, InternalError> { input: Input::Owned(input),
Ok(Serialized { })
octet: i2osp::<L1>(input.len())?, }
input: Input::Owned(input),
})
}
pub(crate) fn deserialize<L: ArrayLength<u8>>(
input: &mut impl Iterator<Item = u8>,
) -> Result<GenericArray<u8, L>, InternalError> {
let input = input.by_ref().take(L::USIZE);
GenericArray::from_exact_iter(input).ok_or(InternalError::SizeError)
} }
macro_rules! chain_name { macro_rules! chain_name {
@@ -135,7 +119,7 @@ mod unit_tests {
use proptest::prelude::*; use proptest::prelude::*;
use super::*; use super::*;
use crate::voprf::{ use crate::{
BlindedElement, EvaluationElement, NonVerifiableClient, NonVerifiableServer, Proof, BlindedElement, EvaluationElement, NonVerifiableClient, NonVerifiableServer, Proof,
VerifiableClient, VerifiableServer, VerifiableClient, VerifiableServer,
}; };
+64 -69
View File
@@ -22,9 +22,8 @@ use generic_array::GenericArray;
use rand_core::{CryptoRng, RngCore}; use rand_core::{CryptoRng, RngCore};
use subtle::ConstantTimeEq; use subtle::ConstantTimeEq;
use crate::errors::InternalError; use crate::util::{i2osp, Serialize};
use crate::group::Group; use crate::{Error, Group, Result};
use crate::util::{i2osp, serialize, serialize_owned};
/////////////// ///////////////
// Constants // // Constants //
@@ -199,7 +198,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> NonVerifiableClient
pub fn blind<R: RngCore + CryptoRng>( pub fn blind<R: RngCore + CryptoRng>(
input: &[u8], input: &[u8],
blinding_factor_rng: &mut R, blinding_factor_rng: &mut R,
) -> Result<NonVerifiableClientBlindResult<G, H>, InternalError> { ) -> Result<NonVerifiableClientBlindResult<G, H>> {
let (blind, blinded_element) = blind::<G, H, _>(input, blinding_factor_rng, Mode::Base)?; let (blind, blinded_element) = blind::<G, H, _>(input, blinding_factor_rng, Mode::Base)?;
Ok(NonVerifiableClientBlindResult { Ok(NonVerifiableClientBlindResult {
state: Self { state: Self {
@@ -225,7 +224,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> NonVerifiableClient
pub fn deterministic_blind_unchecked( pub fn deterministic_blind_unchecked(
input: &[u8], input: &[u8],
blind: G::Scalar, blind: G::Scalar,
) -> Result<NonVerifiableClientBlindResult<G, H>, InternalError> { ) -> Result<NonVerifiableClientBlindResult<G, H>> {
let blinded_element = deterministic_blind_unchecked::<G, H>(input, &blind, Mode::Base)?; let blinded_element = deterministic_blind_unchecked::<G, H>(input, &blind, Mode::Base)?;
Ok(NonVerifiableClientBlindResult { Ok(NonVerifiableClientBlindResult {
state: Self { state: Self {
@@ -246,7 +245,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> NonVerifiableClient
input: &[u8], input: &[u8],
evaluation_element: &EvaluationElement<G, H>, evaluation_element: &EvaluationElement<G, H>,
metadata: Option<&[u8]>, metadata: Option<&[u8]>,
) -> Result<GenericArray<u8, H::OutputSize>, InternalError> { ) -> Result<GenericArray<u8, H::OutputSize>> {
let unblinded_element = evaluation_element.value * &G::scalar_invert(&self.blind); let unblinded_element = evaluation_element.value * &G::scalar_invert(&self.blind);
let mut outputs = finalize_after_unblind::<G, H, _, _>( let mut outputs = finalize_after_unblind::<G, H, _, _>(
Some((input, unblinded_element)).into_iter(), Some((input, unblinded_element)).into_iter(),
@@ -278,7 +277,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> VerifiableClient<G,
pub fn blind<R: RngCore + CryptoRng>( pub fn blind<R: RngCore + CryptoRng>(
input: &[u8], input: &[u8],
blinding_factor_rng: &mut R, blinding_factor_rng: &mut R,
) -> Result<VerifiableClientBlindResult<G, H>, InternalError> { ) -> Result<VerifiableClientBlindResult<G, H>> {
let (blind, blinded_element) = let (blind, blinded_element) =
blind::<G, H, _>(input, blinding_factor_rng, Mode::Verifiable)?; blind::<G, H, _>(input, blinding_factor_rng, Mode::Verifiable)?;
Ok(VerifiableClientBlindResult { Ok(VerifiableClientBlindResult {
@@ -306,7 +305,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> VerifiableClient<G,
pub fn deterministic_blind_unchecked( pub fn deterministic_blind_unchecked(
input: &[u8], input: &[u8],
blind: G::Scalar, blind: G::Scalar,
) -> Result<VerifiableClientBlindResult<G, H>, InternalError> { ) -> Result<VerifiableClientBlindResult<G, H>> {
let blinded_element = let blinded_element =
deterministic_blind_unchecked::<G, H>(input, &blind, Mode::Verifiable)?; deterministic_blind_unchecked::<G, H>(input, &blind, Mode::Verifiable)?;
Ok(VerifiableClientBlindResult { Ok(VerifiableClientBlindResult {
@@ -331,7 +330,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> VerifiableClient<G,
proof: &Proof<G, H>, proof: &Proof<G, H>,
pk: G, pk: G,
metadata: Option<&[u8]>, metadata: Option<&[u8]>,
) -> Result<GenericArray<u8, H::OutputSize>, InternalError> { ) -> Result<GenericArray<u8, H::OutputSize>> {
// `core::array::from_ref` needs a MSRV of 1.53 // `core::array::from_ref` needs a MSRV of 1.53
let inputs: &[&[u8]; 1] = core::slice::from_ref(&input).try_into().unwrap(); let inputs: &[&[u8]; 1] = core::slice::from_ref(&input).try_into().unwrap();
let clients: &[Self; 1] = core::slice::from_ref(self).try_into().unwrap(); let clients: &[Self; 1] = core::slice::from_ref(self).try_into().unwrap();
@@ -353,7 +352,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> VerifiableClient<G,
proof: &Proof<G, H>, proof: &Proof<G, H>,
pk: G, pk: G,
metadata: Option<&'a [u8]>, metadata: Option<&'a [u8]>,
) -> Result<VerifiableClientBatchFinalizeResult<'a, G, H, I, II, IC, IM>, InternalError> ) -> Result<VerifiableClientBatchFinalizeResult<'a, G, H, I, II, IC, IM>>
where where
G: 'a, G: 'a,
H: 'a, H: 'a,
@@ -397,7 +396,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> VerifiableClient<G,
impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> NonVerifiableServer<G, H> { impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> NonVerifiableServer<G, H> {
/// Produces a new instance of a [NonVerifiableServer] using a supplied RNG /// Produces a new instance of a [NonVerifiableServer] using a supplied RNG
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, InternalError> { pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self> {
let mut seed = GenericArray::<_, H::OutputSize>::default(); let mut seed = GenericArray::<_, H::OutputSize>::default();
rng.fill_bytes(&mut seed); rng.fill_bytes(&mut seed);
Self::new_from_seed(&seed) Self::new_from_seed(&seed)
@@ -405,7 +404,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> NonVerifiableServer
/// Produces a new instance of a [NonVerifiableServer] using a supplied set /// Produces a new instance of a [NonVerifiableServer] using a supplied set
/// of bytes to represent the server's private key /// of bytes to represent the server's private key
pub fn new_with_key(private_key_bytes: &[u8]) -> Result<Self, InternalError> { pub fn new_with_key(private_key_bytes: &[u8]) -> Result<Self> {
let sk = G::from_scalar_slice(private_key_bytes)?; let sk = G::from_scalar_slice(private_key_bytes)?;
Ok(Self { Ok(Self {
sk, sk,
@@ -417,7 +416,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> NonVerifiableServer
/// of bytes which are used as a seed to derive the server's private key. /// of bytes which are used as a seed to derive the server's private key.
/// ///
/// Corresponds to DeriveKeyPair() function from the VOPRF specification. /// Corresponds to DeriveKeyPair() function from the VOPRF specification.
pub fn new_from_seed(seed: &[u8]) -> Result<Self, InternalError> { pub fn new_from_seed(seed: &[u8]) -> Result<Self> {
let dst = let dst =
GenericArray::from(STR_HASH_TO_SCALAR).concat(get_context_string::<G>(Mode::Base)?); GenericArray::from(STR_HASH_TO_SCALAR).concat(get_context_string::<G>(Mode::Base)?);
let sk = G::hash_to_scalar::<H, _, _>(Some(seed), dst)?; let sk = G::hash_to_scalar::<H, _, _>(Some(seed), dst)?;
@@ -440,12 +439,12 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> NonVerifiableServer
&self, &self,
blinded_element: &BlindedElement<G, H>, blinded_element: &BlindedElement<G, H>,
metadata: Option<&[u8]>, metadata: Option<&[u8]>,
) -> Result<NonVerifiableServerEvaluateResult<G, H>, InternalError> { ) -> Result<NonVerifiableServerEvaluateResult<G, H>> {
chain!( chain!(
context, context,
STR_CONTEXT => |x| Some(x.as_ref()), STR_CONTEXT => |x| Some(x.as_ref()),
get_context_string::<G>(Mode::Base)? => |x| Some(x.as_slice()), get_context_string::<G>(Mode::Base)? => |x| Some(x.as_slice()),
serialize::<U2>(metadata.unwrap_or_default())?, Serialize::<U2>::from(metadata.unwrap_or_default())?,
); );
let dst = let dst =
GenericArray::from(STR_HASH_TO_SCALAR).concat(get_context_string::<G>(Mode::Base)?); GenericArray::from(STR_HASH_TO_SCALAR).concat(get_context_string::<G>(Mode::Base)?);
@@ -463,7 +462,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> NonVerifiableServer
impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> VerifiableServer<G, H> { impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> VerifiableServer<G, H> {
/// Produces a new instance of a [VerifiableServer] using a supplied RNG /// Produces a new instance of a [VerifiableServer] using a supplied RNG
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, InternalError> { pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self> {
let mut seed = GenericArray::<_, H::OutputSize>::default(); let mut seed = GenericArray::<_, H::OutputSize>::default();
rng.fill_bytes(&mut seed); rng.fill_bytes(&mut seed);
Self::new_from_seed(&seed) Self::new_from_seed(&seed)
@@ -471,7 +470,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> VerifiableServer<G,
/// Produces a new instance of a [VerifiableServer] using a supplied set of /// Produces a new instance of a [VerifiableServer] using a supplied set of
/// bytes to represent the server's private key /// bytes to represent the server's private key
pub fn new_with_key(key: &[u8]) -> Result<Self, InternalError> { pub fn new_with_key(key: &[u8]) -> Result<Self> {
let sk = G::from_scalar_slice(key)?; let sk = G::from_scalar_slice(key)?;
let pk = G::base_point() * &sk; let pk = G::base_point() * &sk;
Ok(Self { Ok(Self {
@@ -485,7 +484,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> VerifiableServer<G,
/// bytes which are used as a seed to derive the server's private key. /// bytes which are used as a seed to derive the server's private key.
/// ///
/// Corresponds to DeriveKeyPair() function from the VOPRF specification. /// Corresponds to DeriveKeyPair() function from the VOPRF specification.
pub fn new_from_seed(seed: &[u8]) -> Result<Self, InternalError> { pub fn new_from_seed(seed: &[u8]) -> Result<Self> {
let dst = GenericArray::from(STR_HASH_TO_SCALAR) let dst = GenericArray::from(STR_HASH_TO_SCALAR)
.concat(get_context_string::<G>(Mode::Verifiable)?); .concat(get_context_string::<G>(Mode::Verifiable)?);
let sk = G::hash_to_scalar::<H, _, _>(Some(seed), dst)?; let sk = G::hash_to_scalar::<H, _, _>(Some(seed), dst)?;
@@ -511,7 +510,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> VerifiableServer<G,
rng: &mut R, rng: &mut R,
blinded_element: &BlindedElement<G, H>, blinded_element: &BlindedElement<G, H>,
metadata: Option<&[u8]>, metadata: Option<&[u8]>,
) -> Result<VerifiableServerEvaluateResult<G, H>, InternalError> { ) -> Result<VerifiableServerEvaluateResult<G, H>> {
let (mut evaluation_elements, t) = let (mut evaluation_elements, t) =
self.batch_evaluate_1(Some(blinded_element.copy()).into_iter(), metadata)?; self.batch_evaluate_1(Some(blinded_element.copy()).into_iter(), metadata)?;
@@ -539,7 +538,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> VerifiableServer<G,
rng: &mut R, rng: &mut R,
blinded_elements: &'a I, blinded_elements: &'a I,
metadata: Option<&[u8]>, metadata: Option<&[u8]>,
) -> Result<VerifiableServerBatchEvaluateResult<G, H>, InternalError> ) -> Result<VerifiableServerBatchEvaluateResult<G, H>>
where where
G: 'a, G: 'a,
H: 'a, H: 'a,
@@ -570,20 +569,17 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> VerifiableServer<G,
&self, &self,
blinded_elements: I, blinded_elements: I,
metadata: Option<&[u8]>, metadata: Option<&[u8]>,
) -> Result< ) -> Result<(
( impl Iterator<Item = EvaluationElement<G, H>> + ExactSizeIterator,
impl Iterator<Item = EvaluationElement<G, H>> + ExactSizeIterator, G::Scalar,
G::Scalar, )>
),
InternalError,
>
where where
I: Iterator<Item = BlindedElement<G, H>> + ExactSizeIterator, I: Iterator<Item = BlindedElement<G, H>> + ExactSizeIterator,
{ {
chain!(context, chain!(context,
STR_CONTEXT => |x| Some(x.as_ref()), STR_CONTEXT => |x| Some(x.as_ref()),
get_context_string::<G>(Mode::Verifiable)? => |x| Some(x.as_slice()), get_context_string::<G>(Mode::Verifiable)? => |x| Some(x.as_slice()),
serialize::<U2>(metadata.unwrap_or_default())?, Serialize::<U2>::from(metadata.unwrap_or_default())?,
); );
let dst = GenericArray::from(STR_HASH_TO_SCALAR) let dst = GenericArray::from(STR_HASH_TO_SCALAR)
.concat(get_context_string::<G>(Mode::Verifiable)?); .concat(get_context_string::<G>(Mode::Verifiable)?);
@@ -604,7 +600,7 @@ impl<G: Group, H: BlockSizeUser + Digest + FixedOutputReset> VerifiableServer<G,
blinded_elements: IB, blinded_elements: IB,
evaluation_elements: IE, evaluation_elements: IE,
t: G::Scalar, t: G::Scalar,
) -> Result<Proof<G, H>, InternalError> ) -> Result<Proof<G, H>>
where where
IB: Iterator<Item = BlindedElement<G, H>> + ExactSizeIterator, IB: Iterator<Item = BlindedElement<G, H>> + ExactSizeIterator,
IE: Iterator<Item = EvaluationElement<G, H>> + ExactSizeIterator, IE: Iterator<Item = EvaluationElement<G, H>> + ExactSizeIterator,
@@ -649,6 +645,7 @@ pub struct VerifiableClientBlindResult<G: Group, H: BlockSizeUser + Digest + Fix
pub message: BlindedElement<G, H>, pub message: BlindedElement<G, H>,
} }
/// Concrete return type for [`VerifiableClient::batch_finalize`].
pub type VerifiableClientBatchFinalizeResult<'a, G, H, I, II, IC, IM> = FinalizeAfterUnblindResult< pub type VerifiableClientBatchFinalizeResult<'a, G, H, I, II, IC, IM> = FinalizeAfterUnblindResult<
'a, 'a,
G, G,
@@ -747,7 +744,7 @@ fn blind<G: Group, H: BlockSizeUser + Digest + FixedOutputReset, R: RngCore + Cr
input: &[u8], input: &[u8],
blinding_factor_rng: &mut R, blinding_factor_rng: &mut R,
mode: Mode, mode: Mode,
) -> Result<(G::Scalar, G), InternalError> { ) -> Result<(G::Scalar, G)> {
// Choose a random scalar that must be non-zero // Choose a random scalar that must be non-zero
let blind = G::random_nonzero_scalar(blinding_factor_rng); let blind = G::random_nonzero_scalar(blinding_factor_rng);
let blinded_element = deterministic_blind_unchecked::<G, H>(input, &blind, mode)?; let blinded_element = deterministic_blind_unchecked::<G, H>(input, &blind, mode)?;
@@ -761,7 +758,7 @@ fn deterministic_blind_unchecked<G: Group, H: BlockSizeUser + Digest + FixedOutp
input: &[u8], input: &[u8],
blind: &G::Scalar, blind: &G::Scalar,
mode: Mode, mode: Mode,
) -> Result<G, InternalError> { ) -> Result<G> {
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(get_context_string::<G>(mode)?); let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(get_context_string::<G>(mode)?);
let hashed_point = G::hash_to_curve::<H, _>(input, dst)?; let hashed_point = G::hash_to_curve::<H, _>(input, dst)?;
Ok(hashed_point * blind) Ok(hashed_point * blind)
@@ -788,7 +785,7 @@ fn verifiable_unblind<
pk: G, pk: G,
proof: &Proof<G, H>, proof: &Proof<G, H>,
info: &[u8], info: &[u8],
) -> Result<VerifiableUnblindResult<'a, G, H, IC, IM>, InternalError> ) -> Result<VerifiableUnblindResult<'a, G, H, IC, IM>>
where where
&'a IC: 'a + IntoIterator<Item = &'a VerifiableClient<G, H>>, &'a IC: 'a + IntoIterator<Item = &'a VerifiableClient<G, H>>,
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator, <&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
@@ -798,7 +795,7 @@ where
chain!(context, chain!(context,
STR_CONTEXT => |x| Some(x.as_ref()), STR_CONTEXT => |x| Some(x.as_ref()),
get_context_string::<G>(Mode::Verifiable)? => |x| Some(x.as_slice()), get_context_string::<G>(Mode::Verifiable)? => |x| Some(x.as_slice()),
serialize::<U2>(info)?, Serialize::<U2>::from(info)?,
); );
let dst = let dst =
@@ -838,7 +835,7 @@ fn generate_proof<
b: G, b: G,
cs: impl Iterator<Item = EvaluationElement<G, H>> + ExactSizeIterator, cs: impl Iterator<Item = EvaluationElement<G, H>> + ExactSizeIterator,
ds: impl Iterator<Item = BlindedElement<G, H>> + ExactSizeIterator, ds: impl Iterator<Item = BlindedElement<G, H>> + ExactSizeIterator,
) -> Result<Proof<G, H>, InternalError> { ) -> Result<Proof<G, H>> {
let (m, z) = compute_composites(Some(k), b, cs, ds)?; let (m, z) = compute_composites(Some(k), b, cs, ds)?;
let r = G::random_nonzero_scalar(rng); let r = G::random_nonzero_scalar(rng);
@@ -849,12 +846,12 @@ fn generate_proof<
GenericArray::from(STR_CHALLENGE).concat(get_context_string::<G>(Mode::Verifiable)?); GenericArray::from(STR_CHALLENGE).concat(get_context_string::<G>(Mode::Verifiable)?);
chain!( chain!(
h2_input, h2_input,
serialize_owned::<U2, _>(b.to_arr())?, Serialize::<U2, _>::from_owned(b.to_arr())?,
serialize_owned::<U2, _>(m.to_arr())?, Serialize::<U2, _>::from_owned(m.to_arr())?,
serialize_owned::<U2, _>(z.to_arr())?, Serialize::<U2, _>::from_owned(z.to_arr())?,
serialize_owned::<U2, _>(t2.to_arr())?, Serialize::<U2, _>::from_owned(t2.to_arr())?,
serialize_owned::<U2, _>(t3.to_arr())?, Serialize::<U2, _>::from_owned(t3.to_arr())?,
serialize_owned::<U2, _>(challenge_dst)?, Serialize::<U2, _>::from_owned(challenge_dst)?,
); );
let hash_to_scalar_dst = let hash_to_scalar_dst =
@@ -877,7 +874,7 @@ fn verify_proof<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
cs: impl Iterator<Item = EvaluationElement<G, H>> + ExactSizeIterator, cs: impl Iterator<Item = EvaluationElement<G, H>> + ExactSizeIterator,
ds: impl Iterator<Item = BlindedElement<G, H>> + ExactSizeIterator, ds: impl Iterator<Item = BlindedElement<G, H>> + ExactSizeIterator,
proof: &Proof<G, H>, proof: &Proof<G, H>,
) -> Result<(), InternalError> { ) -> Result<()> {
let (m, z) = compute_composites(None, b, cs, ds)?; let (m, z) = compute_composites(None, b, cs, ds)?;
let t2 = (a * &proof.s_scalar) + &(b * &proof.c_scalar); let t2 = (a * &proof.s_scalar) + &(b * &proof.c_scalar);
let t3 = (m * &proof.s_scalar) + &(z * &proof.c_scalar); let t3 = (m * &proof.s_scalar) + &(z * &proof.c_scalar);
@@ -886,12 +883,12 @@ fn verify_proof<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
GenericArray::from(STR_CHALLENGE).concat(get_context_string::<G>(Mode::Verifiable)?); GenericArray::from(STR_CHALLENGE).concat(get_context_string::<G>(Mode::Verifiable)?);
chain!( chain!(
h2_input, h2_input,
serialize_owned::<U2, _>(b.to_arr())?, Serialize::<U2, _>::from_owned(b.to_arr())?,
serialize_owned::<U2, _>(m.to_arr())?, Serialize::<U2, _>::from_owned(m.to_arr())?,
serialize_owned::<U2, _>(z.to_arr())?, Serialize::<U2, _>::from_owned(z.to_arr())?,
serialize_owned::<U2, _>(t2.to_arr())?, Serialize::<U2, _>::from_owned(t2.to_arr())?,
serialize_owned::<U2, _>(t3.to_arr())?, Serialize::<U2, _>::from_owned(t3.to_arr())?,
serialize_owned::<U2, _>(challenge_dst)?, Serialize::<U2, _>::from_owned(challenge_dst)?,
); );
let hash_to_scalar_dst = let hash_to_scalar_dst =
@@ -900,16 +897,14 @@ fn verify_proof<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
match c.ct_eq(&proof.c_scalar).into() { match c.ct_eq(&proof.c_scalar).into() {
true => Ok(()), true => Ok(()),
false => Err(InternalError::ProofVerificationError), false => Err(Error::ProofVerificationError),
} }
} }
#[allow(type_alias_bounds)] #[allow(type_alias_bounds)]
type FinalizeAfterUnblindResult<'a, G, H: Digest, I, IE> = Map< type FinalizeAfterUnblindResult<'a, G, H: Digest, I, IE> = Map<
Zip<IE, Repeat<(&'a [u8], GenericArray<u8, U20>)>>, Zip<IE, Repeat<(&'a [u8], GenericArray<u8, U20>)>>,
fn( fn(((I, G), (&'a [u8], GenericArray<u8, U20>))) -> Result<GenericArray<u8, H::OutputSize>>,
((I, G), (&'a [u8], GenericArray<u8, U20>)),
) -> Result<GenericArray<u8, H::OutputSize>, InternalError>,
>; >;
fn finalize_after_unblind< fn finalize_after_unblind<
@@ -922,7 +917,7 @@ fn finalize_after_unblind<
inputs_and_unblinded_elements: IE, inputs_and_unblinded_elements: IE,
info: &'a [u8], info: &'a [u8],
mode: Mode, mode: Mode,
) -> Result<FinalizeAfterUnblindResult<G, H, I, IE>, InternalError> { ) -> Result<FinalizeAfterUnblindResult<G, H, I, IE>> {
let finalize_dst = GenericArray::from(STR_FINALIZE).concat(get_context_string::<G>(mode)?); let finalize_dst = GenericArray::from(STR_FINALIZE).concat(get_context_string::<G>(mode)?);
Ok(inputs_and_unblinded_elements Ok(inputs_and_unblinded_elements
@@ -932,10 +927,10 @@ fn finalize_after_unblind<
.map(|((input, unblinded_element), (info, finalize_dst))| { .map(|((input, unblinded_element), (info, finalize_dst))| {
chain!( chain!(
hash_input, hash_input,
serialize::<U2>(input.as_ref())?, Serialize::<U2>::from(input.as_ref())?,
serialize::<U2>(info)?, Serialize::<U2>::from(info)?,
serialize_owned::<U2, _>(unblinded_element.to_arr())?, Serialize::<U2, _>::from_owned(unblinded_element.to_arr())?,
serialize_owned::<U2, _>(finalize_dst)?, Serialize::<U2, _>::from_owned(finalize_dst)?,
); );
Ok(hash_input Ok(hash_input
@@ -949,9 +944,9 @@ fn compute_composites<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
b: G, b: G,
c_slice: impl Iterator<Item = EvaluationElement<G, H>> + ExactSizeIterator, c_slice: impl Iterator<Item = EvaluationElement<G, H>> + ExactSizeIterator,
d_slice: impl Iterator<Item = BlindedElement<G, H>> + ExactSizeIterator, d_slice: impl Iterator<Item = BlindedElement<G, H>> + ExactSizeIterator,
) -> Result<(G, G), InternalError> { ) -> Result<(G, G)> {
if c_slice.len() != d_slice.len() { if c_slice.len() != d_slice.len() {
return Err(InternalError::MismatchedLengthsForCompositeInputs); return Err(Error::MismatchedLengthsForCompositeInputs);
} }
let seed_dst = GenericArray::from(STR_SEED).concat(get_context_string::<G>(Mode::Verifiable)?); let seed_dst = GenericArray::from(STR_SEED).concat(get_context_string::<G>(Mode::Verifiable)?);
@@ -960,8 +955,8 @@ fn compute_composites<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
chain!( chain!(
h1_input, h1_input,
serialize_owned::<U2, _>(b.to_arr())?, Serialize::<U2, _>::from_owned(b.to_arr())?,
serialize_owned::<U2, _>(seed_dst)?, Serialize::<U2, _>::from_owned(seed_dst)?,
); );
let seed = h1_input let seed = h1_input
.fold(H::new(), |h, bytes| h.chain_update(bytes)) .fold(H::new(), |h, bytes| h.chain_update(bytes))
@@ -972,11 +967,11 @@ fn compute_composites<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
for (i, (c, d)) in c_slice.zip(d_slice).enumerate() { for (i, (c, d)) in c_slice.zip(d_slice).enumerate() {
chain!(h2_input, chain!(h2_input,
serialize_owned::<U2, _>(seed.clone())?, Serialize::<U2, _>::from_owned(seed.clone())?,
i2osp::<U2>(i)? => |x| Some(x.as_slice()), i2osp::<U2>(i)? => |x| Some(x.as_slice()),
serialize_owned::<U2, _>(c.value.to_arr())?, Serialize::<U2, _>::from_owned(c.value.to_arr())?,
serialize_owned::<U2, _>(d.value.to_arr())?, Serialize::<U2, _>::from_owned(d.value.to_arr())?,
serialize_owned::<U2, _>(composite_dst)?, Serialize::<U2, _>::from_owned(composite_dst)?,
); );
let dst = GenericArray::from(STR_HASH_TO_SCALAR) let dst = GenericArray::from(STR_HASH_TO_SCALAR)
.concat(get_context_string::<G>(Mode::Verifiable)?); .concat(get_context_string::<G>(Mode::Verifiable)?);
@@ -998,7 +993,7 @@ fn compute_composites<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
/// Generates the contextString parameter as defined in /// Generates the contextString parameter as defined in
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html> /// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html>
fn get_context_string<G: Group>(mode: Mode) -> Result<GenericArray<u8, U11>, InternalError> { fn get_context_string<G: Group>(mode: Mode) -> Result<GenericArray<u8, U11>> {
Ok(GenericArray::from(STR_VOPRF) Ok(GenericArray::from(STR_VOPRF)
.concat(i2osp::<U1>(mode as usize)?) .concat(i2osp::<U1>(mode as usize)?)
.concat(i2osp::<U2>(G::SUITE_ID)?)) .concat(i2osp::<U2>(G::SUITE_ID)?))
@@ -1021,7 +1016,7 @@ mod tests {
use ::{alloc::vec, alloc::vec::Vec}; use ::{alloc::vec, alloc::vec::Vec};
use super::*; use super::*;
use crate::group::Group; use crate::Group;
fn prf<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>( fn prf<G: Group, H: BlockSizeUser + Digest + FixedOutputReset>(
input: &[u8], input: &[u8],
@@ -1036,7 +1031,7 @@ mod tests {
chain!(context, chain!(context,
STR_CONTEXT => |x| Some(x.as_ref()), STR_CONTEXT => |x| Some(x.as_ref()),
get_context_string::<G>(mode).unwrap() => |x| Some(x.as_slice()), get_context_string::<G>(mode).unwrap() => |x| Some(x.as_slice()),
serialize::<U2>(info).unwrap(), Serialize::<U2>::from(info).unwrap(),
); );
let dst = let dst =
@@ -1145,7 +1140,7 @@ mod tests {
Some(info), Some(info),
) )
.unwrap() .unwrap()
.collect::<Result<Vec<_>, _>>() .collect::<Result<Vec<_>>>()
.unwrap(); .unwrap();
let mut res2 = vec![]; let mut res2 = vec![];
for input in inputs.iter().take(num_iterations) { for input in inputs.iter().take(num_iterations) {
@@ -1305,7 +1300,7 @@ mod tests {
} }
#[test] #[test]
fn test_functionality() -> Result<(), InternalError> { fn test_functionality() -> Result<()> {
#[cfg(feature = "ristretto255")] #[cfg(feature = "ristretto255")]
{ {
use curve25519_dalek::ristretto::RistrettoPoint; use curve25519_dalek::ristretto::RistrettoPoint;