From b1b315f23ce41b4e3cb9ff4df449a689d05f5240 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Tue, 21 Dec 2021 20:17:02 +0100 Subject: [PATCH] General improvements (#34) * Minor improvements * Fix `Debug` implementation * Fix de-serialization * Fix accidental usage of nightly * Fix MSRV warning * Replace macro with derive-where * Add `rust-version` into `Cargo.toml` * Move Serde trait implementation macro to `serialization` module * Add ability to test without a Ristretto backend * Improve docs * Fix testing multiple backends together * Implement `Ord` and `PartialOrd` * `no_std` by default * Remove unnecessary `doc_cfg` * Remove dev-dependency on self * Implement `Ord` and `PartialOrd` for `InternalError` * Remove base64 encoding for serde * Only take references * Remove unnecessary qualifications from super-trait times --- .github/workflows/main.yml | 11 +- Cargo.toml | 8 +- src/errors.rs | 2 +- src/group/expand.rs | 4 +- src/group/mod.rs | 11 +- src/group/ristretto.rs | 183 ++++++++-------- src/group/tests.rs | 8 +- src/impls.rs | 153 ------------- src/lib.rs | 41 ++-- src/serialization.rs | 83 +++++++- src/tests/parser.rs | 8 +- src/tests/voprf_test_vectors.rs | 63 +++--- src/util.rs | 55 ++++- src/voprf.rs | 366 +++++++++++++++++--------------- 14 files changed, 481 insertions(+), 515 deletions(-) delete mode 100644 src/impls.rs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e7f9585..3de270a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,10 +37,13 @@ jobs: backend_feature: - ristretto255_u64 - ristretto255_u32 - - p256,ristretto255_u64 + # skip doc tests + - p256 --lib + - ristretto255_u64,p256 frontend_feature: - - serde - - danger + - + - --features serde + - --features danger toolchain: - stable - 1.51.0 @@ -66,7 +69,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: --no-default-features --features ${{ matrix.frontend_feature }},std --features ${{ matrix.backend_feature }} + args: --no-default-features ${{ matrix.frontend_feature }},std --features ${{ matrix.backend_feature }} build-no-std: name: Build with no-std on ${{ matrix.target }} diff --git a/Cargo.toml b/Cargo.toml index 3cfbf0f..cd59315 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ license = "MIT" edition = "2018" readme = "README.md" resolver = "2" +rust-version = "1.51.0" [features] default = ["ristretto255_u64", "serde"] @@ -21,11 +22,10 @@ ristretto255_fiat_u32 = ["curve25519-dalek/fiat_u32_backend"] ristretto255_simd = ["curve25519-dalek/simd_backend"] p256 = ["num-bigint", "num-integer", "num-traits", "once_cell", "p256_"] std = [] -serde = ["serde_", "base64"] [dependencies] -base64 = { version = "0.13", default-features = false, features = ["alloc"], optional = true } curve25519-dalek = { version = "3", default-features = false, optional = true } +derive-where = { version = "1.0.0-rc.1", features = ["zeroize"] } digest = "0.9" displaydoc = { version = "0.2", default-features = false } generic-array = "0.14" @@ -35,7 +35,7 @@ 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 = ["arithmetic", "zeroize"], optional = true } rand_core = { version = "0.6", default-features = false } -serde_ = { version = "1", package = "serde", default-features = false, optional = true } +serde = { version = "1", default-features = false, optional = true } subtle = { version = "2.3", default-features = false } zeroize = { version = "1", default-features = false } @@ -47,9 +47,7 @@ proptest = "1" rand = "0.8" regex = "1" sha2 = "0.9" -voprf = { path = "", default-features = false, features = ["std", "danger"] } [package.metadata.docs.rs] features = ["danger", "p256", "std"] targets = [] -rustdoc-args = ["--cfg", "docsrs"] diff --git a/src/errors.rs b/src/errors.rs index 45a3f6a..a5239ea 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -12,7 +12,7 @@ use std::error::Error; use displaydoc::Display; /// Represents an error in the manipulation of internal cryptographic data -#[derive(Clone, Debug, Display, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Display, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum InternalError { /// Could not parse byte sequence for key InvalidByteSequence, diff --git a/src/group/expand.rs b/src/group/expand.rs index 69d7e47..26fc20e 100644 --- a/src/group/expand.rs +++ b/src/group/expand.rs @@ -40,13 +40,13 @@ pub fn expand_message_xmd< where >::Output: ArrayLength, { - let digest_len = ::OutputSize::USIZE; + let digest_len = H::OutputSize::USIZE; let ell = div_ceil(L::USIZE, digest_len); if ell > 255 { return Err(InternalError::HashToCurveError); } let dst_prime = dst.concat(i2osp::(D::USIZE)?); - let z_pad = i2osp::<::BlockSize>(0)?; + let z_pad = i2osp::(0)?; let l_i_b_str = i2osp::(L::USIZE)?; let mut h = H::new(); diff --git a/src/group/mod.rs b/src/group/mod.rs index e13ef19..683d912 100644 --- a/src/group/mod.rs +++ b/src/group/mod.rs @@ -18,14 +18,9 @@ mod expand; #[cfg(feature = "p256")] mod p256; -#[cfg(any( - feature = "ristretto255_u64", - feature = "ristretto255_u32", - feature = "ristretto255_fiat_u64", - feature = "ristretto255_fiat_u32", - feature = "ristretto255_simd", -))] -mod ristretto; +cfg_ristretto! { + mod ristretto; +} use crate::errors::InternalError; use core::ops::{Add, Mul, Sub}; diff --git a/src/group/ristretto.rs b/src/group/ristretto.rs index 1209c91..1b761fd 100644 --- a/src/group/ristretto.rs +++ b/src/group/ristretto.rs @@ -22,113 +22,110 @@ use generic_array::{ }; use rand_core::{CryptoRng, RngCore}; -/// The implementation of such a subgroup for Ristretto -#[cfg(any( - feature = "ristretto255_u64", - feature = "ristretto255_u32", - feature = "ristretto255_fiat_u64", - feature = "ristretto255_fiat_u32", - feature = "ristretto255_simd", -))] -impl Group for RistrettoPoint { - const SUITE_ID: usize = 0x0001; +// `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_ristretto! { + /// The implementation of such a subgroup for Ristretto + impl Group for RistrettoPoint { + const SUITE_ID: usize = 0x0001; - // Implements the `hash_to_ristretto255()` function from - // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt - fn hash_to_curve + Add>( - msg: &[u8], - dst: GenericArray, - ) -> Result - where - >::Output: ArrayLength, - { - let uniform_bytes = super::expand::expand_message_xmd::(Some(msg), dst)?; + // Implements the `hash_to_ristretto255()` function from + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt + fn hash_to_curve + Add>( + msg: &[u8], + dst: GenericArray, + ) -> Result + where + >::Output: ArrayLength, + { + let uniform_bytes = super::expand::expand_message_xmd::(Some(msg), dst)?; - Ok(RistrettoPoint::from_uniform_bytes( - uniform_bytes - .as_slice() - .try_into() - .map_err(|_| InternalError::HashToCurveError)?, - )) - } + Ok(RistrettoPoint::from_uniform_bytes( + uniform_bytes + .as_slice() + .try_into() + .map_err(|_| InternalError::HashToCurveError)?, + )) + } - // Implements the `HashToScalar()` function from - // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.1 - fn hash_to_scalar< - 'a, - H: BlockInput + Digest, - D: ArrayLength + Add, - I: IntoIterator, - >( - input: I, - dst: GenericArray, - ) -> Result - where - >::Output: ArrayLength, - { - let uniform_bytes = super::expand::expand_message_xmd::(input, dst)?; + // Implements the `HashToScalar()` function from + // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.1 + fn hash_to_scalar< + 'a, + H: BlockInput + Digest, + D: ArrayLength + Add, + I: IntoIterator, + >( + input: I, + dst: GenericArray, + ) -> Result + where + >::Output: ArrayLength, + { + let uniform_bytes = super::expand::expand_message_xmd::(input, dst)?; - Ok(Scalar::from_bytes_mod_order_wide( - uniform_bytes - .as_slice() - .try_into() - .map_err(|_| InternalError::HashToCurveError)?, - )) - } + Ok(Scalar::from_bytes_mod_order_wide( + uniform_bytes + .as_slice() + .try_into() + .map_err(|_| InternalError::HashToCurveError)?, + )) + } - type Scalar = Scalar; - type ScalarLen = U32; - fn from_scalar_slice_unchecked( - scalar_bits: &GenericArray, - ) -> Result { - Ok(Scalar::from_bytes_mod_order(*scalar_bits.as_ref())) - } + type Scalar = Scalar; + type ScalarLen = U32; + fn from_scalar_slice_unchecked( + scalar_bits: &GenericArray, + ) -> Result { + Ok(Scalar::from_bytes_mod_order(*scalar_bits.as_ref())) + } - fn random_nonzero_scalar(rng: &mut R) -> Self::Scalar { - loop { - let scalar = { - let mut scalar_bytes = [0u8; 64]; - rng.fill_bytes(&mut scalar_bytes); - Scalar::from_bytes_mod_order_wide(&scalar_bytes) - }; + fn random_nonzero_scalar(rng: &mut R) -> Self::Scalar { + loop { + let scalar = { + let mut scalar_bytes = [0u8; 64]; + rng.fill_bytes(&mut scalar_bytes); + Scalar::from_bytes_mod_order_wide(&scalar_bytes) + }; - if scalar != Scalar::zero() { - break scalar; + if scalar != Scalar::zero() { + break scalar; + } } } - } - fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray { - scalar.to_bytes().into() - } + fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray { + scalar.to_bytes().into() + } - fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar { - scalar.invert() - } + fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar { + scalar.invert() + } - // The byte length necessary to represent group elements - type ElemLen = U32; - fn from_element_slice_unchecked( - element_bits: &GenericArray, - ) -> Result { - CompressedRistretto::from_slice(element_bits) - .decompress() - .ok_or(InternalError::PointError) - } - // serialization of a group element - fn to_arr(&self) -> GenericArray { - self.compress().to_bytes().into() - } + // The byte length necessary to represent group elements + type ElemLen = U32; + fn from_element_slice_unchecked( + element_bits: &GenericArray, + ) -> Result { + CompressedRistretto::from_slice(element_bits) + .decompress() + .ok_or(InternalError::PointError) + } + // serialization of a group element + fn to_arr(&self) -> GenericArray { + self.compress().to_bytes().into() + } - fn base_point() -> Self { - RISTRETTO_BASEPOINT_POINT - } + fn base_point() -> Self { + RISTRETTO_BASEPOINT_POINT + } - fn identity() -> Self { - ::identity() - } + fn identity() -> Self { + ::identity() + } - fn scalar_zero() -> Self::Scalar { - Self::Scalar::zero() + fn scalar_zero() -> Self::Scalar { + Self::Scalar::zero() + } } } diff --git a/src/group/tests.rs b/src/group/tests.rs index cc40146..49ae1ae 100644 --- a/src/group/tests.rs +++ b/src/group/tests.rs @@ -15,10 +15,12 @@ use crate::group::Group; #[test] fn test_group_properties() -> Result<(), InternalError> { - use curve25519_dalek::ristretto::RistrettoPoint; + cfg_ristretto! { { + use curve25519_dalek::ristretto::RistrettoPoint; - test_identity_element_error::()?; - test_zero_scalar_error::()?; + test_identity_element_error::()?; + test_zero_scalar_error::()?; + } } #[cfg(feature = "p256")] { diff --git a/src/impls.rs b/src/impls.rs deleted file mode 100644 index f3635d3..0000000 --- a/src/impls.rs +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. -// -// This source code is licensed under both the MIT license found in the -// LICENSE-MIT file in the root directory of this source tree and the Apache -// License, Version 2.0 found in the LICENSE-APACHE file in the root directory -// of this source tree. - -/// Implement multiple similar traits at the same time. Additionally used to -/// find `#[bind]` markers to build `while` constraint. -macro_rules! impl_with_bounds { - ( - $name:ident$(<$($gen:ident$(: $bound1:tt $(+ $bound2:tt)*)?),+>)? - // only collect types marked with `#bind` - // `|` prevents error about a possibly empty token - // `@` prevents ambiguity between `$_2` and `$trait1` - // `#` prevents ambiguity between marker traits and `$_2` - $(|$(@#bind: $type:ty|,)? $(@#pd: $_1:ty|,)? $(@$_2:ty|,)?)+ - $trait1:path => { $($fn1:item)? }, - $($trait2:path => { $($fn2:item)? },)* - ) => { - impl$(<$($gen$(: $bound1 $(+ $bound2)*)?),+>)? $trait1 for $name$(<$($gen),+>)? - where - $($($type: $trait1,)?)+ - { - $($fn1)? - } - - impl_with_bounds!( - $name$(<$($gen$(: $bound1 $(+ $bound2)*)?),+>)? - $(|$(@#bind: $type|,)? $(@#pd: $_1|,)? $(@$_2|,)?)+ - $($trait2 => { $($fn2)? },)* - ); - }; - // signature triggered when all traits are exhausted - ( - $name:ident$(<$($gen:ident$(: $bound1:tt$( + $bound2:tt)*)?),+>)? - $(|$(@#bind: $type:ty|,)? $(@#pd: $_1:ty|,)? $(@$_2:ty|,)?)+ - ) => { }; -} - -/// Skips attempt to call [`zeroize()`](zeroize::Zeroize::zeroize) on -/// [`PhantomData`](core::marker::PhantomData). -macro_rules! impl_internal_zeroize { - ($self_:ident, #pd $field:ident) => {}; - ($self_:ident, #bind $field:ident) => { - $self_.$field.zeroize(); - }; - ($self_:ident, $field:ident) => { - $self_.$field.zeroize(); - }; -} - -macro_rules! impl_traits_for { - ( - // include documentation, Rust can't connect documentation from outside - // a macro to a `struct` generated by a macro - $(#[doc = $doc:literal])* - $vis:vis struct $name:ident$(<$($gen:ident$(: $bound1:tt $(+ $bound2:tt)*)?),+$(,)?>)? { - $(#[$attr1:ident])? $vis1:vis $field1:ident: $type1:ty$(, - $(#[$attr2:ident])? $vis2:vis $field2:ident: $type2:ty)*$(,)? - } - ) => { - // build `struct` itself - $(#[doc = $doc])* - $vis struct $name$(<$($gen$(: $bound1 $(+$bound2)*)?),+>)? { - $vis1 $field1: $type1, - $($vis2 $field2: $type2),* - } - - // implement traits that require specific `where` constraints with the - // help of `#[bind]` - impl_with_bounds!( - $name$(<$($gen$(: $bound1 $(+ $bound2)*)?),+>)? - |@$(#$attr1:)? $type1|, $(|@$(#$attr2:)? $type2|,)* - core::fmt::Debug => { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("$name") - .field("$field1", &self.$field1) - $(.field("$field2", &self.$field2))* - .finish() - } - }, - Eq => { }, - PartialEq => { - fn eq(&self, other: &Self) -> bool { - PartialEq::eq(&self.$field1, &other.$field1) - $(&& PartialEq::eq(&self.$field2, &other.$field2))* - } - }, - core::hash::Hash => { - fn hash<_H: core::hash::Hasher>(&self, state: &mut _H) { - core::hash::Hash::hash(&self.$field1, state); - $(core::hash::Hash::hash(&self.$field2, state);)* - } - }, - Clone => { - fn clone(&self) -> Self { - Self { - $field1: self.$field1.clone(), - $($field2: self.$field2.clone(),)* - } - } - }, - ); - - impl$(<$($gen$(: $bound1 $(+ $bound2)*)?),+>)? zeroize::Zeroize for $name$(<$($gen),+>)? - { - fn zeroize(&mut self) { - impl_internal_zeroize!(self, $(#$attr1)? $field1); - $(impl_internal_zeroize!(self, $(#$attr2)? $field2);)* - } - } - - impl$(<$($gen$(: $bound1 $(+ $bound2)*)?),+>)? Drop for $name$(<$($gen),+>)? - { - fn drop(&mut self) { - zeroize::Zeroize::zeroize(self); - } - } - - #[cfg(feature = "serde")] - impl$(<$($gen$(: $bound1 $(+ $bound2)*)?),+>)? serde_::Serialize for $name$(<$($gen),+>)? { - fn serialize(&self, serializer: S) -> Result - where - S: serde_::Serializer, - { - if serializer.is_human_readable() { - serializer.serialize_str(&base64::encode(&self.serialize())) - } else { - serializer.serialize_bytes(&self.serialize()) - } - } - } - - #[cfg(feature = "serde")] - impl<'de, $($($gen$(: $bound1 $(+ $bound2)*)?),+)?> serde_::Deserialize<'de> for $name$(<$($gen),+>)? { - fn deserialize(deserializer: D) -> Result - where - D: serde_::Deserializer<'de>, - { - use serde_::de::Error; - - if deserializer.is_human_readable() { - let s = <&str>::deserialize(deserializer)?; - Self::deserialize(&base64::decode(s).map_err(Error::custom)?) - } else { - Self::deserialize(<&[u8]>::deserialize(deserializer)?) - } - .map_err(Error::custom) - } - } - }; -} diff --git a/src/lib.rs b/src/lib.rs index 1e83e3f..bceb0ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,7 +107,7 @@ //! # let server = NonVerifiableServer::::new(&mut server_rng) //! # .expect("Unable to construct server"); //! let server_evaluate_result = server.evaluate( -//! client_blind_result.message, +//! &client_blind_result.message, //! None, //! ).expect("Unable to perform server evaluate"); //! ``` @@ -134,11 +134,11 @@ //! # let server = NonVerifiableServer::::new(&mut server_rng) //! # .expect("Unable to construct server"); //! # let server_evaluate_result = server.evaluate( -//! # client_blind_result.message, +//! # &client_blind_result.message, //! # None, //! # ).expect("Unable to perform server evaluate"); //! let client_finalize_result = client_blind_result.state.finalize( -//! server_evaluate_result.message, +//! &server_evaluate_result.message, //! None, //! ).expect("Unable to perform client finalization"); //! @@ -228,7 +228,7 @@ //! # .expect("Unable to construct server"); //! let server_evaluate_result = server.evaluate( //! &mut server_rng, -//! client_blind_result.message, +//! &client_blind_result.message, //! None, //! ).expect("Unable to perform server evaluate"); //! ``` @@ -257,12 +257,12 @@ //! # .expect("Unable to construct server"); //! # let server_evaluate_result = server.evaluate( //! # &mut server_rng, -//! # client_blind_result.message, +//! # &client_blind_result.message, //! # None, //! # ).expect("Unable to perform server evaluate"); //! let client_finalize_result = client_blind_result.state.finalize( -//! server_evaluate_result.message, -//! server_evaluate_result.proof, +//! &server_evaluate_result.message, +//! &server_evaluate_result.proof, //! server.get_public_key(), //! None, //! ).expect("Unable to perform client finalization"); @@ -374,7 +374,7 @@ //! let client_batch_finalize_result = VerifiableClient::batch_finalize( //! &client_states, //! &server_batch_evaluate_result.messages, -//! server_batch_evaluate_result.proof, +//! &server_batch_evaluate_result.proof, //! server.get_public_key(), //! None, //! ).expect("Unable to perform client batch finalization"); @@ -396,10 +396,10 @@ //! # Features //! //! - 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. +//! 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/). +//! [serde](https://serde.rs/). //! //! - The `danger` feature, disabled by default, exposes functions for setting and getting //! internal values not available in the default API. These functions are intended for use in @@ -407,29 +407,30 @@ //! perform the necessary validations on them (such as being valid group elements). //! //! - The backend features are re-exported from -//! [curve25519-dalek](https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) and allow for selecting -//! the corresponding backend for the curve arithmetic used. The `ristretto255_u64` feature is included as the default. -//! Other features are mapped as `ristretto255_u32`, `ristretto255_fiat_u64` and `ristretto255_fiat_u32`. +//! [curve25519-dalek](https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) and allow for selecting +//! the corresponding backend for the curve arithmetic used. The `ristretto255_u64` feature is included as the default. +//! Other features are mapped as `ristretto255_u32`, `ristretto255_fiat_u64` and `ristretto255_fiat_u32`. //! //! - The `ristretto255_simd` feature is re-exported from -//! [curve25519-dalek](https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) and enables parallel formulas, -//! using either AVX2 or AVX512-IFMA. This will automatically enable the `ristretto255_u64` feature and requires Rust nightly. +//! [curve25519-dalek](https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) and enables parallel formulas, +//! using either AVX2 or AVX512-IFMA. This will automatically enable the `ristretto255_u64` feature and requires Rust nightly. #![deny(unsafe_code)] +#![no_std] #![warn(clippy::cargo, missing_docs)] #![allow(clippy::multiple_crate_versions)] -#![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(docsrs, feature(doc_cfg))] extern crate alloc; -#[macro_use] -mod impls; +#[cfg(feature = "std")] +extern crate std; + #[macro_use] mod util; +#[macro_use] +mod serialization; pub mod errors; pub mod group; -mod serialization; mod voprf; #[cfg(test)] diff --git a/src/serialization.rs b/src/serialization.rs index 78abe13..a78d479 100644 --- a/src/serialization.rs +++ b/src/serialization.rs @@ -34,7 +34,7 @@ impl NonVerifiableClient { /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let scalar_len = ::ScalarLen::USIZE; + let scalar_len = G::ScalarLen::USIZE; if input.len() < scalar_len { return Err(InternalError::SizeError); } @@ -63,8 +63,8 @@ impl VerifiableClient { /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let scalar_len = ::ScalarLen::USIZE; - let elem_len = ::ElemLen::USIZE; + let scalar_len = G::ScalarLen::USIZE; + let elem_len = G::ElemLen::USIZE; if input.len() < scalar_len + elem_len { return Err(InternalError::SizeError); } @@ -90,7 +90,7 @@ impl NonVerifiableServer { /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let scalar_len = ::ScalarLen::USIZE; + let scalar_len = G::ScalarLen::USIZE; if input.len() != scalar_len { return Err(InternalError::SizeError); } @@ -112,8 +112,8 @@ impl VerifiableServer { /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let scalar_len = ::ScalarLen::USIZE; - let elem_len = ::ElemLen::USIZE; + let scalar_len = G::ScalarLen::USIZE; + let elem_len = G::ElemLen::USIZE; if input.len() != scalar_len + elem_len { return Err(InternalError::SizeError); } @@ -141,7 +141,7 @@ impl Proof { /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let scalar_len = ::ScalarLen::USIZE; + let scalar_len = G::ScalarLen::USIZE; if input.len() != scalar_len + scalar_len { return Err(InternalError::SizeError); } @@ -161,7 +161,7 @@ impl BlindedElement { /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let elem_len = ::ElemLen::USIZE; + let elem_len = G::ElemLen::USIZE; if input.len() != elem_len { return Err(InternalError::SizeError); } @@ -180,7 +180,7 @@ impl EvaluationElement { /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { - let elem_len = ::ElemLen::USIZE; + let elem_len = G::ElemLen::USIZE; if input.len() != elem_len { return Err(InternalError::SizeError); } @@ -190,3 +190,68 @@ impl EvaluationElement { }) } } + +///////////////////////////////////////////// +// Serde implementation for High-Level API // +// ======================================= // +///////////////////////////////////////////// + +/// Macro used for deriving `serde`'s `Serialize` and `Deserialize` traits. +macro_rules! impl_serialize_and_deserialize_for { + ($item:ident) => { + #[cfg(feature = "serde")] + impl serde::Serialize for $item { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_bytes(&self.serialize()) + } + } + + #[cfg(feature = "serde")] + impl<'de, G: Group, H: BlockInput + Digest> serde::Deserialize<'de> for $item { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + use serde::de::Error; + + struct ByteVisitor(core::marker::PhantomData<(G, H)>); + + impl<'de, G: Group, H: BlockInput + Digest> serde::de::Visitor<'de> for ByteVisitor { + type Value = $item; + + fn expecting( + &self, + formatter: &mut core::fmt::Formatter, + ) -> core::fmt::Result { + formatter.write_str(core::concat!( + "the byte representation of a ", + core::stringify!($item) + )) + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: Error, + { + $item::::deserialize(value).map_err(|_| { + Error::invalid_value( + serde::de::Unexpected::Bytes(value), + &core::concat!( + "invalid byte sequence for ", + core::stringify!($item) + ), + ) + }) + } + } + + deserializer + .deserialize_bytes(ByteVisitor::(core::marker::PhantomData)) + .map_err(Error::custom) + } + } + }; +} diff --git a/src/tests/parser.rs b/src/tests/parser.rs index a5e42ca..313859c 100644 --- a/src/tests/parser.rs +++ b/src/tests/parser.rs @@ -5,7 +5,9 @@ // License, Version 2.0 found in the LICENSE-APACHE file in the root directory // of this source tree. -use alloc::string::String; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use alloc::{format, vec}; pub(crate) fn rfc_to_json(input: &str) -> String { format!("{{\n{}\n}}", parse_ciphersuites(input)) @@ -20,7 +22,7 @@ fn parse_ciphersuites(input: &str) -> String { for caps in re.captures_iter(input) { let ciphersuite = format!( "\"{}\": {{ {} }}", - caps["ciphersuite"].to_string(), + &caps["ciphersuite"], parse_modes(chunks[count]) ); ciphersuites.push(ciphersuite); @@ -39,7 +41,7 @@ fn parse_modes(input: &str) -> String { for caps in re.captures_iter(input) { let mode = format!( "\"{}\": [\n {} \n]", - caps["mode"].to_string(), + &caps["mode"], parse_vectors(chunks[count]) ); modes.push(mode); diff --git a/src/tests/voprf_test_vectors.rs b/src/tests/voprf_test_vectors.rs index 956654b..31b5757 100644 --- a/src/tests/voprf_test_vectors.rs +++ b/src/tests/voprf_test_vectors.rs @@ -14,7 +14,8 @@ use crate::{ VerifiableClient, VerifiableServer, }, }; -use alloc::string::ToString; +use alloc::string::{String, ToString}; +use alloc::vec; use alloc::vec::Vec; use digest::{BlockInput, Digest}; use generic_array::GenericArray; @@ -85,30 +86,32 @@ fn test_vectors() -> Result<(), InternalError> { let rfc = json::parse(rfc_to_json(super::voprf_vectors::VECTORS).as_str()) .expect("Could not parse json"); - use curve25519_dalek::ristretto::RistrettoPoint; - use sha2::Sha512; + cfg_ristretto! { { + use curve25519_dalek::ristretto::RistrettoPoint; + use sha2::Sha512; - let ristretto_base_tvs = json_to_test_vectors!( - rfc, - String::from("ristretto255, SHA-512"), - String::from("Base") - ); + let ristretto_base_tvs = json_to_test_vectors!( + rfc, + String::from("ristretto255, SHA-512"), + String::from("Base") + ); - let ristretto_verifiable_tvs = json_to_test_vectors!( - rfc, - String::from("ristretto255, SHA-512"), - String::from("Verifiable") - ); + let ristretto_verifiable_tvs = json_to_test_vectors!( + rfc, + String::from("ristretto255, SHA-512"), + String::from("Verifiable") + ); - test_base_seed_to_key::(&ristretto_base_tvs)?; - test_base_blind::(&ristretto_base_tvs)?; - test_base_evaluate::(&ristretto_base_tvs)?; - test_base_finalize::(&ristretto_base_tvs)?; + test_base_seed_to_key::(&ristretto_base_tvs)?; + test_base_blind::(&ristretto_base_tvs)?; + test_base_evaluate::(&ristretto_base_tvs)?; + test_base_finalize::(&ristretto_base_tvs)?; - test_verifiable_seed_to_key::(&ristretto_verifiable_tvs)?; - test_verifiable_blind::(&ristretto_verifiable_tvs)?; - test_verifiable_evaluate::(&ristretto_verifiable_tvs)?; - test_verifiable_finalize::(&ristretto_verifiable_tvs)?; + test_verifiable_seed_to_key::(&ristretto_verifiable_tvs)?; + test_verifiable_blind::(&ristretto_verifiable_tvs)?; + test_verifiable_evaluate::(&ristretto_verifiable_tvs)?; + test_verifiable_finalize::(&ristretto_verifiable_tvs)?; + } } #[cfg(feature = "p256")] { @@ -182,7 +185,7 @@ fn test_base_blind( assert_eq!( ¶meters.blind[i], - &G::scalar_as_bytes(client_result.state.get_blind()).to_vec() + &G::scalar_as_bytes(client_result.state.blind).to_vec() ); assert_eq!( ¶meters.blinded_element[i], @@ -227,7 +230,7 @@ fn test_base_evaluate( for i in 0..parameters.input.len() { let server = NonVerifiableServer::::new_with_key(¶meters.sksm)?; let server_result = server.evaluate( - BlindedElement::deserialize(¶meters.blinded_element[i])?, + &BlindedElement::deserialize(¶meters.blinded_element[i])?, Some(¶meters.info), )?; @@ -275,13 +278,11 @@ fn test_base_finalize( for i in 0..parameters.input.len() { let client = NonVerifiableClient::::from_data_and_blind( ¶meters.input[i], - ::from_scalar_slice(&GenericArray::clone_from_slice( - ¶meters.blind[i], - ))?, + G::from_scalar_slice(&GenericArray::clone_from_slice(¶meters.blind[i]))?, ); let client_finalize_result = client.finalize( - EvaluationElement::deserialize(¶meters.evaluation_element[i])?, + &EvaluationElement::deserialize(¶meters.evaluation_element[i])?, Some(¶meters.info), )?; @@ -299,10 +300,8 @@ fn test_verifiable_finalize( for i in 0..parameters.input.len() { let client = VerifiableClient::::from_data_and_blind_and_element( ¶meters.input[i], - ::from_scalar_slice(&GenericArray::clone_from_slice( - ¶meters.blind[i], - ))?, - ::from_element_slice(&GenericArray::clone_from_slice( + G::from_scalar_slice(&GenericArray::clone_from_slice(¶meters.blind[i]))?, + G::from_element_slice(&GenericArray::clone_from_slice( ¶meters.blinded_element[i], ))?, ); @@ -318,7 +317,7 @@ fn test_verifiable_finalize( let batch_result = VerifiableClient::batch_finalize( &clients, &messages, - Proof::deserialize(¶meters.proof)?, + &Proof::deserialize(¶meters.proof)?, G::from_element_slice(GenericArray::from_slice(¶meters.pksm))?, Some(¶meters.info), )?; diff --git a/src/util.rs b/src/util.rs index f1881ed..625d06a 100644 --- a/src/util.rs +++ b/src/util.rs @@ -29,7 +29,7 @@ pub(crate) fn i2osp>( } let mut output = GenericArray::default(); - output[L::USIZE - SIZEOF_USIZE..L::USIZE].copy_from_slice(&input.to_be_bytes()); + output[L::USIZE - SIZEOF_USIZE..].copy_from_slice(&input.to_be_bytes()); Ok(output) } @@ -50,6 +50,8 @@ impl<'a, L1: ArrayLength, L2: ArrayLength> IntoIterator for &'a Serializ type IntoIter = IntoIter<&'a [u8], 2>; fn into_iter(self) -> Self::IntoIter { + // MSRV: array `into_iter` isn't available in 1.51 + #[allow(deprecated)] IntoIter::new([ &self.octet, match self.input { @@ -115,6 +117,29 @@ macro_rules! chain { }; } +macro_rules! cfg_ristretto { + ($tree:tt) => { + #[cfg(any( + feature = "ristretto255_u64", + feature = "ristretto255_u32", + feature = "ristretto255_fiat_u64", + feature = "ristretto255_fiat_u32", + feature = "ristretto255_simd", + ))] + $tree + }; + ($($item:item)+) => { + $(#[cfg(any( + feature = "ristretto255_u64", + feature = "ristretto255_u32", + feature = "ristretto255_fiat_u64", + feature = "ristretto255_fiat_u32", + feature = "ristretto255_simd", + ))] + $item)+ + }; +} + #[cfg(test)] mod unit_tests { use super::*; @@ -122,10 +147,8 @@ mod unit_tests { BlindedElement, EvaluationElement, NonVerifiableClient, NonVerifiableServer, Proof, VerifiableClient, VerifiableServer, }; - use curve25519_dalek::ristretto::RistrettoPoint; use generic_array::typenum::{U1, U2}; use proptest::{collection::vec, prelude::*}; - use sha2::Sha512; // Test the error condition for I2OSP #[test] @@ -141,40 +164,52 @@ mod unit_tests { assert!(i2osp::(256 * 256 + 1).is_err()); } + macro_rules! test_deserialize { + ($item:ident, $bytes:ident) => { + cfg_ristretto! { { + let _ = $item::::deserialize(&$bytes[..]); + } } + #[cfg(feature = "p256")] + { + let _ = $item::::deserialize(&$bytes[..]); + } + }; + } + proptest! { #[test] fn test_nocrash_nonverifiable_client(bytes in vec(any::(), 0..200)) { - NonVerifiableClient::::deserialize(&bytes[..]).map_or(true, |_| true); + test_deserialize!(NonVerifiableClient, bytes); } #[test] fn test_nocrash_verifiable_client(bytes in vec(any::(), 0..200)) { - VerifiableClient::::deserialize(&bytes[..]).map_or(true, |_| true); + test_deserialize!(VerifiableClient, bytes); } #[test] fn test_nocrash_nonverifiable_server(bytes in vec(any::(), 0..200)) { - NonVerifiableServer::::deserialize(&bytes[..]).map_or(true, |_| true); + test_deserialize!(NonVerifiableServer, bytes); } #[test] fn test_nocrash_verifiable_server(bytes in vec(any::(), 0..200)) { - VerifiableServer::::deserialize(&bytes[..]).map_or(true, |_| true); + test_deserialize!(VerifiableServer, bytes); } #[test] fn test_nocrash_blinded_element(bytes in vec(any::(), 0..200)) { - BlindedElement::::deserialize(&bytes[..]).map_or(true, |_| true); + test_deserialize!(BlindedElement, bytes); } #[test] fn test_nocrash_evaluation_element(bytes in vec(any::(), 0..200)) { - EvaluationElement::::deserialize(&bytes[..]).map_or(true, |_| true); + test_deserialize!(EvaluationElement, bytes); } #[test] fn test_nocrash_proof(bytes in vec(any::(), 0..200)) { - Proof::::deserialize(&bytes[..]).map_or(true, |_| true); + test_deserialize!(Proof, bytes); } } } diff --git a/src/voprf.rs b/src/voprf.rs index e938302..02ac827 100644 --- a/src/voprf.rs +++ b/src/voprf.rs @@ -15,6 +15,7 @@ use crate::{ use alloc::vec::Vec; use core::convert::TryInto; use core::marker::PhantomData; +use derive_where::DeriveWhere; use digest::{BlockInput, Digest}; use generic_array::sequence::Concat; use generic_array::{ @@ -29,14 +30,14 @@ use subtle::ConstantTimeEq; // ========= // /////////////// -static STR_HASH_TO_SCALAR: &[u8; 13] = b"HashToScalar-"; -static STR_HASH_TO_GROUP: &[u8; 12] = b"HashToGroup-"; -static STR_FINALIZE: &[u8; 9] = b"Finalize-"; -static STR_SEED: &[u8; 5] = b"Seed-"; -static STR_CONTEXT: &[u8] = b"Context-"; -static STR_COMPOSITE: &[u8; 10] = b"Composite-"; -static STR_CHALLENGE: &[u8; 10] = b"Challenge-"; -static STR_VOPRF: &[u8; 8] = b"VOPRF08-"; +static STR_HASH_TO_SCALAR: [u8; 13] = *b"HashToScalar-"; +static STR_HASH_TO_GROUP: [u8; 12] = *b"HashToGroup-"; +static STR_FINALIZE: [u8; 9] = *b"Finalize-"; +static STR_SEED: [u8; 5] = *b"Seed-"; +static STR_CONTEXT: [u8; 8] = *b"Context-"; +static STR_COMPOSITE: [u8; 10] = *b"Composite-"; +static STR_CHALLENGE: [u8; 10] = *b"Challenge-"; +static STR_VOPRF: [u8; 8] = *b"VOPRF08-"; /// Determines the mode of operation (either base mode or /// verifiable mode) @@ -51,95 +52,107 @@ enum Mode { // ====================== // //////////////////////////// -impl_traits_for! { - /// A client which engages with a [NonVerifiableServer] - /// in base mode, meaning that the OPRF outputs are not - /// verifiable. - pub struct NonVerifiableClient { - #[bind] - pub(crate) blind: ::Scalar, - pub(crate) data: Vec, - #[pd] - pub(crate) hash: PhantomData, - } +/// A client which engages with a [NonVerifiableServer] +/// in base mode, meaning that the OPRF outputs are not +/// verifiable. +#[derive(DeriveWhere)] +#[derive_where(Clone, Zeroize(drop))] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Scalar)] +pub struct NonVerifiableClient { + pub(crate) blind: G::Scalar, + pub(crate) data: Vec, + #[derive_where(skip(Zeroize))] + pub(crate) hash: PhantomData, } -impl_traits_for! { - /// A client which engages with a [VerifiableServer] - /// in verifiable mode, meaning that the OPRF outputs - /// can be checked against a server public key. - pub struct VerifiableClient { - #[bind] - pub(crate) blind: ::Scalar, - #[bind] - pub(crate) blinded_element: G, - pub(crate) data: Vec, - #[pd] - pub(crate) hash: PhantomData, - } +impl_serialize_and_deserialize_for!(NonVerifiableClient); + +/// A client which engages with a [VerifiableServer] +/// in verifiable mode, meaning that the OPRF outputs +/// can be checked against a server public key. +#[derive(DeriveWhere)] +#[derive_where(Clone, Zeroize(drop))] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G, G::Scalar)] +pub struct VerifiableClient { + pub(crate) blind: G::Scalar, + pub(crate) blinded_element: G, + pub(crate) data: Vec, + #[derive_where(skip(Zeroize))] + pub(crate) hash: PhantomData, } -impl_traits_for! { - /// A server which engages with a [NonVerifiableClient] - /// in base mode, meaning that the OPRF outputs are not - /// verifiable. - pub struct NonVerifiableServer { - #[bind] - pub(crate) sk: ::Scalar, - #[pd] - pub(crate) hash: PhantomData, - } +impl_serialize_and_deserialize_for!(VerifiableClient); + +/// A server which engages with a [NonVerifiableClient] +/// in base mode, meaning that the OPRF outputs are not +/// verifiable. +#[derive(DeriveWhere)] +#[derive_where(Clone, Zeroize(drop))] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Scalar)] +pub struct NonVerifiableServer { + pub(crate) sk: G::Scalar, + #[derive_where(skip(Zeroize))] + pub(crate) hash: PhantomData, } -impl_traits_for! { - /// A server which engages with a [VerifiableClient] - /// in verifiable mode, meaning that the OPRF outputs - /// can be checked against a server public key. - pub struct VerifiableServer { - #[bind] - pub(crate) sk: ::Scalar, - #[bind] - pub(crate) pk: G, - #[pd] - pub(crate) hash: PhantomData, - } +impl_serialize_and_deserialize_for!(NonVerifiableServer); + +/// A server which engages with a [VerifiableClient] +/// in verifiable mode, meaning that the OPRF outputs +/// can be checked against a server public key. +#[derive(DeriveWhere)] +#[derive_where(Clone, Zeroize(drop))] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G, G::Scalar)] +pub struct VerifiableServer { + pub(crate) sk: G::Scalar, + pub(crate) pk: G, + #[derive_where(skip(Zeroize))] + pub(crate) hash: PhantomData, } -impl_traits_for! { - /// A proof produced by a [VerifiableServer] that - /// the OPRF output matches against a server public key. - pub struct Proof { - #[bind] - pub(crate) c_scalar: ::Scalar, - pub(crate) s_scalar: ::Scalar, - #[pd] - pub(crate) hash: PhantomData, - } +impl_serialize_and_deserialize_for!(VerifiableServer); + +/// A proof produced by a [VerifiableServer] that +/// the OPRF output matches against a server public key. +#[derive(DeriveWhere)] +#[derive_where(Clone, Zeroize(drop))] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Scalar)] +pub struct Proof { + pub(crate) c_scalar: G::Scalar, + pub(crate) s_scalar: G::Scalar, + #[derive_where(skip(Zeroize))] + pub(crate) hash: PhantomData, } -impl_traits_for! { - /// The first client message sent from a client (either verifiable or not) - /// to a server (either verifiable or not). - pub struct BlindedElement { - #[bind] - pub(crate) value: G, - #[pd] - pub(crate) hash: PhantomData, - } +impl_serialize_and_deserialize_for!(Proof); + +/// The first client message sent from a client (either verifiable or not) +/// to a server (either verifiable or not). +#[derive(DeriveWhere)] +#[derive_where(Clone, Zeroize(drop))] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G)] +pub struct BlindedElement { + pub(crate) value: G, + #[derive_where(skip(Zeroize))] + pub(crate) hash: PhantomData, } -impl_traits_for! { - /// The server's response to the [BlindedElement] message from - /// a client (either verifiable or not) - /// to a server (either verifiable or not). - pub struct EvaluationElement { - #[bind] - pub(crate) value: G, - #[pd] - pub(crate) hash: PhantomData, - } +impl_serialize_and_deserialize_for!(BlindedElement); + +/// The server's response to the [BlindedElement] message from +/// a client (either verifiable or not) +/// to a server (either verifiable or not). +#[derive(DeriveWhere)] +#[derive_where(Clone, Zeroize(drop))] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G)] +pub struct EvaluationElement { + pub(crate) value: G, + #[derive_where(skip(Zeroize))] + pub(crate) hash: PhantomData, } +impl_serialize_and_deserialize_for!(EvaluationElement); + ///////////////////////// // API Implementations // // =================== // @@ -165,7 +178,7 @@ impl NonVerifiableClient { }) } - #[cfg(feature = "danger")] + #[cfg(any(feature = "danger", test))] /// Computes the first step for the multiplicative blinding version of DH-OPRF, /// taking a blinding factor scalar as input instead of sampling from an RNG. /// @@ -175,7 +188,7 @@ impl NonVerifiableClient { /// it does not perform any checks on the validity of the blinding factor! pub fn deterministic_blind_unchecked( input: Vec, - blind: ::Scalar, + blind: G::Scalar, ) -> Result, InternalError> { let blinded_element = deterministic_blind_unchecked::(&input, &blind, Mode::Base)?; Ok(NonVerifiableClientBlindResult { @@ -195,11 +208,10 @@ impl NonVerifiableClient { /// the client unblinds the server's message. pub fn finalize( &self, - evaluation_element: EvaluationElement, + evaluation_element: &EvaluationElement, metadata: Option<&[u8]>, - ) -> Result::OutputSize>, InternalError> { - let unblinded_element = - evaluation_element.value * &::scalar_invert(&self.blind); + ) -> Result, InternalError> { + let unblinded_element = evaluation_element.value * &G::scalar_invert(&self.blind); let outputs = finalize_after_unblind::( Some((self.data.as_slice(), unblinded_element)).into_iter(), metadata.unwrap_or_default(), @@ -210,7 +222,7 @@ impl NonVerifiableClient { #[cfg(test)] /// Only used for test functions - pub fn from_data_and_blind(data: &[u8], blind: ::Scalar) -> Self { + pub fn from_data_and_blind(data: &[u8], blind: G::Scalar) -> Self { Self { data: data.to_vec(), blind, @@ -220,7 +232,7 @@ impl NonVerifiableClient { #[cfg(feature = "danger")] /// Exposes the blind group element - pub fn get_blind(&self) -> ::Scalar { + pub fn get_blind(&self) -> G::Scalar { self.blind } } @@ -247,7 +259,7 @@ impl VerifiableClient { }) } - #[cfg(feature = "danger")] + #[cfg(any(feature = "danger", test))] /// Computes the first step for the multiplicative blinding version of DH-OPRF, /// taking a blinding factor scalar as input instead of sampling from an RNG. /// @@ -257,7 +269,7 @@ impl VerifiableClient { /// it does not perform any checks on the validity of the blinding factor! pub fn deterministic_blind_unchecked( input: Vec, - blind: ::Scalar, + blind: G::Scalar, ) -> Result, InternalError> { let blinded_element = deterministic_blind_unchecked::(&input, &blind, Mode::Verifiable)?; @@ -279,15 +291,18 @@ impl VerifiableClient { /// the client unblinds the server's message. pub fn finalize( &self, - evaluation_element: EvaluationElement, - proof: Proof, + evaluation_element: &EvaluationElement, + proof: &Proof, pk: G, metadata: Option<&[u8]>, - ) -> Result::OutputSize>, InternalError> { - // circumvent `.clone()` + ) -> Result, InternalError> { + // `core::array::from_ref` needs a MSRV of 1.53 let clients: &[Self; 1] = core::slice::from_ref(self).try_into().unwrap(); - let batch_result = - Self::batch_finalize(clients, &[evaluation_element], proof, pk, metadata)?; + let messages: &[EvaluationElement; 1] = core::slice::from_ref(evaluation_element) + .try_into() + .unwrap(); + + let batch_result = Self::batch_finalize(clients, messages, proof, pk, metadata)?; Ok(batch_result[0].clone()) } @@ -295,10 +310,10 @@ impl VerifiableClient { pub fn batch_finalize<'a, IC, IM>( clients: &'a IC, messages: &'a IM, - proof: Proof, + proof: &Proof, pk: G, metadata: Option<&[u8]>, - ) -> Result::OutputSize>>, InternalError> + ) -> Result>, InternalError> where G: 'a, H: 'a, @@ -359,7 +374,7 @@ impl VerifiableClient { /// Only used for test functions pub fn from_data_and_blind_and_element( data: &[u8], - blind: ::Scalar, + blind: G::Scalar, blinded_element: G, ) -> Self { Self { @@ -372,7 +387,7 @@ impl VerifiableClient { #[cfg(test)] /// Only used for test functions - pub fn get_blind(&self) -> ::Scalar { + pub fn get_blind(&self) -> G::Scalar { self.blind } } @@ -380,7 +395,7 @@ impl VerifiableClient { impl NonVerifiableServer { /// Produces a new instance of a [NonVerifiableServer] using a supplied RNG pub fn new(rng: &mut R) -> Result { - let mut seed = GenericArray::<_, ::OutputSize>::default(); + let mut seed = GenericArray::<_, H::OutputSize>::default(); rng.fill_bytes(&mut seed); Self::new_from_seed(&seed) } @@ -401,7 +416,7 @@ impl NonVerifiableServer { /// Corresponds to DeriveKeyPair() function from the VOPRF specification. pub fn new_from_seed(seed: &[u8]) -> Result { let dst = - GenericArray::from(*STR_HASH_TO_SCALAR).concat(get_context_string::(Mode::Base)?); + GenericArray::from(STR_HASH_TO_SCALAR).concat(get_context_string::(Mode::Base)?); let sk = G::hash_to_scalar::(Some(seed), dst)?; Ok(Self { sk, @@ -419,17 +434,17 @@ impl NonVerifiableServer { /// message is sent from the server (who holds the OPRF key) to the client. pub fn evaluate( &self, - blinded_element: BlindedElement, + blinded_element: &BlindedElement, metadata: Option<&[u8]>, ) -> Result, InternalError> { chain!( context, - STR_CONTEXT => |x| Some(x), + STR_CONTEXT => |x| Some(x.as_ref()), get_context_string::(Mode::Base)? => |x| Some(x.as_slice()), serialize::(metadata.unwrap_or_default())?, ); let dst = - GenericArray::from(*STR_HASH_TO_SCALAR).concat(get_context_string::(Mode::Base)?); + GenericArray::from(STR_HASH_TO_SCALAR).concat(get_context_string::(Mode::Base)?); let m = G::hash_to_scalar::(context, dst)?; let t = self.sk + &m; let evaluation_element = blinded_element.value * &G::scalar_invert(&t); @@ -445,7 +460,7 @@ impl NonVerifiableServer { impl VerifiableServer { /// Produces a new instance of a [VerifiableServer] using a supplied RNG pub fn new(rng: &mut R) -> Result { - let mut seed = GenericArray::<_, ::OutputSize>::default(); + let mut seed = GenericArray::<_, H::OutputSize>::default(); rng.fill_bytes(&mut seed); Self::new_from_seed(&seed) } @@ -467,7 +482,7 @@ impl VerifiableServer { /// /// Corresponds to DeriveKeyPair() function from the VOPRF specification. pub fn new_from_seed(seed: &[u8]) -> Result { - let dst = GenericArray::from(*STR_HASH_TO_SCALAR) + let dst = GenericArray::from(STR_HASH_TO_SCALAR) .concat(get_context_string::(Mode::Verifiable)?); let sk = G::hash_to_scalar::(Some(seed), dst)?; let pk = G::base_point() * &sk; @@ -480,7 +495,7 @@ impl VerifiableServer { // Only used for tests #[cfg(test)] - pub fn get_private_key(&self) -> ::Scalar { + pub fn get_private_key(&self) -> G::Scalar { self.sk } @@ -489,10 +504,14 @@ impl VerifiableServer { pub fn evaluate( &self, rng: &mut R, - blinded_element: BlindedElement, + blinded_element: &BlindedElement, metadata: Option<&[u8]>, ) -> Result, InternalError> { - let batch_result = self.batch_evaluate(rng, &[blinded_element], metadata)?; + // `core::array::from_ref` needs a MSRV of 1.53 + let blinded_elements: &[BlindedElement; 1] = + core::slice::from_ref(blinded_element).try_into().unwrap(); + + let batch_result = self.batch_evaluate(rng, blinded_elements, metadata)?; Ok(VerifiableServerEvaluateResult { message: batch_result.messages[0].copy(), proof: batch_result.proof, @@ -513,11 +532,11 @@ impl VerifiableServer { <&'a I as IntoIterator>::IntoIter: ExactSizeIterator, { chain!(context, - STR_CONTEXT => |x| Some(x), + STR_CONTEXT => |x| Some(x.as_ref()), get_context_string::(Mode::Verifiable)? => |x| Some(x.as_slice()), serialize::(metadata.unwrap_or_default())?, ); - let dst = GenericArray::from(*STR_HASH_TO_SCALAR) + let dst = GenericArray::from(STR_HASH_TO_SCALAR) .concat(get_context_string::(Mode::Verifiable)?); let m = G::hash_to_scalar::(context, dst)?; let t = self.sk + &m; @@ -603,7 +622,7 @@ pub struct VerifiableServerBatchEvaluateResult /// Convenience struct only used in batching APIs struct BatchItems { - blind: ::Scalar, + blind: G::Scalar, evaluation_element: EvaluationElement, blinded_element: BlindedElement, } @@ -673,9 +692,9 @@ fn blind( input: &[u8], blinding_factor_rng: &mut R, mode: Mode, -) -> Result<(::Scalar, G), InternalError> { +) -> Result<(G::Scalar, G), InternalError> { // Choose a random scalar that must be non-zero - let blind = ::random_nonzero_scalar(blinding_factor_rng); + let blind = G::random_nonzero_scalar(blinding_factor_rng); let blinded_element = deterministic_blind_unchecked::(input, &blind, mode)?; Ok((blind, blinded_element)) } @@ -684,18 +703,18 @@ fn blind( // and therefore takes it as input. Does not check if the blinding factor is non-zero. fn deterministic_blind_unchecked( input: &[u8], - blind: &::Scalar, + blind: &G::Scalar, mode: Mode, ) -> Result { - let dst = GenericArray::from(*STR_HASH_TO_GROUP).concat(get_context_string::(mode)?); - let hashed_point = ::hash_to_curve::(input, dst)?; + 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) } fn verifiable_unblind<'a, G: 'a + Group, H: 'a + BlockInput + Digest, I>( batch_items: &'a I, pk: G, - proof: Proof, + proof: &Proof, info: &[u8], ) -> Result, InternalError> where @@ -703,13 +722,13 @@ where <&'a I as IntoIterator>::IntoIter: ExactSizeIterator, { chain!(context, - STR_CONTEXT => |x| Some(x), + STR_CONTEXT => |x| Some(x.as_ref()), get_context_string::(Mode::Verifiable)? => |x| Some(x.as_slice()), serialize::(info)?, ); let dst = - GenericArray::from(*STR_HASH_TO_SCALAR).concat(get_context_string::(Mode::Verifiable)?); + GenericArray::from(STR_HASH_TO_SCALAR).concat(get_context_string::(Mode::Verifiable)?); let m = G::hash_to_scalar::(context, dst)?; let g = G::base_point(); @@ -732,7 +751,7 @@ where #[allow(clippy::many_single_char_names)] fn generate_proof( rng: &mut R, - k: ::Scalar, + k: G::Scalar, a: G, b: G, cs: impl Iterator> + ExactSizeIterator, @@ -745,7 +764,7 @@ fn generate_proof( let t3 = m * &r; let challenge_dst = - GenericArray::from(*STR_CHALLENGE).concat(get_context_string::(Mode::Verifiable)?); + GenericArray::from(STR_CHALLENGE).concat(get_context_string::(Mode::Verifiable)?); chain!( h2_input, serialize_owned::(b.to_arr())?, @@ -757,7 +776,7 @@ fn generate_proof( ); let hash_to_scalar_dst = - GenericArray::from(*STR_HASH_TO_SCALAR).concat(get_context_string::(Mode::Verifiable)?); + GenericArray::from(STR_HASH_TO_SCALAR).concat(get_context_string::(Mode::Verifiable)?); let c_scalar = G::hash_to_scalar::(h2_input, hash_to_scalar_dst)?; let s_scalar = r - &(c_scalar * &k); @@ -775,14 +794,14 @@ fn verify_proof( b: G, cs: impl Iterator> + ExactSizeIterator, ds: impl Iterator> + ExactSizeIterator, - proof: Proof, + proof: &Proof, ) -> Result<(), InternalError> { 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); let challenge_dst = - GenericArray::from(*STR_CHALLENGE).concat(get_context_string::(Mode::Verifiable)?); + GenericArray::from(STR_CHALLENGE).concat(get_context_string::(Mode::Verifiable)?); chain!( h2_input, serialize_owned::(b.to_arr())?, @@ -794,7 +813,7 @@ fn verify_proof( ); let hash_to_scalar_dst = - GenericArray::from(*STR_HASH_TO_SCALAR).concat(get_context_string::(Mode::Verifiable)?); + GenericArray::from(STR_HASH_TO_SCALAR).concat(get_context_string::(Mode::Verifiable)?); let c = G::hash_to_scalar::(h2_input, hash_to_scalar_dst)?; match c.ct_eq(&proof.c_scalar).into() { @@ -812,8 +831,8 @@ fn finalize_after_unblind< inputs_and_unblinded_elements: I, info: &[u8], mode: Mode, -) -> Result::OutputSize>>, InternalError> { - let finalize_dst = GenericArray::from(*STR_FINALIZE).concat(get_context_string::(mode)?); +) -> Result>, InternalError> { + let finalize_dst = GenericArray::from(STR_FINALIZE).concat(get_context_string::(mode)?); inputs_and_unblinded_elements .map(|(input, unblinded_element)| { @@ -826,14 +845,14 @@ fn finalize_after_unblind< ); Ok(hash_input - .fold(::new(), |h, bytes| h.chain(bytes)) + .fold(H::new(), |h, bytes| h.chain(bytes)) .finalize()) }) .collect() } fn compute_composites( - k_option: Option<::Scalar>, + k_option: Option, b: G, c_slice: impl Iterator> + ExactSizeIterator, d_slice: impl Iterator> + ExactSizeIterator, @@ -842,9 +861,9 @@ fn compute_composites( return Err(InternalError::MismatchedLengthsForCompositeInputs); } - let seed_dst = GenericArray::from(*STR_SEED).concat(get_context_string::(Mode::Verifiable)?); + let seed_dst = GenericArray::from(STR_SEED).concat(get_context_string::(Mode::Verifiable)?); let composite_dst = - GenericArray::from(*STR_COMPOSITE).concat(get_context_string::(Mode::Verifiable)?); + GenericArray::from(STR_COMPOSITE).concat(get_context_string::(Mode::Verifiable)?); chain!( h1_input, @@ -852,7 +871,7 @@ fn compute_composites( serialize_owned::(seed_dst)?, ); let seed = h1_input - .fold(::new(), |h, bytes| h.chain(bytes)) + .fold(H::new(), |h, bytes| h.chain(bytes)) .finalize(); let mut m = G::identity(); @@ -866,7 +885,7 @@ fn compute_composites( serialize_owned::(d.value.to_arr())?, serialize_owned::(composite_dst)?, ); - let dst = GenericArray::from(*STR_HASH_TO_SCALAR) + let dst = GenericArray::from(STR_HASH_TO_SCALAR) .concat(get_context_string::(Mode::Verifiable)?); let di = G::hash_to_scalar::(h2_input, dst)?; m = c.value * &di + &m; @@ -887,7 +906,7 @@ fn compute_composites( /// Generates the contextString parameter as defined in /// fn get_context_string(mode: Mode) -> Result, InternalError> { - Ok(GenericArray::from(*STR_VOPRF) + Ok(GenericArray::from(STR_VOPRF) .concat(i2osp::(mode as usize)?) .concat(i2osp::(G::SUITE_ID)?)) } @@ -901,31 +920,32 @@ fn get_context_string(mode: Mode) -> Result, Int mod tests { use super::*; use crate::group::Group; + use alloc::vec; use generic_array::GenericArray; use rand::rngs::OsRng; use zeroize::Zeroize; fn prf( input: &[u8], - key: ::Scalar, + key: G::Scalar, info: &[u8], mode: Mode, - ) -> GenericArray::OutputSize> { + ) -> GenericArray { let dst = - GenericArray::from(*STR_HASH_TO_GROUP).concat(get_context_string::(mode).unwrap()); + GenericArray::from(STR_HASH_TO_GROUP).concat(get_context_string::(mode).unwrap()); let point = G::hash_to_curve::(input, dst).unwrap(); chain!(context, - STR_CONTEXT => |x| Some(x), + STR_CONTEXT => |x| Some(x.as_ref()), get_context_string::(mode).unwrap() => |x| Some(x.as_slice()), serialize::(info).unwrap(), ); let dst = - GenericArray::from(*STR_HASH_TO_SCALAR).concat(get_context_string::(mode).unwrap()); - let m = ::hash_to_scalar::(context, dst).unwrap(); + GenericArray::from(STR_HASH_TO_SCALAR).concat(get_context_string::(mode).unwrap()); + let m = G::hash_to_scalar::(context, dst).unwrap(); - let res = point * &::scalar_invert(&(key + &m)); + let res = point * &G::scalar_invert(&(key + &m)); finalize_after_unblind::(Some((input, res)).into_iter(), info, mode).unwrap()[0] .clone() @@ -939,11 +959,11 @@ mod tests { NonVerifiableClient::::blind(input.to_vec(), &mut rng).unwrap(); let server = NonVerifiableServer::::new(&mut rng).unwrap(); let server_result = server - .evaluate(client_blind_result.message, Some(info)) + .evaluate(&client_blind_result.message, Some(info)) .unwrap(); let client_finalize_result = client_blind_result .state - .finalize(server_result.message, Some(info)) + .finalize(&server_result.message, Some(info)) .unwrap(); let res2 = prf::(input, server.get_private_key(), info, Mode::Base); assert_eq!(client_finalize_result, res2); @@ -957,13 +977,13 @@ mod tests { VerifiableClient::::blind(input.to_vec(), &mut rng).unwrap(); let server = VerifiableServer::::new(&mut rng).unwrap(); let server_result = server - .evaluate(&mut rng, client_blind_result.message, Some(info)) + .evaluate(&mut rng, &client_blind_result.message, Some(info)) .unwrap(); let client_finalize_result = client_blind_result .state .finalize( - server_result.message, - server_result.proof, + &server_result.message, + &server_result.proof, server.get_public_key(), Some(info), ) @@ -980,15 +1000,15 @@ mod tests { VerifiableClient::::blind(input.to_vec(), &mut rng).unwrap(); let server = VerifiableServer::::new(&mut rng).unwrap(); let server_result = server - .evaluate(&mut rng, client_blind_result.message, Some(info)) + .evaluate(&mut rng, &client_blind_result.message, Some(info)) .unwrap(); let wrong_pk = { // Choose a group element that is unlikely to be the right public key G::hash_to_curve::(b"msg", (*b"dst").into()).unwrap() }; let client_finalize_result = client_blind_result.state.finalize( - server_result.message, - server_result.proof, + &server_result.message, + &server_result.proof, wrong_pk, Some(info), ); @@ -1018,7 +1038,7 @@ mod tests { let client_finalize_result = VerifiableClient::batch_finalize( &client_states, &server_result.messages, - server_result.proof, + &server_result.proof, server.get_public_key(), Some(info), ) @@ -1058,7 +1078,7 @@ mod tests { let client_finalize_result = VerifiableClient::batch_finalize( &client_states, &server_result.messages, - server_result.proof, + &server_result.proof, wrong_pk, Some(info), ); @@ -1075,7 +1095,7 @@ mod tests { let client_finalize_result = client_blind_result .state .finalize( - EvaluationElement { + &EvaluationElement { value: client_blind_result.message.value, hash: PhantomData, }, @@ -1083,7 +1103,7 @@ mod tests { ) .unwrap(); - let dst = GenericArray::from(*STR_HASH_TO_GROUP) + let dst = GenericArray::from(STR_HASH_TO_GROUP) .concat(get_context_string::(Mode::Base).unwrap()); let point = G::hash_to_curve::(&input, dst).unwrap(); let res2 = finalize_after_unblind::( @@ -1135,7 +1155,7 @@ mod tests { NonVerifiableClient::::blind(input.to_vec(), &mut rng).unwrap(); let server = NonVerifiableServer::::new(&mut rng).unwrap(); let server_result = server - .evaluate(client_blind_result.message, Some(info)) + .evaluate(&client_blind_result.message, Some(info)) .unwrap(); let mut state = server; @@ -1155,7 +1175,7 @@ mod tests { VerifiableClient::::blind(input.to_vec(), &mut rng).unwrap(); let server = VerifiableServer::::new(&mut rng).unwrap(); let server_result = server - .evaluate(&mut rng, client_blind_result.message, Some(info)) + .evaluate(&mut rng, &client_blind_result.message, Some(info)) .unwrap(); let mut state = server; @@ -1173,20 +1193,22 @@ mod tests { #[test] fn test_functionality() -> Result<(), InternalError> { - use curve25519_dalek::ristretto::RistrettoPoint; - use sha2::Sha512; + cfg_ristretto! { { + use curve25519_dalek::ristretto::RistrettoPoint; + use sha2::Sha512; - base_retrieval::(); - base_inversion_unsalted::(); - verifiable_retrieval::(); - verifiable_batch_retrieval::(); - verifiable_bad_public_key::(); - verifiable_batch_bad_public_key::(); + base_retrieval::(); + base_inversion_unsalted::(); + verifiable_retrieval::(); + verifiable_batch_retrieval::(); + verifiable_bad_public_key::(); + verifiable_batch_bad_public_key::(); - zeroize_base_client::(); - zeroize_base_server::(); - zeroize_verifiable_client::(); - zeroize_verifiable_server::(); + zeroize_base_client::(); + zeroize_base_server::(); + zeroize_verifiable_client::(); + zeroize_verifiable_server::(); + } } #[cfg(feature = "p256")] {