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