From 16e072dcd4ce4d9373b07d873dccbec1ba520fc0 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 21 Jan 2022 22:52:09 +0100 Subject: [PATCH] `Group` trait overhaul part 2 (#53) * Rely on elliptic-curve for hash-to-curve and P-256 implementations * Update MSRV * Remove unnecessary `#[macro_use]` * Re-introduce `CipherSuite` * Provide types for length shortcuts * Remove `SUITE_ID` from `Group` * Blanket implementation for RustCrypto `Curve`s * Remove the p256 crate feature * Rename `ristretto_*` crate features to `ristretto-*` for consistency * Remove unnecessary allowed Clippy lints * Remove some unnecessary constraints --- .github/workflows/main.yml | 27 +- Cargo.toml | 45 +- README.md | 2 +- src/ciphersuite.rs | 48 ++ src/group/elliptic_curve.rs | 113 ++++ src/group/expand.rs | 215 ------- src/group/mod.rs | 44 +- src/group/p256.rs | 586 ------------------- src/group/ristretto.rs | 57 +- src/group/tests.rs | 11 +- src/lib.rs | 196 +++---- src/serialization.rs | 176 +++--- src/tests/voprf_test_vectors.rs | 178 +++--- src/util.rs | 8 +- src/voprf.rs | 975 +++++++++++++++++--------------- 15 files changed, 1038 insertions(+), 1643 deletions(-) create mode 100644 src/ciphersuite.rs create mode 100644 src/group/elliptic_curve.rs delete mode 100644 src/group/expand.rs delete mode 100644 src/group/p256.rs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0b1bc48..310e708 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,22 +35,16 @@ jobs: fail-fast: false matrix: backend_feature: - - ristretto255_u64 - - ristretto255_u32 - - p256 - - ristretto255_u64,p256 + - --features ristretto255-ciphersuite,ristretto255-u64 + - --features ristretto255-ciphersuite,ristretto255-u32 + - frontend_feature: - - --features danger - --features serde toolchain: - stable - - 1.51.0 - exclude: - - backend_feature: p256 - toolchain: 1.51.0 - - backend_feature: ristretto255_u64,p256 - toolchain: 1.51.0 + - 1.57.0 name: test steps: - name: Checkout sources @@ -67,19 +61,19 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: --no-default-features --features ${{ matrix.backend_feature }} + args: --no-default-features ${{ matrix.backend_feature }} - name: Run cargo test with alloc uses: actions-rs/cargo@v1 with: command: test - args: --no-default-features ${{ matrix.frontend_feature }},alloc --features ${{ matrix.backend_feature }} + args: --no-default-features ${{ matrix.frontend_feature }},alloc ${{ matrix.backend_feature }} - name: Run cargo test with std uses: actions-rs/cargo@v1 with: command: test - args: --no-default-features ${{ matrix.frontend_feature }},std --features ${{ matrix.backend_feature }} + args: --no-default-features ${{ matrix.frontend_feature }},std ${{ matrix.backend_feature }} build-no-std: name: Build with no-std on ${{ matrix.target }} @@ -94,9 +88,8 @@ jobs: - thumbv6m-none-eabi backend_feature: - - - --features ristretto255_u64 - - --features ristretto255_u32 - - --features p256 + - --features ristretto255-ciphersuite,ristretto255-u64 + - --features ristretto255-ciphersuite,ristretto255-u32 frontend_feature: - - --features danger @@ -135,7 +128,7 @@ jobs: RUSTDOCFLAGS: -D warnings with: command: doc - args: --no-deps --document-private-items --features std,p256 + args: --no-deps --document-private-items --features danger,std rustfmt: diff --git a/Cargo.toml b/Cargo.toml index 816ac30..1aa7b87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,34 +2,26 @@ authors = ["Kevin Lewi "] categories = ["no-std", "algorithms", "cryptography"] description = "An implementation of a verifiable oblivious pseudorandom function (VOPRF)" -edition = "2018" +edition = "2021" keywords = ["oprf"] license = "MIT" name = "voprf" readme = "README.md" repository = "https://github.com/novifinancial/voprf/" -resolver = "2" -rust-version = "1.51" +rust-version = "1.57" version = "0.3.0" [features] alloc = [] danger = [] -default = ["ristretto255_u64", "serde"] -p256 = [ - "alloc", - "num-bigint", - "num-integer", - "num-traits", - "once_cell", - "p256_", -] +default = ["ristretto255-ciphersuite", "ristretto255-u64", "serde"] ristretto255 = ["generic-array/more_lengths"] -ristretto255_fiat_u32 = ["curve25519-dalek/fiat_u32_backend", "ristretto255"] -ristretto255_fiat_u64 = ["curve25519-dalek/fiat_u64_backend", "ristretto255"] -ristretto255_simd = ["curve25519-dalek/simd_backend", "ristretto255"] -ristretto255_u32 = ["curve25519-dalek/u32_backend", "ristretto255"] -ristretto255_u64 = ["curve25519-dalek/u64_backend", "ristretto255"] +ristretto255-ciphersuite = ["ristretto255", "sha2"] +ristretto255-fiat-u32 = ["curve25519-dalek/fiat_u32_backend", "ristretto255"] +ristretto255-fiat-u64 = ["curve25519-dalek/fiat_u64_backend", "ristretto255"] +ristretto255-simd = ["curve25519-dalek/simd_backend", "ristretto255"] +ristretto255-u32 = ["curve25519-dalek/u32_backend", "ristretto255"] +ristretto255-u64 = ["curve25519-dalek/u64_backend", "ristretto255"] std = ["alloc"] [dependencies] @@ -37,18 +29,17 @@ curve25519-dalek = { version = "3", default-features = false, optional = true } derive-where = { version = "1.0.0-rc.1", features = ["zeroize"] } digest = "0.10" displaydoc = { version = "0.2", default-features = false } +elliptic-curve = { version = "0.12.0-pre.1", features = [ + "hash2curve", + "sec1", + "voprf", +] } generic-array = "0.14" -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.10", default-features = false, features = [ - "arithmetic", -], optional = true } rand_core = { version = "0.6", default-features = false } serde = { version = "1", default-features = false, features = [ "derive", ], optional = true } +sha2 = { version = "0.10", default-features = false, optional = true } subtle = { version = "2.3", default-features = false } zeroize = { version = "1", default-features = false } @@ -56,11 +47,15 @@ zeroize = { version = "1", default-features = false } generic-array = { version = "0.14", features = ["more_lengths"] } hex = "0.4" json = "0.12" +p256 = { version = "0.11.0-pre.0", default-features = false, features = [ + "hash2curve", + "voprf", +] } proptest = "1" rand = "0.8" regex = "1" sha2 = "0.10" [package.metadata.docs.rs] -features = ["danger", "p256", "std"] +features = ["danger", "std"] targets = [] diff --git a/README.md b/README.md index ea87546..90d06d0 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ voprf = "0.3" ### Minimum Supported Rust Version -Rust **1.51** or higher. +Rust **1.57** or higher. Contributors ------------ diff --git a/src/ciphersuite.rs b/src/ciphersuite.rs new file mode 100644 index 0000000..87a295c --- /dev/null +++ b/src/ciphersuite.rs @@ -0,0 +1,48 @@ +// 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. + +//! Defines the CipherSuite trait to specify the underlying primitives for VOPRF + +use digest::core_api::BlockSizeUser; +use digest::{Digest, OutputSizeUser}; +use elliptic_curve::VoprfParameters; +use generic_array::typenum::{IsLess, IsLessOrEqual, U256}; + +use crate::Group; + +/// Configures the underlying primitives used in VOPRF +pub trait CipherSuite +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + /// The ciphersuite identifier as dictated by + /// + const ID: u16; + + /// A finite cyclic group along with a point representation that allows some + /// customization on how to hash an input to a curve point. See [`Group`]. + type Group: Group; + + /// The main hash function to use (for HKDF computations and hashing + /// transcripts). + type Hash: BlockSizeUser + Digest; +} + +impl CipherSuite for T +where + T: Group, + T::Hash: BlockSizeUser + Digest, + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + const ID: u16 = T::ID; + + type Group = T; + + type Hash = T::Hash; +} diff --git a/src/group/elliptic_curve.rs b/src/group/elliptic_curve.rs new file mode 100644 index 0000000..ae84642 --- /dev/null +++ b/src/group/elliptic_curve.rs @@ -0,0 +1,113 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// This source code is licensed under both the MIT license found in the +// LICENSE-MIT file in the root directory of this source tree and the Apache +// License, Version 2.0 found in the LICENSE-APACHE file in the root directory +// of this source tree. + +use digest::core_api::BlockSizeUser; +use digest::OutputSizeUser; +use elliptic_curve::group::cofactor::CofactorGroup; +use elliptic_curve::hash2curve::{ExpandMsgXmd, FromOkm, GroupDigest}; +use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint}; +use elliptic_curve::{ + AffinePoint, Field, FieldSize, Group as _, ProjectivePoint, PublicKey, Scalar, SecretKey, +}; +use generic_array::sequence::Concat; +use generic_array::typenum::{IsLess, IsLessOrEqual, U256}; +use generic_array::GenericArray; +use rand_core::{CryptoRng, RngCore}; + +use super::Group; +use crate::group::{STR_HASH_TO_GROUP, STR_HASH_TO_SCALAR}; +use crate::voprf::{self, Mode}; +use crate::{CipherSuite, Error, Result}; + +impl Group for C +where + C: GroupDigest, + ProjectivePoint: CofactorGroup, + FieldSize: ModulusSize, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + Scalar: FromOkm, +{ + type Elem = ProjectivePoint; + + type ElemLen = as ModulusSize>::CompressedPointSize; + + type Scalar = Scalar; + + type ScalarLen = FieldSize; + + // Implements the `hash_to_curve()` function from + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3 + fn hash_to_curve(msg: &[&[u8]], mode: Mode) -> Result + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { + let dst = + GenericArray::from(STR_HASH_TO_GROUP).concat(voprf::get_context_string::(mode)); + + Self::hash_from_bytes::>(msg, &dst).map_err(|_| Error::PointError) + } + + // Implements the `HashToScalar()` function + fn hash_to_scalar(input: &[&[u8]], mode: Mode) -> Result + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { + let dst = + GenericArray::from(STR_HASH_TO_SCALAR).concat(voprf::get_context_string::(mode)); + + ::hash_to_scalar::>(input, &dst) + .map_err(|_| Error::PointError) + } + + fn base_elem() -> Self::Elem { + ProjectivePoint::::generator() + } + + fn identity_elem() -> Self::Elem { + ProjectivePoint::::identity() + } + + fn serialize_elem(elem: Self::Elem) -> GenericArray { + let point: AffinePoint = elem.into(); + let bytes = point.to_encoded_point(true); + let bytes = bytes.as_bytes(); + let mut result = GenericArray::default(); + result[..bytes.len()].copy_from_slice(bytes); + result + } + + fn deserialize_elem(element_bits: &GenericArray) -> Result { + PublicKey::::from_sec1_bytes(element_bits) + .map(|public_key| public_key.to_projective()) + .map_err(|_| Error::PointError) + } + + fn random_scalar(rng: &mut R) -> Self::Scalar { + *SecretKey::::random(rng).to_nonzero_scalar() + } + + fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar { + Option::from(scalar.invert()).unwrap() + } + + #[cfg(test)] + fn zero_scalar() -> Self::Scalar { + Scalar::::zero() + } + + fn serialize_scalar(scalar: Self::Scalar) -> GenericArray { + scalar.into() + } + + fn deserialize_scalar(scalar_bits: &GenericArray) -> Result { + SecretKey::::from_be_bytes(scalar_bits) + .map(|secret_key| *secret_key.to_nonzero_scalar()) + .map_err(|_| Error::ScalarError) + } +} diff --git a/src/group/expand.rs b/src/group/expand.rs deleted file mode 100644 index ec54907..0000000 --- a/src/group/expand.rs +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. -// -// This source code is licensed under both the MIT license found in the -// LICENSE-MIT file in the root directory of this source tree and the Apache -// License, Version 2.0 found in the LICENSE-APACHE file in the root directory -// of this source tree. - -use core::convert::TryFrom; - -use digest::core_api::{Block, BlockSizeUser}; -use digest::{Digest, FixedOutputReset}; -use generic_array::typenum::{IsLess, NonZero, Unsigned, U65536}; -use generic_array::{ArrayLength, GenericArray}; - -use crate::{Error, Result}; - -fn xor>(x: GenericArray, y: GenericArray) -> GenericArray { - x.into_iter().zip(y).map(|(x1, x2)| x1 ^ x2).collect() -} - -/// Corresponds to the expand_message_xmd() function defined in -/// -pub fn expand_message_xmd>( - msg: &[&[u8]], - dst: &[u8], -) -> Result> -where - // Constraint set by `expand_message_xmd`: - // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#section-5.4.1-6 - L: NonZero + IsLess, -{ - // DST, a byte string of at most 255 bytes. - let dst_len = u8::try_from(dst.len()).map_err(|_| Error::HashToCurveError)?; - - // b_in_bytes, b / 8 for b the output size of H in bits. - let b_in_bytes = H::OutputSize::to_usize(); - - // Constraint set by `expand_message_xmd`: - // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#section-5.4.1-4 - if b_in_bytes > H::BlockSize::USIZE { - return Err(Error::HashToCurveError); - } - - // ell = ceil(len_in_bytes / b_in_bytes) - // ABORT if ell > 255 - let ell = u8::try_from((L::USIZE + b_in_bytes - 1) / b_in_bytes) - .map_err(|_| Error::HashToCurveError)?; - - let mut hash = H::new(); - - // b_0 = H(msg_prime) - // msg_prime = Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime - // Z_pad = I2OSP(0, s_in_bytes) - // s_in_bytes, the input block size of H, measured in bytes - Digest::update(&mut hash, Block::::default()); - for msg in msg { - Digest::update(&mut hash, msg); - } - // l_i_b_str = I2OSP(len_in_bytes, 2) - Digest::update(&mut hash, L::U16.to_be_bytes()); - Digest::update(&mut hash, [0]); - // DST_prime = DST || I2OSP(len(DST), 1) - Digest::update(&mut hash, dst); - Digest::update(&mut hash, [dst_len]); - let b_0 = hash.finalize_reset(); - - let mut b_i = GenericArray::default(); - - let mut uniform_bytes = GenericArray::default(); - - // b_1 = H(b_0 || I2OSP(1, 1) || DST_prime) - // for i in (2, ..., ell): - for (i, chunk) in (1..(ell + 1)).zip(uniform_bytes.chunks_mut(b_in_bytes)) { - // b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime) - Digest::update(&mut hash, xor(b_0.clone(), b_i.clone())); - Digest::update(&mut hash, [i]); - // DST_prime = DST || I2OSP(len(DST), 1) - Digest::update(&mut hash, dst); - Digest::update(&mut hash, [dst_len]); - b_i = hash.finalize_reset(); - // uniform_bytes = b_1 || ... || b_ell - // return substr(uniform_bytes, 0, len_in_bytes) - chunk.copy_from_slice(&b_i[..b_in_bytes.min(chunk.len())]); - } - - Ok(uniform_bytes) -} - -#[cfg(test)] -mod tests { - use generic_array::typenum::{U128, U32}; - - struct Params { - msg: &'static str, - len_in_bytes: usize, - uniform_bytes: &'static str, - } - - #[test] - fn test_expand_message_xmd() { - const DST: [u8; 27] = *b"QUUX-V01-CS02-with-expander"; - - // Test vectors taken from Section K.1 of https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt - let test_vectors: alloc::vec::Vec = alloc::vec![ - Params { - msg: "", - len_in_bytes: 0x20, - uniform_bytes: "f659819a6473c1835b25ea59e3d38914c98b374f0970b7e4c\ - 92181df928fca88", - }, - Params { - msg: "abc", - len_in_bytes: 0x20, - uniform_bytes: "1c38f7c211ef233367b2420d04798fa4698080a8901021a79\ - 5a1151775fe4da7", - }, - Params { - msg: "abcdef0123456789", - len_in_bytes: 0x20, - uniform_bytes: "8f7e7b66791f0da0dbb5ec7c22ec637f79758c0a48170bfb7c4611bd304ece89", - }, - Params { - msg: "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ - qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ - qqqqqqqqqqqqqqqqqqqqqqqqq", - len_in_bytes: 0x20, - uniform_bytes: "72d5aa5ec810370d1f0013c0df2f1d65699494ee2a39f72e\ - 1716b1b964e1c642", - }, - Params { - msg: "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - len_in_bytes: 0x20, - uniform_bytes: "3b8e704fc48336aca4c2a12195b720882f2162a4b7b13a9c\ - 350db46f429b771b", - }, - Params { - msg: "", - len_in_bytes: 0x80, - uniform_bytes: "8bcffd1a3cae24cf9cd7ab85628fd111bb17e3739d3b53f8\ - 9580d217aa79526f1708354a76a402d3569d6a9d19ef3de4d0b991\ - e4f54b9f20dcde9b95a66824cbdf6c1a963a1913d43fd7ac443a02\ - fc5d9d8d77e2071b86ab114a9f34150954a7531da568a1ea8c7608\ - 61c0cde2005afc2c114042ee7b5848f5303f0611cf297f", - }, - Params { - msg: "abc", - len_in_bytes: 0x80, - uniform_bytes: "fe994ec51bdaa821598047b3121c149b364b178606d5e72b\ - fbb713933acc29c186f316baecf7ea22212f2496ef3f785a27e84a\ - 40d8b299cec56032763eceeff4c61bd1fe65ed81decafff4a31d01\ - 98619c0aa0c6c51fca15520789925e813dcfd318b542f879944127\ - 1f4db9ee3b8092a7a2e8d5b75b73e28fb1ab6b4573c192", - }, - Params { - msg: "abcdef0123456789", - len_in_bytes: 0x80, - uniform_bytes: "c9ec7941811b1e19ce98e21db28d22259354d4d0643e3011\ - 75e2f474e030d32694e9dd5520dde93f3600d8edad94e5c3649030\ - 88a7228cc9eff685d7eaac50d5a5a8229d083b51de4ccc3733917f\ - 4b9535a819b445814890b7029b5de805bf62b33a4dc7e24acdf2c9\ - 24e9fe50d55a6b832c8c84c7f82474b34e48c6d43867be", - }, - Params { - msg: "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ - qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ - qqqqqqqqqqqqqqqqqqqqqqqqq", - len_in_bytes: 0x80, - uniform_bytes: "48e256ddba722053ba462b2b93351fc966026e6d6db49318\ - 9798181c5f3feea377b5a6f1d8368d7453faef715f9aecb078cd40\ - 2cbd548c0e179c4ed1e4c7e5b048e0a39d31817b5b24f50db58bb3\ - 720fe96ba53db947842120a068816ac05c159bb5266c63658b4f00\ - 0cbf87b1209a225def8ef1dca917bcda79a1e42acd8069", - }, - Params { - msg: "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - len_in_bytes: 0x80, - uniform_bytes: "396962db47f749ec3b5042ce2452b619607f27fd3939ece2\ - 746a7614fb83a1d097f554df3927b084e55de92c7871430d6b95c2\ - a13896d8a33bc48587b1f66d21b128a1a8240d5b0c26dfe795a1a8\ - 42a0807bb148b77c2ef82ed4b6c9f7fcb732e7f94466c8b51e52bf\ - 378fba044a31f5cb44583a892f5969dcd73b3fa128816e", - }, - ]; - - for tv in test_vectors { - let uniform_bytes = match tv.len_in_bytes { - 32 => super::expand_message_xmd::(&[tv.msg.as_bytes()], &DST) - .map(|bytes| bytes.to_vec()), - 128 => super::expand_message_xmd::(&[tv.msg.as_bytes()], &DST) - .map(|bytes| bytes.to_vec()), - _ => unimplemented!(), - } - .unwrap(); - assert_eq!(tv.uniform_bytes, hex::encode(uniform_bytes)); - } - } -} diff --git a/src/group/mod.rs b/src/group/mod.rs index 47d5fb7..ad2444f 100644 --- a/src/group/mod.rs +++ b/src/group/mod.rs @@ -7,17 +7,15 @@ //! Defines the Group trait to specify the underlying prime order group -#[cfg(any(feature = "ristretto255", feature = "p256",))] -mod expand; -#[cfg(feature = "p256")] -mod p256; +mod elliptic_curve; #[cfg(feature = "ristretto255")] mod ristretto; use core::ops::{Add, Mul, Sub}; use digest::core_api::BlockSizeUser; -use digest::{Digest, FixedOutputReset}; +use digest::OutputSizeUser; +use generic_array::typenum::{IsLess, IsLessOrEqual, U256}; use generic_array::{ArrayLength, GenericArray}; use rand_core::{CryptoRng, RngCore}; #[cfg(feature = "ristretto255")] @@ -26,7 +24,7 @@ use subtle::ConstantTimeEq; use zeroize::Zeroize; use crate::voprf::Mode; -use crate::Result; +use crate::{CipherSuite, Result}; pub(crate) const STR_HASH_TO_SCALAR: [u8; 13] = *b"HashToScalar-"; pub(crate) const STR_HASH_TO_GROUP: [u8; 12] = *b"HashToGroup-"; @@ -34,43 +32,37 @@ pub(crate) const STR_HASH_TO_GROUP: [u8; 12] = *b"HashToGroup-"; /// 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. pub trait Group { - /// The ciphersuite identifier as dictated by - /// - const SUITE_ID: u16; - /// The type of group elements type Elem: Copy - + Sized - + ConstantTimeEq + Zeroize - + for<'a> Mul<&'a Self::Scalar, Output = Self::Elem> - + for<'a> Add<&'a Self::Elem, Output = Self::Elem>; + + for<'a> Add<&'a Self::Elem, Output = Self::Elem> + + for<'a> Mul<&'a Self::Scalar, Output = Self::Elem>; /// The byte length necessary to represent group elements type ElemLen: ArrayLength + 'static; /// The type of base field scalars - type Scalar: Zeroize + type Scalar: ConstantTimeEq + Copy - + ConstantTimeEq + + Zeroize + for<'a> Add<&'a Self::Scalar, Output = Self::Scalar> - + for<'a> Sub<&'a Self::Scalar, Output = Self::Scalar> - + for<'a> Mul<&'a Self::Scalar, Output = Self::Scalar>; + + for<'a> Mul<&'a Self::Scalar, Output = Self::Scalar> + + for<'a> Sub<&'a Self::Scalar, Output = Self::Scalar>; /// The byte length necessary to represent scalars type ScalarLen: ArrayLength + 'static; /// transforms a password and domain separation tag (DST) into a curve point - fn hash_to_curve( - msg: &[&[u8]], - mode: Mode, - ) -> Result; + fn hash_to_curve(msg: &[&[u8]], mode: Mode) -> Result + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>; /// Hashes a slice of pseudo-random bytes to a scalar - fn hash_to_scalar( - input: &[&[u8]], - mode: Mode, - ) -> Result; + fn hash_to_scalar(input: &[&[u8]], mode: Mode) -> Result + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>; /// Get the base point for the group fn base_elem() -> Self::Elem; diff --git a/src/group/p256.rs b/src/group/p256.rs deleted file mode 100644 index da8cb7b..0000000 --- a/src/group/p256.rs +++ /dev/null @@ -1,586 +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. - -// Note: This group implementation of p256 is experimental for now, until -// hash-to-curve or crypto-bigint are fully supported. - -#![allow( - clippy::borrow_interior_mutable_const, - clippy::declare_interior_mutable_const -)] - -use core::ops::{Add, Div, Mul, Neg}; -use core::str::FromStr; - -use digest::core_api::BlockSizeUser; -use digest::{Digest, FixedOutputReset}; -use generic_array::sequence::Concat; -use generic_array::typenum::{Unsigned, U2, U32, U33, U48}; -use generic_array::{ArrayLength, GenericArray}; -use num_bigint::{BigInt, Sign}; -use num_integer::Integer; -use num_traits::{One, ToPrimitive, Zero}; -use once_cell::unsync::Lazy; -use p256_::elliptic_curve::bigint::{Encoding, U384}; -use p256_::elliptic_curve::group::prime::PrimeCurveAffine; -use p256_::elliptic_curve::ops::Reduce; -use p256_::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint}; -#[cfg(test)] -use p256_::elliptic_curve::Field; -use p256_::{AffinePoint, EncodedPoint, NistP256, ProjectivePoint, PublicKey, Scalar, SecretKey}; -use rand_core::{CryptoRng, RngCore}; -use subtle::{Choice, ConditionallySelectable}; - -use super::Group; -use crate::group::{STR_HASH_TO_GROUP, STR_HASH_TO_SCALAR}; -use crate::voprf::{self, Mode}; -use crate::{Error, Result}; - -// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.2 -// `L: 48` -pub type L = U48; - -#[cfg(feature = "p256")] -impl Group for NistP256 { - const SUITE_ID: u16 = 0x0003; - - type Elem = ProjectivePoint; - - type ElemLen = U33; - - type Scalar = Scalar; - - type ScalarLen = U32; - - // Implements the `hash_to_curve()` function from - // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3 - fn hash_to_curve( - msg: &[&[u8]], - mode: Mode, - ) -> Result { - let dst = - GenericArray::from(STR_HASH_TO_GROUP).concat(voprf::get_context_string::(mode)); - - // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.2 - // `p: 2^256 - 2^224 + 2^192 + 2^96 - 1` - const P: Lazy = Lazy::new(|| { - BigInt::from_str( - "115792089210356248762697446949407573530086143415290314195533631308867097853951", - ) - .unwrap() - }); - // `A: -3` - const A: Lazy = Lazy::new(|| BigInt::from(-3)); - // `B: 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b` - const B: Lazy = Lazy::new(|| { - BigInt::parse_bytes( - b"5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", - 16, - ) - .unwrap() - }); - // `Z: -10` - const Z: Lazy = Lazy::new(|| BigInt::from(-10)); - - // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3 - // `hash_to_curve` calls `hash_to_field` with a `count` of `2` - // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3 - // `hash_to_field` calls `expand_message` with a `len_in_bytes` of `count * L` - let uniform_bytes = - super::expand::expand_message_xmd::>::Output>(msg, &dst)?; - - // hash to curve - let (q0x, q0y) = hash_to_curve_simple_swu(&uniform_bytes[..L::USIZE], &A, &B, &P, &Z); - let (q1x, q1y) = hash_to_curve_simple_swu(&uniform_bytes[L::USIZE..], &A, &B, &P, &Z); - - // convert to `p256` types - let p0 = Option::::from(AffinePoint::from_encoded_point( - &EncodedPoint::from_affine_coordinates(&q0x, &q0y, false), - )) - .ok_or(Error::PointError)? - .to_curve(); - let p1 = Option::::from(AffinePoint::from_encoded_point( - &EncodedPoint::from_affine_coordinates(&q1x, &q1y, false), - )) - .ok_or(Error::PointError)?; - - Ok(p0 + p1) - } - - // Implements the `HashToScalar()` function - fn hash_to_scalar( - input: &[&[u8]], - mode: Mode, - ) -> Result { - let dst = - GenericArray::from(STR_HASH_TO_SCALAR).concat(voprf::get_context_string::(mode)); - - // https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf#[{%22num%22:211,%22gen%22:0},{%22name%22:%22XYZ%22},70,700,0] - // P-256 `n` is defined as - // `115792089210356248762697446949407573529996955224135760342 - // 422259061068512044369` - const N: U384 = - U384::from_be_hex("00000000000000000000000000000000FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"); - - // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3 - // `HashToScalar` is `hash_to_field` - let uniform_bytes = super::expand::expand_message_xmd::(input, &dst)?; - let bytes = Option::::from(U384::from_be_slice(&uniform_bytes).reduce(&N)) - .unwrap() - .to_be_bytes(); - - Ok(Scalar::from_be_bytes_reduced( - GenericArray::clone_from_slice(&bytes[16..]), - )) - } - - fn base_elem() -> Self::Elem { - ProjectivePoint::generator() - } - - fn identity_elem() -> Self::Elem { - ProjectivePoint::identity() - } - - fn serialize_elem(elem: Self::Elem) -> GenericArray { - let bytes = elem.to_affine().to_encoded_point(true); - let bytes = bytes.as_bytes(); - let mut result = GenericArray::default(); - result[..bytes.len()].copy_from_slice(bytes); - result - } - - fn deserialize_elem(element_bits: &GenericArray) -> Result { - PublicKey::from_sec1_bytes(element_bits) - .map(|public_key| public_key.to_projective()) - .map_err(|_| Error::PointError) - } - - fn random_scalar(rng: &mut R) -> Self::Scalar { - *SecretKey::random(rng).to_nonzero_scalar() - } - - fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar { - Option::from(scalar.invert()).unwrap() - } - - #[cfg(test)] - fn zero_scalar() -> Self::Scalar { - Scalar::zero() - } - - fn serialize_scalar(scalar: Self::Scalar) -> GenericArray { - scalar.into() - } - - fn deserialize_scalar(scalar_bits: &GenericArray) -> Result { - SecretKey::from_be_bytes(scalar_bits) - .map(|secret_key| *secret_key.to_nonzero_scalar()) - .map_err(|_| Error::ScalarError) - } -} - -/// Corresponds to the hash_to_curve_simple_swu() function defined in -/// -/// -/// `cmov`, `mod_floor` and `modpow` needs to be made constant-time, which will -/// be supported after crypto-bigint is no longer experimental. See -/// for more context. - -#[allow(clippy::many_single_char_names)] -fn hash_to_curve_simple_swu>( - u: &[u8], - a: &BigInt, - b: &BigInt, - p: &BigInt, - z: &BigInt, -) -> (GenericArray, GenericArray) { - #[derive(Clone)] - struct Field<'a>(&'a BigInt); - - impl<'a> Field<'a> { - fn new(p: &'a BigInt) -> Self { - Self(p) - } - - fn element(&'a self, number: &BigInt) -> FieldElement<'a> { - FieldElement { - number: number.mod_floor(self.0), - f: self, - } - } - - fn one(&'a self) -> FieldElement<'a> { - self.element(&BigInt::one()) - } - } - - /// Finite field arithmetic - #[derive(Clone)] - struct FieldElement<'a> { - number: BigInt, - f: &'a Field<'a>, - } - - impl<'a> Add for FieldElement<'a> { - type Output = FieldElement<'a>; - - fn add(self, rhs: Self) -> Self::Output { - &self + &rhs - } - } - - impl<'a> Add for &FieldElement<'a> { - type Output = FieldElement<'a>; - - fn add(self, rhs: Self) -> Self::Output { - self.f.element(&(&self.number + &rhs.number)) - } - } - - impl<'a> Neg for FieldElement<'a> { - type Output = FieldElement<'a>; - - fn neg(self) -> Self::Output { - -&self - } - } - - impl<'a> Neg for &FieldElement<'a> { - type Output = FieldElement<'a>; - - fn neg(self) -> Self::Output { - self.f.element(&-&self.number) - } - } - - impl<'a> Mul for FieldElement<'a> { - type Output = FieldElement<'a>; - - fn mul(self, rhs: Self) -> Self::Output { - &self * &rhs - } - } - - impl<'a> Mul<&Self> for FieldElement<'a> { - type Output = FieldElement<'a>; - - fn mul(self, rhs: &Self) -> Self::Output { - &self * rhs - } - } - - impl<'a> Mul> for &FieldElement<'a> { - type Output = FieldElement<'a>; - - fn mul(self, rhs: FieldElement<'a>) -> Self::Output { - self * &rhs - } - } - - impl<'a> Mul for &FieldElement<'a> { - type Output = FieldElement<'a>; - - fn mul(self, rhs: Self) -> Self::Output { - self.f.element(&(&self.number * &rhs.number)) - } - } - - impl<'a> Div<&Self> for FieldElement<'a> { - type Output = FieldElement<'a>; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn div(self, rhs: &Self) -> Self::Output { - self * rhs.inv0() - } - } - - impl<'a> FieldElement<'a> { - fn square(&self) -> Self { - self * self - } - - fn pow_internal(&self, exponent: &BigInt) -> Self { - let exponent = exponent.mod_floor(&(self.f.0 - 1)); - Self { - number: self.number.modpow(&exponent, self.f.0), - f: self.f, - } - } - - /// Corresponds to the sqrt_3mod4() function defined in - /// - fn sqrt(&self) -> Self { - // constant - let c1 = (self.f.0 + 1) >> 2; - - self.pow_internal(&c1) - } - - /// Corresponds to the sgn0_m_eq_1() function defined in - /// - fn sgn0(&self) -> i32 { - (&self.number % 2_usize).to_i32().unwrap() - } - - /// See - fn inv0(&self) -> Self { - self.pow_internal(&(self.f.0 - 2)) - } - - fn is_zero(&self) -> bool { - self.number.is_zero() - } - - /// Corresponds to the is_square() function defined in - /// - fn is_square(&self) -> bool { - // constant - let exponent = (self.f.0 - 1) >> 1; - - let result = self.pow_internal(&exponent); - result.is_zero() || result.number.is_one() - } - - fn to_bytes>(&self) -> GenericArray { - let bytes = self.number.to_bytes_be().1; - let mut result = GenericArray::default(); - result[N::USIZE - bytes.len()..].copy_from_slice(&bytes); - result - } - } - - fn cmov<'a>(x: &FieldElement<'a>, y: &FieldElement<'a>, b: bool) -> FieldElement<'a> { - let f = x.f; - - let x_bytes = x.number.to_bytes_le().1; - let mut x = [0; 32]; - x[..x_bytes.len()].copy_from_slice(&x_bytes); - - let y_bytes = y.number.to_bytes_le().1; - let mut y = [0; 32]; - y[..y_bytes.len()].copy_from_slice(&y_bytes); - - let mut bytes = [0; 32]; - - let choice = Choice::from(u8::from(b)); - - for ((byte, x), y) in bytes.iter_mut().zip(&x).zip(&y) { - *byte = u8::conditional_select(x, y, choice); - } - - FieldElement { - f, - number: BigInt::from_bytes_le(Sign::Plus, &bytes), - } - } - - let f = Field::new(p); - let a = f.element(a); - let b = f.element(b); - let z = f.element(z); - let u = f.element(&BigInt::from_bytes_be(Sign::Plus, u)); - - // Constants: - // 1. c1 = -B / A - let c1 = -&b / &a; - // 2. c2 = -1 / Z - let c2 = -f.one() / &z; - - // Steps: - // 1. tv1 = Z * u^2 - let tv1 = z * u.square(); - // 2. tv2 = tv1^2 - let mut tv2 = tv1.square(); - // 3. x1 = tv1 + tv2 - let mut x1 = &tv1 + &tv2; - // 4. x1 = inv0(x1) - x1 = x1.inv0(); - // 5. e1 = x1 == 0 - let e1 = x1.is_zero(); - // 6. x1 = x1 + 1 - x1 = x1 + f.one(); - // 7. x1 = CMOV(x1, c2, e1) # If (tv1 + tv2) == 0, set x1 = -1 / Z - x1 = cmov(&x1, &c2, e1); - // 8. x1 = x1 * c1 # x1 = (-B / A) * (1 + (1 / (Z^2 * u^4 + Z * u^2))) - x1 = x1 * c1; - // 9. gx1 = x1^2 - let mut gx1 = x1.square(); - // 10. gx1 = gx1 + A - gx1 = gx1 + a; - // 11. gx1 = gx1 * x1 - gx1 = gx1 * &x1; - // 12. gx1 = gx1 + B # gx1 = g(x1) = x1^3 + A * x1 + B - gx1 = gx1 + b; - // 13. x2 = tv1 * x1 # x2 = Z * u^2 * x1 - let x2 = &tv1 * &x1; - // 14. tv2 = tv1 * tv2 - tv2 = tv1 * tv2; - // 15. gx2 = gx1 * tv2 # gx2 = (Z * u^2)^3 * gx1 - let gx2 = &gx1 * tv2; - // 16. e2 = is_square(gx1) - let e2 = gx1.is_square(); - // 17. x = CMOV(x2, x1, e2) # If is_square(gx1), x = x1, else x = x2 - let x = cmov(&x2, &x1, e2); - // 18. y2 = CMOV(gx2, gx1, e2) # If is_square(gx1), y2 = gx1, else y2 = gx2 - let y2 = cmov(&gx2, &gx1, e2); - // 19. y = sqrt(y2) - let mut y = y2.sqrt(); - // 20. e3 = sgn0(u) == sgn0(y) # Fix sign of y - let e3 = u.sgn0() == y.sgn0(); - // 21. y = CMOV(-y, y, e3) - y = cmov(&-&y, &y, e3); - // 22. return (x, y) - (x.to_bytes(), y.to_bytes()) -} - -#[cfg(test)] -mod tests { - use generic_array::typenum::U96; - - use super::*; - - struct Params { - msg: &'static str, - px: &'static str, - py: &'static str, - u0: &'static str, - u1: &'static str, - q0x: &'static str, - q0y: &'static str, - q1x: &'static str, - q1y: &'static str, - } - - #[test] - fn hash_to_curve_simple_swu() { - const DST: [u8; 44] = *b"QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_"; - - const P: Lazy = Lazy::new(|| { - BigInt::from_str( - "115792089210356248762697446949407573530086143415290314195533631308867097853951", - ) - .unwrap() - }); - const A: Lazy = Lazy::new(|| BigInt::from(-3)); - const B: Lazy = Lazy::new(|| { - BigInt::parse_bytes( - b"5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", - 16, - ) - .unwrap() - }); - const Z: Lazy = Lazy::new(|| BigInt::from(-10)); - - // Test vectors taken from https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-J.1.1 - let test_vectors = alloc::vec![ - Params { - msg: "", - px: "2c15230b26dbc6fc9a37051158c95b79656e17a1a920b11394ca91c44247d3e4", - py: "8a7a74985cc5c776cdfe4b1f19884970453912e9d31528c060be9ab5c43e8415", - u0: "ad5342c66a6dd0ff080df1da0ea1c04b96e0330dd89406465eeba11582515009", - u1: "8c0f1d43204bd6f6ea70ae8013070a1518b43873bcd850aafa0a9e220e2eea5a", - q0x: "ab640a12220d3ff283510ff3f4b1953d09fad35795140b1c5d64f313967934d5", - q0y: "dccb558863804a881d4fff3455716c836cef230e5209594ddd33d85c565b19b1", - q1x: "51cce63c50d972a6e51c61334f0f4875c9ac1cd2d3238412f84e31da7d980ef5", - q1y: "b45d1a36d00ad90e5ec7840a60a4de411917fbe7c82c3949a6e699e5a1b66aac", - }, - Params { - msg: "abc", - px: "0bb8b87485551aa43ed54f009230450b492fead5f1cc91658775dac4a3388a0f", - py: "5c41b3d0731a27a7b14bc0bf0ccded2d8751f83493404c84a88e71ffd424212e", - u0: "afe47f2ea2b10465cc26ac403194dfb68b7f5ee865cda61e9f3e07a537220af1", - u1: "379a27833b0bfe6f7bdca08e1e83c760bf9a338ab335542704edcd69ce9e46e0", - q0x: "5219ad0ddef3cc49b714145e91b2f7de6ce0a7a7dc7406c7726c7e373c58cb48", - q0y: "7950144e52d30acbec7b624c203b1996c99617d0b61c2442354301b191d93ecf", - q1x: "019b7cb4efcfeaf39f738fe638e31d375ad6837f58a852d032ff60c69ee3875f", - q1y: "589a62d2b22357fed5449bc38065b760095ebe6aeac84b01156ee4252715446e", - }, - Params { - msg: "abcdef0123456789", - px: "65038ac8f2b1def042a5df0b33b1f4eca6bff7cb0f9c6c1526811864e544ed80", - py: "cad44d40a656e7aff4002a8de287abc8ae0482b5ae825822bb870d6df9b56ca3", - u0: "0fad9d125a9477d55cf9357105b0eb3a5c4259809bf87180aa01d651f53d312c", - u1: "b68597377392cd3419d8fcc7d7660948c8403b19ea78bbca4b133c9d2196c0fb", - q0x: "a17bdf2965eb88074bc01157e644ed409dac97cfcf0c61c998ed0fa45e79e4a2", - q0y: "4f1bc80c70d411a3cc1d67aeae6e726f0f311639fee560c7f5a664554e3c9c2e", - q1x: "7da48bb67225c1a17d452c983798113f47e438e4202219dd0715f8419b274d66", - q1y: "b765696b2913e36db3016c47edb99e24b1da30e761a8a3215dc0ec4d8f96e6f9", - }, - Params { - msg: "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ - qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\ - qqqqqqqqqqqqqqqqqqqqqqqqq", - px: "4be61ee205094282ba8a2042bcb48d88dfbb609301c49aa8b078533dc65a0b5d", - py: "98f8df449a072c4721d241a3b1236d3caccba603f916ca680f4539d2bfb3c29e", - u0: "3bbc30446f39a7befad080f4d5f32ed116b9534626993d2cc5033f6f8d805919", - u1: "76bb02db019ca9d3c1e02f0c17f8baf617bbdae5c393a81d9ce11e3be1bf1d33", - q0x: "c76aaa823aeadeb3f356909cb08f97eee46ecb157c1f56699b5efebddf0e6398", - q0y: "776a6f45f528a0e8d289a4be12c4fab80762386ec644abf2bffb9b627e4352b1", - q1x: "418ac3d85a5ccc4ea8dec14f750a3a9ec8b85176c95a7022f391826794eb5a75", - q1y: "fd6604f69e9d9d2b74b072d14ea13050db72c932815523305cb9e807cc900aff", - }, - Params { - msg: "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - px: "457ae2981f70ca85d8e24c308b14db22f3e3862c5ea0f652ca38b5e49cd64bc5", - py: "ecb9f0eadc9aeed232dabc53235368c1394c78de05dd96893eefa62b0f4757dc", - u0: "4ebc95a6e839b1ae3c63b847798e85cb3c12d3817ec6ebc10af6ee51adb29fec", - u1: "4e21af88e22ea80156aff790750121035b3eefaa96b425a8716e0d20b4e269ee", - q0x: "d88b989ee9d1295df413d4456c5c850b8b2fb0f5402cc5c4c7e815412e926db8", - q0y: "bb4a1edeff506cf16def96afff41b16fc74f6dbd55c2210e5b8f011ba32f4f40", - q1x: "a281e34e628f3a4d2a53fa87ff973537d68ad4fbc28d3be5e8d9f6a2571c5a4b", - q1y: "f6ed88a7aab56a488100e6f1174fa9810b47db13e86be999644922961206e184", - }, - ]; - - for tv in test_vectors { - let uniform_bytes = super::super::expand::expand_message_xmd::( - &[tv.msg.as_bytes()], - &DST, - ) - .unwrap(); - - let u0 = BigInt::from_bytes_be(Sign::Plus, &uniform_bytes[..48]).mod_floor(&P); - let u1 = BigInt::from_bytes_be(Sign::Plus, &uniform_bytes[48..]).mod_floor(&P); - - assert_eq!(BigInt::parse_bytes(tv.u0.as_bytes(), 16).unwrap(), u0); - assert_eq!(BigInt::parse_bytes(tv.u1.as_bytes(), 16).unwrap(), u1); - - let (q0x, q0y) = super::hash_to_curve_simple_swu(&u0.to_bytes_be().1, &A, &B, &P, &Z); - let (q1x, q1y) = super::hash_to_curve_simple_swu(&u1.to_bytes_be().1, &A, &B, &P, &Z); - - assert_eq!(tv.q0x, hex::encode(q0x)); - assert_eq!(tv.q0y, hex::encode(q0y)); - assert_eq!(tv.q1x, hex::encode(q1x)); - assert_eq!(tv.q1y, hex::encode(q1y)); - - let p0 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates( - &q0x, &q0y, false, - )) - .unwrap() - .to_curve(); - let p1 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates( - &q1x, &q1y, false, - )) - .unwrap(); - - let p = (p0 + p1).to_encoded_point(false); - - assert_eq!(tv.px, hex::encode(p.x().unwrap())); - assert_eq!(tv.py, hex::encode(p.y().unwrap())); - } - } -} diff --git a/src/group/ristretto.rs b/src/group/ristretto.rs index ea1cc47..8237a22 100644 --- a/src/group/ristretto.rs +++ b/src/group/ristretto.rs @@ -5,31 +5,37 @@ // License, Version 2.0 found in the LICENSE-APACHE file in the root directory // of this source tree. -use core::convert::TryInto; - use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT; use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; use curve25519_dalek::scalar::Scalar; use curve25519_dalek::traits::Identity; use digest::core_api::BlockSizeUser; -use digest::{Digest, FixedOutputReset}; +use digest::OutputSizeUser; +use elliptic_curve::hash2curve::{ExpandMsg, ExpandMsgXmd, Expander}; use generic_array::sequence::Concat; -use generic_array::typenum::{U32, U64}; +use generic_array::typenum::{IsLess, IsLessOrEqual, U256, U32, U64}; use generic_array::GenericArray; use rand_core::{CryptoRng, RngCore}; -use super::{expand, Group, STR_HASH_TO_GROUP, STR_HASH_TO_SCALAR}; +use super::{Group, STR_HASH_TO_GROUP, STR_HASH_TO_SCALAR}; use crate::voprf::{self, Mode}; -use crate::{Error, Result}; +use crate::{CipherSuite, Error, Result}; /// [`Group`] implementation for Ristretto255. pub struct Ristretto255; +#[cfg(feature = "ristretto255-ciphersuite")] +impl crate::CipherSuite for Ristretto255 { + const ID: u16 = 0x0001; + + type Group = Ristretto255; + + type Hash = sha2::Sha512; +} + // `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")] impl Group for Ristretto255 { - const SUITE_ID: u16 = 0x0001; - type Elem = RistrettoPoint; type ElemLen = U32; @@ -40,35 +46,38 @@ impl Group for Ristretto255 { // 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( - msg: &[&[u8]], - mode: Mode, - ) -> Result { + fn hash_to_curve(msg: &[&[u8]], mode: Mode) -> Result + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(voprf::get_context_string::(mode)); - let uniform_bytes = expand::expand_message_xmd::(msg, &dst)?; + let mut uniform_bytes = GenericArray::<_, U64>::default(); + ExpandMsgXmd::::expand_message(msg, &dst, 64) + .map_err(|_| Error::PointError)? + .fill_bytes(&mut uniform_bytes); Ok(RistrettoPoint::from_uniform_bytes(&uniform_bytes.into())) } // 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: BlockSizeUser + Digest + FixedOutputReset>( - input: &[&[u8]], - mode: Mode, - ) -> Result { + fn hash_to_scalar<'a, CS: CipherSuite>(input: &[&[u8]], mode: Mode) -> Result + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(voprf::get_context_string::(mode)); - let uniform_bytes = expand::expand_message_xmd::(input, &dst)?; + let mut uniform_bytes = GenericArray::<_, U64>::default(); + ExpandMsgXmd::::expand_message(input, &dst, 64) + .map_err(|_| Error::PointError)? + .fill_bytes(&mut uniform_bytes); - Ok(Scalar::from_bytes_mod_order_wide( - uniform_bytes - .as_slice() - .try_into() - .map_err(|_| Error::HashToCurveError)?, - )) + Ok(Scalar::from_bytes_mod_order_wide(&uniform_bytes.into())) } fn base_elem() -> Self::Elem { diff --git a/src/group/tests.rs b/src/group/tests.rs index d389048..79cbb86 100644 --- a/src/group/tests.rs +++ b/src/group/tests.rs @@ -14,6 +14,8 @@ use crate::{Error, Group, Result}; #[test] fn test_group_properties() -> Result<()> { + use p256::NistP256; + #[cfg(feature = "ristretto255")] { use crate::Ristretto255; @@ -22,13 +24,8 @@ fn test_group_properties() -> Result<()> { test_zero_scalar_error::()?; } - #[cfg(feature = "p256")] - { - use p256_::NistP256; - - test_identity_element_error::()?; - test_zero_scalar_error::()?; - } + test_identity_element_error::()?; + test_zero_scalar_error::()?; Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 8aa59b9..1c04a42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,8 +24,7 @@ //! We will use the following choices in this example: //! //! ```ignore -//! type Group = voprf::Ristretto255; -//! type Hash = sha2::Sha512; +//! type CipherSuite = voprf::Ristretto255; //! ``` //! //! ## Modes of Operation @@ -52,19 +51,15 @@ //! //! ``` //! # #[cfg(feature = "ristretto255")] -//! # type Group = voprf::Ristretto255; -//! # #[cfg(feature = "ristretto255")] -//! # type Hash = sha2::Sha512; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Group = p256_::NistP256; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Hash = sha2::Sha256; +//! # type CipherSuite = voprf::Ristretto255; +//! # #[cfg(not(feature = "ristretto255"))] +//! # type CipherSuite = p256::NistP256; //! use rand::rngs::OsRng; //! use rand::RngCore; //! use voprf::NonVerifiableServer; //! //! let mut server_rng = OsRng; -//! let server = NonVerifiableServer::::new(&mut server_rng) +//! let server = NonVerifiableServer::::new(&mut server_rng) //! .expect("Unable to construct server"); //! ``` //! @@ -78,19 +73,15 @@ //! //! ``` //! # #[cfg(feature = "ristretto255")] -//! # type Group = voprf::Ristretto255; -//! # #[cfg(feature = "ristretto255")] -//! # type Hash = sha2::Sha512; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Group = p256_::NistP256; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Hash = sha2::Sha256; +//! # type CipherSuite = voprf::Ristretto255; +//! # #[cfg(not(feature = "ristretto255"))] +//! # type CipherSuite = p256::NistP256; //! use rand::rngs::OsRng; //! use rand::RngCore; //! use voprf::NonVerifiableClient; //! //! let mut client_rng = OsRng; -//! let client_blind_result = NonVerifiableClient::::blind(b"input", &mut client_rng) +//! let client_blind_result = NonVerifiableClient::::blind(b"input", &mut client_rng) //! .expect("Unable to construct client"); //! ``` //! @@ -104,24 +95,20 @@ //! //! ``` //! # #[cfg(feature = "ristretto255")] -//! # type Group = voprf::Ristretto255; -//! # #[cfg(feature = "ristretto255")] -//! # type Hash = sha2::Sha512; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Group = p256_::NistP256; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Hash = sha2::Sha256; +//! # type CipherSuite = voprf::Ristretto255; +//! # #[cfg(not(feature = "ristretto255"))] +//! # type CipherSuite = p256::NistP256; //! # use voprf::NonVerifiableClient; //! # use rand::{rngs::OsRng, RngCore}; //! # //! # let mut client_rng = OsRng; -//! # let client_blind_result = NonVerifiableClient::::blind( +//! # let client_blind_result = NonVerifiableClient::::blind( //! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); //! # use voprf::NonVerifiableServer; //! # let mut server_rng = OsRng; -//! # let server = NonVerifiableServer::::new(&mut server_rng) +//! # let server = NonVerifiableServer::::new(&mut server_rng) //! # .expect("Unable to construct server"); //! let server_evaluate_result = server //! .evaluate(&client_blind_result.message, None) @@ -136,24 +123,20 @@ //! //! ``` //! # #[cfg(feature = "ristretto255")] -//! # type Group = voprf::Ristretto255; -//! # #[cfg(feature = "ristretto255")] -//! # type Hash = sha2::Sha512; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Group = p256_::NistP256; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Hash = sha2::Sha256; +//! # type CipherSuite = voprf::Ristretto255; +//! # #[cfg(not(feature = "ristretto255"))] +//! # type CipherSuite = p256::NistP256; //! # use voprf::NonVerifiableClient; //! # use rand::{rngs::OsRng, RngCore}; //! # //! # let mut client_rng = OsRng; -//! # let client_blind_result = NonVerifiableClient::::blind( +//! # let client_blind_result = NonVerifiableClient::::blind( //! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); //! # use voprf::NonVerifiableServer; //! # let mut server_rng = OsRng; -//! # let server = NonVerifiableServer::::new(&mut server_rng) +//! # let server = NonVerifiableServer::::new(&mut server_rng) //! # .expect("Unable to construct server"); //! # let server_evaluate_result = server.evaluate( //! # &client_blind_result.message, @@ -187,20 +170,16 @@ //! //! ``` //! # #[cfg(feature = "ristretto255")] -//! # type Group = voprf::Ristretto255; -//! # #[cfg(feature = "ristretto255")] -//! # type Hash = sha2::Sha512; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Group = p256_::NistP256; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Hash = sha2::Sha256; +//! # type CipherSuite = voprf::Ristretto255; +//! # #[cfg(not(feature = "ristretto255"))] +//! # type CipherSuite = p256::NistP256; //! use rand::rngs::OsRng; //! use rand::RngCore; //! use voprf::VerifiableServer; //! //! let mut server_rng = OsRng; //! let server = -//! VerifiableServer::::new(&mut server_rng).expect("Unable to construct server"); +//! VerifiableServer::::new(&mut server_rng).expect("Unable to construct server"); //! //! // To be sent to the client //! println!("Server public key: {:?}", server.get_public_key()); @@ -220,19 +199,15 @@ //! //! ``` //! # #[cfg(feature = "ristretto255")] -//! # type Group = voprf::Ristretto255; -//! # #[cfg(feature = "ristretto255")] -//! # type Hash = sha2::Sha512; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Group = p256_::NistP256; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Hash = sha2::Sha256; +//! # type CipherSuite = voprf::Ristretto255; +//! # #[cfg(not(feature = "ristretto255"))] +//! # type CipherSuite = p256::NistP256; //! use rand::rngs::OsRng; //! use rand::RngCore; //! use voprf::VerifiableClient; //! //! let mut client_rng = OsRng; -//! let client_blind_result = VerifiableClient::::blind(b"input", &mut client_rng) +//! let client_blind_result = VerifiableClient::::blind(b"input", &mut client_rng) //! .expect("Unable to construct client"); //! ``` //! @@ -246,24 +221,20 @@ //! //! ``` //! # #[cfg(feature = "ristretto255")] -//! # type Group = voprf::Ristretto255; -//! # #[cfg(feature = "ristretto255")] -//! # type Hash = sha2::Sha512; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Group = p256_::NistP256; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Hash = sha2::Sha256; +//! # type CipherSuite = voprf::Ristretto255; +//! # #[cfg(not(feature = "ristretto255"))] +//! # type CipherSuite = p256::NistP256; //! # use voprf::VerifiableClient; //! # use rand::{rngs::OsRng, RngCore}; //! # //! # let mut client_rng = OsRng; -//! # let client_blind_result = VerifiableClient::::blind( +//! # let client_blind_result = VerifiableClient::::blind( //! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); //! # use voprf::VerifiableServer; //! # let mut server_rng = OsRng; -//! # let server = VerifiableServer::::new(&mut server_rng) +//! # let server = VerifiableServer::::new(&mut server_rng) //! # .expect("Unable to construct server"); //! let server_evaluate_result = server //! .evaluate(&mut server_rng, &client_blind_result.message, None) @@ -279,24 +250,20 @@ //! //! ``` //! # #[cfg(feature = "ristretto255")] -//! # type Group = voprf::Ristretto255; -//! # #[cfg(feature = "ristretto255")] -//! # type Hash = sha2::Sha512; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Group = p256_::NistP256; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Hash = sha2::Sha256; +//! # type CipherSuite = voprf::Ristretto255; +//! # #[cfg(not(feature = "ristretto255"))] +//! # type CipherSuite = p256::NistP256; //! # use voprf::VerifiableClient; //! # use rand::{rngs::OsRng, RngCore}; //! # //! # let mut client_rng = OsRng; -//! # let client_blind_result = VerifiableClient::::blind( +//! # let client_blind_result = VerifiableClient::::blind( //! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); //! # use voprf::VerifiableServer; //! # let mut server_rng = OsRng; -//! # let server = VerifiableServer::::new(&mut server_rng) +//! # let server = VerifiableServer::::new(&mut server_rng) //! # .expect("Unable to construct server"); //! # let server_evaluate_result = server.evaluate( //! # &mut server_rng, @@ -336,13 +303,9 @@ //! //! ``` //! # #[cfg(feature = "ristretto255")] -//! # type Group = voprf::Ristretto255; -//! # #[cfg(feature = "ristretto255")] -//! # type Hash = sha2::Sha512; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Group = p256_::NistP256; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Hash = sha2::Sha256; +//! # type CipherSuite = voprf::Ristretto255; +//! # #[cfg(not(feature = "ristretto255"))] +//! # type CipherSuite = p256::NistP256; //! # use voprf::VerifiableClient; //! # use rand::{rngs::OsRng, RngCore}; //! # @@ -350,7 +313,7 @@ //! let mut client_states = vec![]; //! let mut client_messages = vec![]; //! for _ in 0..10 { -//! let client_blind_result = VerifiableClient::::blind(b"input", &mut client_rng) +//! let client_blind_result = VerifiableClient::::blind(b"input", &mut client_rng) //! .expect("Unable to construct client"); //! client_states.push(client_blind_result.state); //! client_messages.push(client_blind_result.message); @@ -364,13 +327,9 @@ //! //! ``` //! # #[cfg(feature = "ristretto255")] -//! # type Group = voprf::Ristretto255; -//! # #[cfg(feature = "ristretto255")] -//! # type Hash = sha2::Sha512; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Group = p256_::NistP256; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Hash = sha2::Sha256; +//! # type CipherSuite = voprf::Ristretto255; +//! # #[cfg(not(feature = "ristretto255"))] +//! # type CipherSuite = p256::NistP256; //! # use voprf::{VerifiableServerBatchEvaluatePrepareResult, VerifiableServerBatchEvaluateFinishResult, VerifiableClient}; //! # use rand::{rngs::OsRng, RngCore}; //! # @@ -378,7 +337,7 @@ //! # let mut client_states = vec![]; //! # let mut client_messages = vec![]; //! # for _ in 0..10 { -//! # let client_blind_result = VerifiableClient::::blind( +//! # let client_blind_result = VerifiableClient::::blind( //! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); @@ -387,7 +346,7 @@ //! # } //! # use voprf::VerifiableServer; //! let mut server_rng = OsRng; -//! # let server = VerifiableServer::::new(&mut server_rng) +//! # let server = VerifiableServer::::new(&mut server_rng) //! # .expect("Unable to construct server"); //! let VerifiableServerBatchEvaluatePrepareResult { //! prepared_evaluation_elements, @@ -407,13 +366,9 @@ //! ``` //! # #[cfg(feature = "alloc")] { //! # #[cfg(feature = "ristretto255")] -//! # type Group = voprf::Ristretto255; -//! # #[cfg(feature = "ristretto255")] -//! # type Hash = sha2::Sha512; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Group = p256_::NistP256; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Hash = sha2::Sha256; +//! # type CipherSuite = voprf::Ristretto255; +//! # #[cfg(not(feature = "ristretto255"))] +//! # type CipherSuite = p256::NistP256; //! # use voprf::{VerifiableServerBatchEvaluateResult, VerifiableClient}; //! # use rand::{rngs::OsRng, RngCore}; //! # @@ -421,7 +376,7 @@ //! # let mut client_states = vec![]; //! # let mut client_messages = vec![]; //! # for _ in 0..10 { -//! # let client_blind_result = VerifiableClient::::blind( +//! # let client_blind_result = VerifiableClient::::blind( //! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); @@ -430,7 +385,7 @@ //! # } //! # use voprf::VerifiableServer; //! let mut server_rng = OsRng; -//! # let server = VerifiableServer::::new(&mut server_rng) +//! # let server = VerifiableServer::::new(&mut server_rng) //! # .expect("Unable to construct server"); //! let VerifiableServerBatchEvaluateResult { messages, proof } = server //! .batch_evaluate(&mut server_rng, &client_messages, None) @@ -446,13 +401,9 @@ //! ``` //! # #[cfg(feature = "alloc")] { //! # #[cfg(feature = "ristretto255")] -//! # type Group = voprf::Ristretto255; -//! # #[cfg(feature = "ristretto255")] -//! # type Hash = sha2::Sha512; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Group = p256_::NistP256; -//! # #[cfg(all(feature = "p256", not(feature = "ristretto255")))] -//! # type Hash = sha2::Sha256; +//! # type CipherSuite = voprf::Ristretto255; +//! # #[cfg(not(feature = "ristretto255"))] +//! # type CipherSuite = p256::NistP256; //! # use voprf::{VerifiableServerBatchEvaluateResult, VerifiableClient}; //! # use rand::{rngs::OsRng, RngCore}; //! # @@ -460,7 +411,7 @@ //! # let mut client_states = vec![]; //! # let mut client_messages = vec![]; //! # for _ in 0..10 { -//! # let client_blind_result = VerifiableClient::::blind( +//! # let client_blind_result = VerifiableClient::::blind( //! # b"input", //! # &mut client_rng, //! # ).expect("Unable to construct client"); @@ -469,7 +420,7 @@ //! # } //! # use voprf::VerifiableServer; //! # let mut server_rng = OsRng; -//! # let server = VerifiableServer::::new(&mut server_rng) +//! # let server = VerifiableServer::::new(&mut server_rng) //! # .expect("Unable to construct server"); //! # let VerifiableServerBatchEvaluateResult { messages, proof } = server //! # .batch_evaluate(&mut server_rng, &client_messages, None) @@ -507,11 +458,6 @@ //! - The `alloc` feature requires Rusts [`alloc`] crate and enables batching //! VOPRF evaluations. //! -//! - The `p256` feature enables using [`NistP256`](p256_::NistP256) as the -//! underlying group for the [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/). //! @@ -521,18 +467,21 @@ //! that need access to these raw values and are able to perform the necessary //! validations on them (such as being valid group elements). //! +//! - The `ristretto255-ciphersuite` features enables using [`Ristretto255`] as +//! a [`CipherSuite`]. +//! //! - The `ristretto255` feature enables using [`Ristretto255`] as the //! underlying group for the [Group] choice. A backend feature, which are //! re-exported from [curve25519-dalek] and allow for selecting the //! corresponding backend for the curve arithmetic used, has to be selected, -//! otherwise compilation will fail. The `ristretto255_u64` feature is -//! included as the default. Other features are mapped as `ristretto255_u32`, -//! `ristretto255_fiat_u64` and `ristretto255_fiat_u32`. Any `ristretto255_*` +//! otherwise compilation will fail. The `ristretto255-u64` feature is +//! included as the default. Other features are mapped as `ristretto255-u32`, +//! `ristretto255-fiat-u64` and `ristretto255-fiat-u32`. Any `ristretto255-*` //! backend feature will enable the `ristretto255` feature. //! -//! - The `ristretto255_simd` feature is re-exported from [curve25519-dalek] and +//! - The `ristretto255-simd` feature is re-exported from [curve25519-dalek] and //! enables parallel formulas, using either AVX2 or AVX512-IFMA. This will -//! automatically enable the `ristretto255_u64` feature and requires Rust +//! automatically enable the `ristretto255-u64` feature and requires Rust //! nightly. //! //! [curve25519-dalek]: (https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) @@ -548,12 +497,11 @@ extern crate alloc; #[cfg(feature = "std")] extern crate std; -#[macro_use] -mod util; -#[macro_use] -mod serialization; +mod ciphersuite; mod error; mod group; +mod serialization; +mod util; mod voprf; #[cfg(test)] @@ -561,11 +509,15 @@ mod tests; // Exports -#[cfg(feature = "ristretto255")] -pub use group::Ristretto255; - +pub use crate::ciphersuite::CipherSuite; pub use crate::error::{Error, Result}; pub use crate::group::Group; +#[cfg(feature = "ristretto255")] +pub use crate::group::Ristretto255; +pub use crate::serialization::{ + BlindedElementLen, EvaluationElementLen, NonVerifiableClientLen, NonVerifiableServerLen, + ProofLen, VerifiableClientLen, VerifiableServerLen, +}; #[cfg(feature = "alloc")] pub use crate::voprf::VerifiableServerBatchEvaluateResult; pub use crate::voprf::{ diff --git a/src/serialization.rs b/src/serialization.rs index 1d4abff..167019b 100644 --- a/src/serialization.rs +++ b/src/serialization.rs @@ -8,18 +8,17 @@ //! Handles the serialization of each of the components used in the VOPRF //! protocol -use core::marker::PhantomData; use core::ops::Add; use digest::core_api::BlockSizeUser; -use digest::{Digest, FixedOutputReset}; +use digest::OutputSizeUser; use generic_array::sequence::Concat; -use generic_array::typenum::Sum; +use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256}; use generic_array::{ArrayLength, GenericArray}; use crate::{ - BlindedElement, Error, EvaluationElement, Group, NonVerifiableClient, NonVerifiableServer, - Proof, Result, VerifiableClient, VerifiableServer, + BlindedElement, CipherSuite, Error, EvaluationElement, Group, NonVerifiableClient, + NonVerifiableServer, Proof, Result, VerifiableClient, VerifiableServer, }; ////////////////////////////////////////////////////////// @@ -27,154 +26,193 @@ use crate::{ // ==================================================== // ////////////////////////////////////////////////////////// -impl NonVerifiableClient { +/// Length of [`NonVerifiableClient`] in bytes for serialization. +pub type NonVerifiableClientLen = <::Group as Group>::ScalarLen; + +impl NonVerifiableClient +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ /// Serialization into bytes - pub fn serialize(&self) -> GenericArray { - G::serialize_scalar(self.blind) + pub fn serialize(&self) -> GenericArray> { + CS::Group::serialize_scalar(self.blind) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let mut input = input.iter().copied(); - let blind = G::deserialize_scalar(&deserialize(&mut input)?)?; + let blind = CS::Group::deserialize_scalar(&deserialize(&mut input)?)?; - Ok(Self { - blind, - hash: PhantomData, - }) + Ok(Self { blind }) } } -impl VerifiableClient { +/// Length of [`VerifiableClient`] in bytes for serialization. +pub type VerifiableClientLen = Sum< + <::Group as Group>::ScalarLen, + <::Group as Group>::ElemLen, +>; + +impl VerifiableClient +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ /// Serialization into bytes - pub fn serialize(&self) -> GenericArray> + pub fn serialize(&self) -> GenericArray> where - G::ScalarLen: Add, - Sum: ArrayLength, + ::ScalarLen: Add<::ElemLen>, + VerifiableClientLen: ArrayLength, { - G::serialize_scalar(self.blind).concat(G::serialize_elem(self.blinded_element)) + ::serialize_scalar(self.blind) + .concat(::serialize_elem(self.blinded_element)) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let mut input = input.iter().copied(); - let blind = G::deserialize_scalar(&deserialize(&mut input)?)?; - let blinded_element = G::deserialize_elem(&deserialize(&mut input)?)?; + let blind = CS::Group::deserialize_scalar(&deserialize(&mut input)?)?; + let blinded_element = CS::Group::deserialize_elem(&deserialize(&mut input)?)?; Ok(Self { blind, blinded_element, - hash: PhantomData, }) } } -impl NonVerifiableServer { +/// Length of [`NonVerifiableServer`] in bytes for serialization. +pub type NonVerifiableServerLen = <::Group as Group>::ScalarLen; + +impl NonVerifiableServer +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ /// Serialization into bytes - pub fn serialize(&self) -> GenericArray { - G::serialize_scalar(self.sk) + pub fn serialize(&self) -> GenericArray> { + CS::Group::serialize_scalar(self.sk) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let mut input = input.iter().copied(); - let sk = G::deserialize_scalar(&deserialize(&mut input)?)?; + let sk = CS::Group::deserialize_scalar(&deserialize(&mut input)?)?; - Ok(Self { - sk, - hash: PhantomData, - }) + Ok(Self { sk }) } } -impl VerifiableServer { +/// Length of [`VerifiableServer`] in bytes for serialization. +pub type VerifiableServerLen = Sum< + <::Group as Group>::ScalarLen, + <::Group as Group>::ElemLen, +>; + +impl VerifiableServer +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ /// Serialization into bytes - pub fn serialize(&self) -> GenericArray> + pub fn serialize(&self) -> GenericArray> where - G::ScalarLen: Add, - Sum: ArrayLength, + ::ScalarLen: Add<::ElemLen>, + VerifiableServerLen: ArrayLength, { - G::serialize_scalar(self.sk).concat(G::serialize_elem(self.pk)) + CS::Group::serialize_scalar(self.sk).concat(CS::Group::serialize_elem(self.pk)) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let mut input = input.iter().copied(); - let sk = G::deserialize_scalar(&deserialize(&mut input)?)?; - let pk = G::deserialize_elem(&deserialize(&mut input)?)?; + let sk = CS::Group::deserialize_scalar(&deserialize(&mut input)?)?; + let pk = CS::Group::deserialize_elem(&deserialize(&mut input)?)?; - Ok(Self { - sk, - pk, - hash: PhantomData, - }) + Ok(Self { sk, pk }) } } -impl Proof { +/// Length of [`Proof`] in bytes for serialization. +pub type ProofLen = Sum< + <::Group as Group>::ScalarLen, + <::Group as Group>::ScalarLen, +>; + +impl Proof +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ /// Serialization into bytes - pub fn serialize(&self) -> GenericArray> + pub fn serialize(&self) -> GenericArray> where - G::ScalarLen: Add, - Sum: ArrayLength, + ::ScalarLen: Add<::ScalarLen>, + ProofLen: ArrayLength, { - G::serialize_scalar(self.c_scalar).concat(G::serialize_scalar(self.s_scalar)) + CS::Group::serialize_scalar(self.c_scalar) + .concat(CS::Group::serialize_scalar(self.s_scalar)) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let mut input = input.iter().copied(); - let c_scalar = G::deserialize_scalar(&deserialize(&mut input)?)?; - let s_scalar = G::deserialize_scalar(&deserialize(&mut input)?)?; + let c_scalar = CS::Group::deserialize_scalar(&deserialize(&mut input)?)?; + let s_scalar = CS::Group::deserialize_scalar(&deserialize(&mut input)?)?; - Ok(Proof { - c_scalar, - s_scalar, - hash: PhantomData, - }) + Ok(Proof { c_scalar, s_scalar }) } } -impl BlindedElement { +/// Length of [`BlindedElement`] in bytes for serialization. +pub type BlindedElementLen = <::Group as Group>::ElemLen; + +impl BlindedElement +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ /// Serialization into bytes - pub fn serialize(&self) -> GenericArray { - G::serialize_elem(self.value) + pub fn serialize(&self) -> GenericArray> { + CS::Group::serialize_elem(self.0) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let mut input = input.iter().copied(); - let value = G::deserialize_elem(&deserialize(&mut input)?)?; + let value = CS::Group::deserialize_elem(&deserialize(&mut input)?)?; - Ok(Self { - value, - hash: PhantomData, - }) + Ok(Self(value)) } } -impl EvaluationElement { +/// Length of [`EvaluationElement`] in bytes for serialization. +pub type EvaluationElementLen = <::Group as Group>::ElemLen; + +impl EvaluationElement +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ /// Serialization into bytes - pub fn serialize(&self) -> GenericArray { - G::serialize_elem(self.value) + pub fn serialize(&self) -> GenericArray> { + CS::Group::serialize_elem(self.0) } /// Deserialization from bytes pub fn deserialize(input: &[u8]) -> Result { let mut input = input.iter().copied(); - let value = G::deserialize_elem(&deserialize(&mut input)?)?; + let value = CS::Group::deserialize_elem(&deserialize(&mut input)?)?; - Ok(Self { - value, - hash: PhantomData, - }) + Ok(Self(value)) } } diff --git a/src/tests/voprf_test_vectors.rs b/src/tests/voprf_test_vectors.rs index 898404d..7691811 100644 --- a/src/tests/voprf_test_vectors.rs +++ b/src/tests/voprf_test_vectors.rs @@ -11,16 +11,16 @@ use alloc::vec::Vec; use core::ops::Add; use digest::core_api::BlockSizeUser; -use digest::{Digest, FixedOutputReset}; -use generic_array::typenum::Sum; +use digest::OutputSizeUser; +use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256}; use generic_array::{ArrayLength, GenericArray}; use json::JsonValue; use crate::tests::mock_rng::CycleRng; use crate::tests::parser::*; use crate::{ - BlindedElement, EvaluationElement, Group, NonVerifiableClient, NonVerifiableServer, Proof, - Result, VerifiableClient, VerifiableServer, + BlindedElement, CipherSuite, EvaluationElement, Group, NonVerifiableClient, + NonVerifiableServer, Proof, Result, VerifiableClient, VerifiableServer, }; #[derive(Debug)] @@ -85,13 +85,13 @@ macro_rules! json_to_test_vectors { #[test] fn test_vectors() -> Result<()> { + use p256::NistP256; + let rfc = json::parse(rfc_to_json(super::voprf_vectors::VECTORS).as_str()) .expect("Could not parse json"); #[cfg(feature = "ristretto255")] { - use sha2::Sha512; - use crate::Ristretto255; let ristretto_base_tvs = json_to_test_vectors!( @@ -106,93 +106,94 @@ fn test_vectors() -> Result<()> { 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")] - { - use p256_::NistP256; - use sha2::Sha256; + let p256base_tvs = + json_to_test_vectors!(rfc, String::from("P-256, SHA-256"), String::from("Base")); - let p256_base_tvs = - json_to_test_vectors!(rfc, String::from("P-256, SHA-256"), String::from("Base")); + let p256verifiable_tvs = json_to_test_vectors!( + rfc, + String::from("P-256, SHA-256"), + String::from("Verifiable") + ); - let p256_verifiable_tvs = json_to_test_vectors!( - rfc, - String::from("P-256, SHA-256"), - String::from("Verifiable") - ); + test_base_seed_to_key::(&p256base_tvs)?; + test_base_blind::(&p256base_tvs)?; + test_base_evaluate::(&p256base_tvs)?; + test_base_finalize::(&p256base_tvs)?; - test_base_seed_to_key::(&p256_base_tvs)?; - test_base_blind::(&p256_base_tvs)?; - test_base_evaluate::(&p256_base_tvs)?; - test_base_finalize::(&p256_base_tvs)?; - - test_verifiable_seed_to_key::(&p256_verifiable_tvs)?; - test_verifiable_blind::(&p256_verifiable_tvs)?; - test_verifiable_evaluate::(&p256_verifiable_tvs)?; - test_verifiable_finalize::(&p256_verifiable_tvs)?; - } + test_verifiable_seed_to_key::(&p256verifiable_tvs)?; + test_verifiable_blind::(&p256verifiable_tvs)?; + test_verifiable_evaluate::(&p256verifiable_tvs)?; + test_verifiable_finalize::(&p256verifiable_tvs)?; Ok(()) } -fn test_base_seed_to_key( - tvs: &[VOPRFTestVectorParameters], -) -> Result<()> { +fn test_base_seed_to_key(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ for parameters in tvs { - let server = NonVerifiableServer::::new_from_seed(¶meters.seed)?; + let server = NonVerifiableServer::::new_from_seed(¶meters.seed)?; assert_eq!( ¶meters.sksm, - &G::serialize_scalar(server.get_private_key()).to_vec() + &CS::Group::serialize_scalar(server.get_private_key()).to_vec() ); } Ok(()) } -fn test_verifiable_seed_to_key( - tvs: &[VOPRFTestVectorParameters], -) -> Result<()> { +fn test_verifiable_seed_to_key(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ for parameters in tvs { - let server = VerifiableServer::::new_from_seed(¶meters.seed)?; + let server = VerifiableServer::::new_from_seed(¶meters.seed)?; assert_eq!( ¶meters.sksm, - &G::serialize_scalar(server.get_private_key()).to_vec() + &CS::Group::serialize_scalar(server.get_private_key()).to_vec() ); assert_eq!( ¶meters.pksm, - G::serialize_elem(server.get_public_key()).as_slice() + CS::Group::serialize_elem(server.get_public_key()).as_slice() ); } Ok(()) } // Tests input -> blind, blinded_element -fn test_base_blind( - tvs: &[VOPRFTestVectorParameters], -) -> Result<()> { +fn test_base_blind(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ for parameters in tvs { for i in 0..parameters.input.len() { - let blind = - G::deserialize_scalar(&GenericArray::clone_from_slice(¶meters.blind[i]))?; - let client_result = NonVerifiableClient::::deterministic_blind_unchecked( + let blind = CS::Group::deserialize_scalar(&GenericArray::clone_from_slice( + ¶meters.blind[i], + ))?; + let client_result = NonVerifiableClient::::deterministic_blind_unchecked( ¶meters.input[i], blind, )?; assert_eq!( ¶meters.blind[i], - &G::serialize_scalar(client_result.state.blind).to_vec() + &CS::Group::serialize_scalar(client_result.state.blind).to_vec() ); assert_eq!( parameters.blinded_element[i].as_slice(), @@ -204,21 +205,22 @@ fn test_base_blind( } // Tests input -> blind, blinded_element -fn test_verifiable_blind( - tvs: &[VOPRFTestVectorParameters], -) -> Result<()> { +fn test_verifiable_blind(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ for parameters in tvs { for i in 0..parameters.input.len() { - let blind = - G::deserialize_scalar(&GenericArray::clone_from_slice(¶meters.blind[i]))?; - let client_blind_result = VerifiableClient::::deterministic_blind_unchecked( - ¶meters.input[i], - blind, - )?; + let blind = CS::Group::deserialize_scalar(&GenericArray::clone_from_slice( + ¶meters.blind[i], + ))?; + let client_blind_result = + VerifiableClient::::deterministic_blind_unchecked(¶meters.input[i], blind)?; assert_eq!( ¶meters.blind[i], - &G::serialize_scalar(client_blind_result.state.get_blind()).to_vec() + &CS::Group::serialize_scalar(client_blind_result.state.get_blind()).to_vec() ); assert_eq!( parameters.blinded_element[i].as_slice(), @@ -230,12 +232,14 @@ fn test_verifiable_blind } // Tests sksm, blinded_element -> evaluation_element -fn test_base_evaluate( - tvs: &[VOPRFTestVectorParameters], -) -> Result<()> { +fn test_base_evaluate(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ for parameters in tvs { for i in 0..parameters.input.len() { - let server = NonVerifiableServer::::new_with_key(¶meters.sksm)?; + let server = NonVerifiableServer::::new_with_key(¶meters.sksm)?; let server_result = server.evaluate( &BlindedElement::deserialize(¶meters.blinded_element[i])?, Some(¶meters.info), @@ -250,12 +254,12 @@ fn test_base_evaluate( Ok(()) } -fn test_verifiable_evaluate( - tvs: &[VOPRFTestVectorParameters], -) -> Result<()> +fn test_verifiable_evaluate(tvs: &[VOPRFTestVectorParameters]) -> Result<()> where - G::ScalarLen: Add, - Sum: ArrayLength, + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + ::ScalarLen: Add<::ScalarLen>, + Sum<::ScalarLen, ::ScalarLen>: ArrayLength, { use crate::{ VerifiableServerBatchEvaluateFinishResult, VerifiableServerBatchEvaluatePrepareResult, @@ -263,7 +267,7 @@ where for parameters in tvs { let mut rng = CycleRng::new(parameters.proof_random_scalar.clone()); - let server = VerifiableServer::::new_with_key(¶meters.sksm)?; + let server = VerifiableServer::::new_with_key(¶meters.sksm)?; let mut blinded_elements = vec![]; for blinded_element_bytes in ¶meters.blinded_element { @@ -294,12 +298,14 @@ where } // Tests input, blind, evaluation_element -> output -fn test_base_finalize( - tvs: &[VOPRFTestVectorParameters], -) -> Result<()> { +fn test_base_finalize(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ for parameters in tvs { for i in 0..parameters.input.len() { - let client = NonVerifiableClient::::from_blind(G::deserialize_scalar( + let client = NonVerifiableClient::::from_blind(CS::Group::deserialize_scalar( &GenericArray::clone_from_slice(¶meters.blind[i]), )?); @@ -315,15 +321,19 @@ fn test_base_finalize( Ok(()) } -fn test_verifiable_finalize( - tvs: &[VOPRFTestVectorParameters], -) -> Result<()> { +fn test_verifiable_finalize(tvs: &[VOPRFTestVectorParameters]) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ for parameters in tvs { let mut clients = vec![]; for i in 0..parameters.input.len() { - let client = VerifiableClient::::from_blind_and_element( - G::deserialize_scalar(&GenericArray::clone_from_slice(¶meters.blind[i]))?, - G::deserialize_elem(&GenericArray::clone_from_slice( + let client = VerifiableClient::::from_blind_and_element( + CS::Group::deserialize_scalar(&GenericArray::clone_from_slice( + ¶meters.blind[i], + ))?, + CS::Group::deserialize_elem(&GenericArray::clone_from_slice( ¶meters.blinded_element[i], ))?, ); @@ -341,7 +351,7 @@ fn test_verifiable_finalize { #[cfg(feature = "ristretto255")] { - let _ = $item::::deserialize(&$bytes[..]); - } - #[cfg(feature = "p256")] - { - let _ = $item::::deserialize(&$bytes[..]); + let _ = $item::::deserialize(&$bytes[..]); } + + let _ = $item::::deserialize(&$bytes[..]); }; } diff --git a/src/voprf.rs b/src/voprf.rs index 6605faf..1001f41 100644 --- a/src/voprf.rs +++ b/src/voprf.rs @@ -9,21 +9,19 @@ #[cfg(feature = "alloc")] use alloc::vec::Vec; -use core::convert::{TryFrom, TryInto}; use core::iter::{self, Map, Repeat, Zip}; -use core::marker::PhantomData; use derive_where::DeriveWhere; use digest::core_api::BlockSizeUser; -use digest::{Digest, FixedOutputReset, Output}; +use digest::{Digest, Output, OutputSizeUser}; use generic_array::sequence::Concat; -use generic_array::typenum::{Unsigned, U11, U20}; +use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U11, U20, U256}; use generic_array::GenericArray; use rand_core::{CryptoRng, RngCore}; use subtle::ConstantTimeEq; use crate::util::{i2osp_2, i2osp_2_array}; -use crate::{Error, Group, Result}; +use crate::{CipherSuite, Error, Group, Result}; /////////////// // Constants // @@ -66,160 +64,170 @@ impl Mode { /// that the OPRF outputs are not verifiable. #[derive(DeriveWhere)] #[derive_where(Clone, Zeroize(drop))] -#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Scalar)] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar)] #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound( - deserialize = "G::Scalar: serde::Deserialize<'de>", - serialize = "G::Scalar: serde::Serialize" + deserialize = "::Scalar: serde::Deserialize<'de>", + serialize = "::Scalar: serde::Serialize" )) )] -pub struct NonVerifiableClient { - pub(crate) blind: G::Scalar, - #[derive_where(skip(Zeroize))] - pub(crate) hash: PhantomData, +pub struct NonVerifiableClient +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + pub(crate) blind: ::Scalar, } /// 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::Elem, G::Scalar)] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar, ::Elem)] #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound( - deserialize = "G::Scalar: serde::Deserialize<'de>, G::Elem: serde::Deserialize<'de>", - serialize = "G::Scalar: serde::Serialize, G::Elem: serde::Serialize" + deserialize = "::Scalar: serde::Deserialize<'de>, ::Elem: serde::Deserialize<'de>", + serialize = "::Scalar: serde::Serialize, ::Elem: \ + serde::Serialize" )) )] -pub struct VerifiableClient { - pub(crate) blind: G::Scalar, - pub(crate) blinded_element: G::Elem, - #[derive_where(skip(Zeroize))] - pub(crate) hash: PhantomData, +pub struct VerifiableClient +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + pub(crate) blind: ::Scalar, + pub(crate) blinded_element: ::Elem, } /// 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)] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar)] #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound( - deserialize = "G::Scalar: serde::Deserialize<'de>", - serialize = "G::Scalar: serde::Serialize" + deserialize = "::Scalar: serde::Deserialize<'de>", + serialize = "::Scalar: serde::Serialize" )) )] -pub struct NonVerifiableServer { - pub(crate) sk: G::Scalar, - #[derive_where(skip(Zeroize))] - pub(crate) hash: PhantomData, +pub struct NonVerifiableServer +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + pub(crate) sk: ::Scalar, } /// 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::Elem, G::Scalar)] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar, ::Elem)] #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound( - deserialize = "G::Scalar: serde::Deserialize<'de>, G::Elem: serde::Deserialize<'de>", - serialize = "G::Scalar: serde::Serialize, G::Elem: serde::Serialize" + deserialize = "::Scalar: serde::Deserialize<'de>, ::Elem: serde::Deserialize<'de>", + serialize = "::Scalar: serde::Serialize, ::Elem: \ + serde::Serialize" )) )] -pub struct VerifiableServer { - pub(crate) sk: G::Scalar, - pub(crate) pk: G::Elem, - #[derive_where(skip(Zeroize))] - pub(crate) hash: PhantomData, +pub struct VerifiableServer +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + pub(crate) sk: ::Scalar, + pub(crate) pk: ::Elem, } /// 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)] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Scalar)] #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound( - deserialize = "G::Scalar: serde::Deserialize<'de>", - serialize = "G::Scalar: serde::Serialize" + deserialize = "::Scalar: serde::Deserialize<'de>", + serialize = "::Scalar: serde::Serialize" )) )] -pub struct Proof { - pub(crate) c_scalar: G::Scalar, - pub(crate) s_scalar: G::Scalar, - #[derive_where(skip(Zeroize))] - pub(crate) hash: PhantomData, +pub struct Proof +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + pub(crate) c_scalar: ::Scalar, + pub(crate) s_scalar: ::Scalar, } /// 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::Elem)] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Elem)] #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound( - deserialize = "G::Elem: serde::Deserialize<'de>", - serialize = "G::Elem: serde::Serialize" + deserialize = "::Elem: serde::Deserialize<'de>", + serialize = "::Elem: serde::Serialize" )) )] -pub struct BlindedElement { - pub(crate) value: G::Elem, - #[derive_where(skip(Zeroize))] - pub(crate) hash: PhantomData, -} +pub struct BlindedElement(pub(crate) ::Elem) +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>; /// 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::Elem)] +#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; ::Elem)] #[cfg_attr( feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound( - deserialize = "G::Elem: serde::Deserialize<'de>", - serialize = "G::Elem: serde::Serialize" + deserialize = "::Elem: serde::Deserialize<'de>", + serialize = "::Elem: serde::Serialize" )) )] -pub struct EvaluationElement { - pub(crate) value: G::Elem, - #[derive_where(skip(Zeroize))] - pub(crate) hash: PhantomData, -} +pub struct EvaluationElement(pub(crate) ::Elem) +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>; ///////////////////////// // API Implementations // // =================== // ///////////////////////// -impl NonVerifiableClient { +impl NonVerifiableClient +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ /// Computes the first step for the multiplicative blinding version of /// DH-OPRF. pub fn blind( input: &[u8], blinding_factor_rng: &mut R, - ) -> Result> { - let (blind, blinded_element) = blind::(input, blinding_factor_rng, Mode::Base)?; + ) -> Result> { + let (blind, blinded_element) = blind::(input, blinding_factor_rng, Mode::Base)?; Ok(NonVerifiableClientBlindResult { - state: Self { - blind, - hash: PhantomData, - }, - message: BlindedElement { - value: blinded_element, - hash: PhantomData, - }, + state: Self { blind }, + message: BlindedElement(blinded_element), }) } @@ -234,18 +242,12 @@ impl NonVerifiableClient /// on the validity of the blinding factor! pub fn deterministic_blind_unchecked( input: &[u8], - blind: G::Scalar, - ) -> Result> { - let blinded_element = deterministic_blind_unchecked::(input, &blind, Mode::Base)?; + blind: ::Scalar, + ) -> Result> { + let blinded_element = deterministic_blind_unchecked::(input, &blind, Mode::Base)?; Ok(NonVerifiableClientBlindResult { - state: Self { - blind, - hash: PhantomData, - }, - message: BlindedElement { - value: blinded_element, - hash: PhantomData, - }, + state: Self { blind }, + message: BlindedElement(blinded_element), }) } @@ -254,11 +256,11 @@ impl NonVerifiableClient pub fn finalize( &self, input: &[u8], - evaluation_element: &EvaluationElement, + evaluation_element: &EvaluationElement, metadata: Option<&[u8]>, - ) -> Result> { - let unblinded_element = evaluation_element.value * &G::invert_scalar(self.blind); - let mut outputs = finalize_after_unblind::( + ) -> Result> { + let unblinded_element = evaluation_element.0 * &CS::Group::invert_scalar(self.blind); + let mut outputs = finalize_after_unblind::( Some((input, unblinded_element)).into_iter(), metadata.unwrap_or_default(), Mode::Base, @@ -268,39 +270,36 @@ impl NonVerifiableClient #[cfg(test)] /// Only used for test functions - pub fn from_blind(blind: G::Scalar) -> Self { - Self { - blind, - hash: PhantomData, - } + pub fn from_blind(blind: ::Scalar) -> Self { + Self { blind } } #[cfg(feature = "danger")] /// Exposes the blind group element - pub fn get_blind(&self) -> G::Scalar { + pub fn get_blind(&self) -> ::Scalar { self.blind } } -impl VerifiableClient { +impl VerifiableClient +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ /// Computes the first step for the multiplicative blinding version of /// DH-OPRF. pub fn blind( input: &[u8], blinding_factor_rng: &mut R, - ) -> Result> { + ) -> Result> { let (blind, blinded_element) = - blind::(input, blinding_factor_rng, Mode::Verifiable)?; + blind::(input, blinding_factor_rng, Mode::Verifiable)?; Ok(VerifiableClientBlindResult { state: Self { blind, blinded_element, - hash: PhantomData, - }, - message: BlindedElement { - value: blinded_element, - hash: PhantomData, }, + message: BlindedElement(blinded_element), }) } @@ -315,20 +314,15 @@ impl VerifiableClient Result> { - let blinded_element = - deterministic_blind_unchecked::(input, &blind, Mode::Verifiable)?; + blind: ::Scalar, + ) -> Result> { + let blinded_element = deterministic_blind_unchecked::(input, &blind, Mode::Verifiable)?; Ok(VerifiableClientBlindResult { state: Self { blind, blinded_element, - hash: PhantomData, - }, - message: BlindedElement { - value: blinded_element, - hash: PhantomData, }, + message: BlindedElement(blinded_element), }) } @@ -337,17 +331,14 @@ impl VerifiableClient, - proof: &Proof, - pk: G::Elem, + evaluation_element: &EvaluationElement, + proof: &Proof, + pk: ::Elem, metadata: Option<&[u8]>, - ) -> 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(); - let messages: &[EvaluationElement; 1] = core::slice::from_ref(evaluation_element) - .try_into() - .unwrap(); + ) -> Result> { + let inputs: &[&[u8]; 1] = core::array::from_ref(&input); + let clients: &[Self; 1] = core::array::from_ref(self); + let messages: &[EvaluationElement; 1] = core::array::from_ref(evaluation_element); let mut batch_result = Self::batch_finalize(inputs, clients, messages, proof, pk, metadata)?; @@ -360,19 +351,18 @@ impl VerifiableClient, - pk: G::Elem, + proof: &Proof, + pk: ::Elem, metadata: Option<&'a [u8]>, - ) -> Result> + ) -> Result> where - G: 'a, - H: 'a, + CS: 'a, I: AsRef<[u8]>, &'a II: 'a + IntoIterator, <&'a II as IntoIterator>::IntoIter: ExactSizeIterator, - &'a IC: 'a + IntoIterator>, + &'a IC: 'a + IntoIterator>, <&'a IC as IntoIterator>::IntoIter: ExactSizeIterator, - &'a IM: 'a + IntoIterator>, + &'a IM: 'a + IntoIterator>, <&'a IM as IntoIterator>::IntoIter: ExactSizeIterator, { let metadata = metadata.unwrap_or_default(); @@ -381,7 +371,7 @@ impl VerifiableClient( + finalize_after_unblind::( inputs_and_unblinded_elements, metadata, Mode::Verifiable, @@ -390,25 +380,31 @@ impl VerifiableClient Self { + pub fn from_blind_and_element( + blind: ::Scalar, + blinded_element: ::Elem, + ) -> Self { Self { blind, blinded_element, - hash: PhantomData, } } #[cfg(test)] /// Only used for test functions - pub fn get_blind(&self) -> G::Scalar { + pub fn get_blind(&self) -> ::Scalar { self.blind } } -impl NonVerifiableServer { +impl NonVerifiableServer +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ /// Produces a new instance of a [NonVerifiableServer] using a supplied RNG pub fn new(rng: &mut R) -> Result { - let mut seed = Output::::default(); + let mut seed = Output::::default(); rng.fill_bytes(&mut seed); Self::new_from_seed(&seed) } @@ -416,11 +412,8 @@ 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 { - let sk = G::deserialize_scalar(private_key_bytes.into())?; - Ok(Self { - sk, - hash: PhantomData, - }) + let sk = CS::Group::deserialize_scalar(private_key_bytes.into())?; + Ok(Self { sk }) } /// Produces a new instance of a [NonVerifiableServer] using a supplied set @@ -428,16 +421,13 @@ impl NonVerifiableServer /// /// Corresponds to DeriveKeyPair() function from the VOPRF specification. pub fn new_from_seed(seed: &[u8]) -> Result { - let sk = G::hash_to_scalar::(&[seed], Mode::Base)?; - Ok(Self { - sk, - hash: PhantomData, - }) + let sk = CS::Group::hash_to_scalar::(&[seed], Mode::Base)?; + Ok(Self { sk }) } // Only used for tests #[cfg(test)] - pub fn get_private_key(&self) -> ::Scalar { + pub fn get_private_key(&self) -> ::Scalar { self.sk } @@ -446,12 +436,12 @@ impl NonVerifiableServer /// to the client. pub fn evaluate( &self, - blinded_element: &BlindedElement, + blinded_element: &BlindedElement, metadata: Option<&[u8]>, - ) -> Result> { + ) -> Result> { // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.1.1-1 - let context_string = get_context_string::(Mode::Base); + let context_string = get_context_string::(Mode::Base); let metadata = metadata.unwrap_or_default(); // context = "Context-" || contextString || I2OSP(len(info), 2) || info @@ -461,25 +451,26 @@ impl NonVerifiableServer let context = [&context, metadata]; // m = GG.HashToScalar(context) - let m = G::hash_to_scalar::(&context, Mode::Base)?; + let m = CS::Group::hash_to_scalar::(&context, Mode::Base)?; // t = skS + m let t = self.sk + &m; // Z = t^(-1) * R - let z = blinded_element.value * &G::invert_scalar(t); + let z = blinded_element.0 * &CS::Group::invert_scalar(t); Ok(NonVerifiableServerEvaluateResult { - message: EvaluationElement { - value: z, - hash: PhantomData, - }, + message: EvaluationElement(z), }) } } -impl VerifiableServer { +impl VerifiableServer +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ /// Produces a new instance of a [VerifiableServer] using a supplied RNG pub fn new(rng: &mut R) -> Result { - let mut seed = Output::::default(); + let mut seed = Output::::default(); rng.fill_bytes(&mut seed); Self::new_from_seed(&seed) } @@ -487,13 +478,9 @@ impl VerifiableServer Result { - let sk = G::deserialize_scalar(key.into())?; - let pk = G::base_elem() * &sk; - Ok(Self { - sk, - pk, - hash: PhantomData, - }) + let sk = CS::Group::deserialize_scalar(key.into())?; + let pk = CS::Group::base_elem() * &sk; + Ok(Self { sk, pk }) } /// Produces a new instance of a [VerifiableServer] using a supplied set of @@ -501,18 +488,14 @@ impl VerifiableServer Result { - let sk = G::hash_to_scalar::(&[seed], Mode::Verifiable)?; - let pk = G::base_elem() * &sk; - Ok(Self { - sk, - pk, - hash: PhantomData, - }) + let sk = CS::Group::hash_to_scalar::(&[seed], Mode::Verifiable)?; + let pk = CS::Group::base_elem() * &sk; + Ok(Self { sk, pk }) } // Only used for tests #[cfg(test)] - pub fn get_private_key(&self) -> G::Scalar { + pub fn get_private_key(&self) -> ::Scalar { self.sk } @@ -522,9 +505,9 @@ impl VerifiableServer( &self, rng: &mut R, - blinded_element: &BlindedElement, + blinded_element: &BlindedElement, metadata: Option<&[u8]>, - ) -> Result> { + ) -> Result> { let VerifiableServerBatchEvaluatePrepareResult { prepared_evaluation_elements: mut evaluation_elements, t, @@ -556,11 +539,10 @@ impl VerifiableServer, - ) -> Result> + ) -> Result> where - G: 'a, - H: 'a, - &'a I: IntoIterator>, + CS: 'a, + &'a I: IntoIterator>, <&'a I as IntoIterator>::IntoIter: ExactSizeIterator, { let VerifiableServerBatchEvaluatePrepareResult { @@ -588,14 +570,14 @@ impl VerifiableServer>>( + pub fn batch_evaluate_prepare<'a, I: Iterator>>( &self, blinded_elements: I, metadata: Option<&[u8]>, - ) -> Result> { + ) -> Result> { // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.2.1-1 - let context_string = get_context_string::(Mode::Verifiable); + let context_string = get_context_string::(Mode::Verifiable); let metadata = metadata.unwrap_or_default(); // context = "Context-" || contextString || I2OSP(len(info), 2) || info @@ -604,25 +586,19 @@ impl VerifiableServer(&context, Mode::Verifiable)?; + let m = CS::Group::hash_to_scalar::(&context, Mode::Verifiable)?; let t = self.sk + &m; let evaluation_elements = blinded_elements // To make a return type possible, we have to convert to a `fn` pointer, which isn't // possible if we `move` from context. - .zip(iter::repeat(G::invert_scalar(t))) - .map(, _)) -> _>::from(|(x, t)| { - PreparedEvaluationElement(EvaluationElement { - value: x.value * &t, - hash: PhantomData, - }) + .zip(iter::repeat(CS::Group::invert_scalar(t))) + .map(, _)) -> _>::from(|(x, t)| { + PreparedEvaluationElement(EvaluationElement(x.0 * &t)) })); Ok(VerifiableServerBatchEvaluatePrepareResult { prepared_evaluation_elements: evaluation_elements, - t: PreparedTscalar { - t, - hash: PhantomData, - }, + t: PreparedTscalar(t), }) } @@ -632,16 +608,15 @@ impl VerifiableServer, - ) -> Result> + PreparedTscalar(t): &PreparedTscalar, + ) -> Result> where - G: 'a + 'b, - H: 'a + 'b, - IB: Iterator> + ExactSizeIterator, - &'b IE: IntoIterator>, + CS: 'a + 'b, + IB: Iterator> + ExactSizeIterator, + &'b IE: IntoIterator>, <&'b IE as IntoIterator>::IntoIter: ExactSizeIterator, { - let g = G::base_elem(); + let g = CS::Group::base_elem(); let u = g * t; let proof = generate_proof( @@ -657,15 +632,15 @@ impl VerifiableServer) -> _>::from( - |element| element.0.copy(), - )); + .map() -> _>::from(|element| { + element.0.copy() + })); Ok(VerifiableServerBatchEvaluateFinishResult { messages, proof }) } /// Retrieves the server's public key - pub fn get_public_key(&self) -> G::Elem { + pub fn get_public_key(&self) -> ::Elem { self.pk } } @@ -676,108 +651,123 @@ impl VerifiableServer { +pub struct NonVerifiableClientBlindResult +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ /// The state to be persisted on the client - pub state: NonVerifiableClient, + pub state: NonVerifiableClient, /// The message to send to the server - pub message: BlindedElement, + pub message: BlindedElement, } /// Contains the fields that are returned by a non-verifiable server evaluate -pub struct NonVerifiableServerEvaluateResult +pub struct NonVerifiableServerEvaluateResult +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, { /// The message to send to the client - pub message: EvaluationElement, + pub message: EvaluationElement, } /// Contains the fields that are returned by a verifiable client blind -pub struct VerifiableClientBlindResult { +pub struct VerifiableClientBlindResult +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ /// The state to be persisted on the client - pub state: VerifiableClient, + pub state: VerifiableClient, /// The message to send to the server - pub message: BlindedElement, + pub message: BlindedElement, } /// Concrete return type for [`VerifiableClient::batch_finalize`]. -pub type VerifiableClientBatchFinalizeResult<'a, G, H, I, II, IC, IM> = FinalizeAfterUnblindResult< +pub type VerifiableClientBatchFinalizeResult<'a, C, I, II, IC, IM> = FinalizeAfterUnblindResult< 'a, - G, - H, + C, I, - Zip<<&'a II as IntoIterator>::IntoIter, VerifiableUnblindResult<'a, G, H, IC, IM>>, + Zip<<&'a II as IntoIterator>::IntoIter, VerifiableUnblindResult<'a, C, IC, IM>>, >; /// Contains the fields that are returned by a verifiable server evaluate -pub struct VerifiableServerEvaluateResult { +pub struct VerifiableServerEvaluateResult +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ /// The message to send to the client - pub message: EvaluationElement, + pub message: EvaluationElement, /// The proof for the client to verify - pub proof: Proof, + pub proof: Proof, } /// Contains prepared [`EvaluationElement`]s by a verifiable server batch /// evaluate preparation. -pub struct PreparedEvaluationElement( - EvaluationElement, -); +pub struct PreparedEvaluationElement(EvaluationElement) +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>; /// Contains the prepared `t` by a verifiable server batch evaluate preparation. #[derive(DeriveWhere)] #[derive_where(Zeroize(drop))] -pub struct PreparedTscalar { - t: G::Scalar, - #[derive_where(skip)] - hash: PhantomData, -} +pub struct PreparedTscalar(::Scalar) +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>; /// Contains the fields that are returned by a verifiable server batch evaluate /// preparation. pub struct VerifiableServerBatchEvaluatePrepareResult< 'a, - G: 'a + Group, - H: 'a + BlockSizeUser + Digest + FixedOutputReset, - I: Iterator>, -> { + CS: 'a + CipherSuite, + I: Iterator>, +> where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ /// Prepared [`EvaluationElement`]s that will become messages. #[allow(clippy::type_complexity)] pub prepared_evaluation_elements: Map< - Zip>, - fn((&BlindedElement, G::Scalar)) -> PreparedEvaluationElement, + Zip::Scalar>>, + fn((&BlindedElement, ::Scalar)) -> PreparedEvaluationElement, >, /// Prepared `t` needed to finish the verifiable server batch evaluation. - pub t: PreparedTscalar, + pub t: PreparedTscalar, } /// Contains the fields that are returned by a verifiable server batch evaluate /// finish. -pub struct VerifiableServerBatchEvaluateFinishResult< - 'a, - G: 'a + Group, - H: 'a + BlockSizeUser + Digest + FixedOutputReset, - I, -> where - &'a I: IntoIterator>, +pub struct VerifiableServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + &'a I: IntoIterator>, { /// The messages to send to the client #[allow(clippy::type_complexity)] pub messages: Map< <&'a I as IntoIterator>::IntoIter, - fn(&PreparedEvaluationElement) -> EvaluationElement, + fn(&PreparedEvaluationElement) -> EvaluationElement, >, /// The proof for the client to verify - pub proof: Proof, + pub proof: Proof, } /// Contains the fields that are returned by a verifiable server batch evaluate #[cfg(feature = "alloc")] -pub struct VerifiableServerBatchEvaluateResult< - G: Group, - H: BlockSizeUser + Digest + FixedOutputReset, -> { +pub struct VerifiableServerBatchEvaluateResult +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ /// The messages to send to the client - pub messages: alloc::vec::Vec>, + pub messages: alloc::vec::Vec>, /// The proof for the client to verify - pub proof: Proof, + pub proof: Proof, } /////////////////////////////////////////////// @@ -785,13 +775,14 @@ pub struct VerifiableServerBatchEvaluateResult< // ========================================= // /////////////////////////////////////////////// -impl BlindedElement { +impl BlindedElement +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ /// Only used to easier validate allocation fn copy(&self) -> Self { - Self { - value: self.value, - hash: PhantomData, - } + Self(self.0) } #[cfg(feature = "danger")] @@ -801,27 +792,25 @@ impl BlindedElement Self { - Self { - value, - hash: PhantomData, - } + pub fn from_value_unchecked(value: ::Elem) -> Self { + Self(value) } #[cfg(feature = "danger")] /// Exposes the internal value - pub fn value(&self) -> G::Elem { - self.value + pub fn value(&self) -> ::Elem { + self.0 } } -impl EvaluationElement { +impl EvaluationElement +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ /// Only used to easier validate allocation fn copy(&self) -> Self { - Self { - value: self.value, - hash: PhantomData, - } + Self(self.0) } #[cfg(feature = "danger")] @@ -831,77 +820,88 @@ impl EvaluationElement Self { - Self { - value, - hash: PhantomData, - } + pub fn from_value_unchecked(value: ::Elem) -> Self { + Self(value) } #[cfg(feature = "danger")] /// Exposes the internal value - pub fn value(&self) -> G::Elem { - self.value + pub fn value(&self) -> ::Elem { + self.0 } } +type BlindResult = ( + <::Group as Group>::Scalar, + <::Group as Group>::Elem, +); + // Inner function for blind. Returns the blind scalar and the blinded element -fn blind( +fn blind( input: &[u8], blinding_factor_rng: &mut R, mode: Mode, -) -> Result<(G::Scalar, G::Elem)> { +) -> Result> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ // Choose a random scalar that must be non-zero - let blind = G::random_scalar(blinding_factor_rng); - let blinded_element = deterministic_blind_unchecked::(input, &blind, mode)?; + let blind = CS::Group::random_scalar(blinding_factor_rng); + let blinded_element = deterministic_blind_unchecked::(input, &blind, mode)?; Ok((blind, blinded_element)) } // Inner function for blind that assumes that the blinding factor has already // been chosen, and therefore takes it as input. Does not check if the blinding // factor is non-zero. -fn deterministic_blind_unchecked( +fn deterministic_blind_unchecked( input: &[u8], - blind: &G::Scalar, + blind: &::Scalar, mode: Mode, -) -> Result { - let hashed_point = G::hash_to_curve::(&[input], mode)?; +) -> Result<::Elem> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ + let hashed_point = CS::Group::hash_to_curve::(&[input], mode)?; Ok(hashed_point * blind) } -type VerifiableUnblindResult<'a, G, H, IC, IM> = Map< +type VerifiableUnblindResult<'a, CS, IC, IM> = Map< Zip< Map< <&'a IC as IntoIterator>::IntoIter, - fn(&VerifiableClient) -> ::Scalar, + fn(&VerifiableClient) -> <::Group as Group>::Scalar, >, <&'a IM as IntoIterator>::IntoIter, >, - fn((::Scalar, &EvaluationElement)) -> ::Elem, + fn( + ( + <::Group as Group>::Scalar, + &EvaluationElement, + ), + ) -> <::Group as Group>::Elem, >; -fn verifiable_unblind< - 'a, - G: 'a + Group, - H: 'a + BlockSizeUser + Digest + FixedOutputReset, - IC, - IM, ->( +fn verifiable_unblind<'a, CS: 'a + CipherSuite, IC, IM>( clients: &'a IC, messages: &'a IM, - pk: G::Elem, - proof: &Proof, + pk: ::Elem, + proof: &Proof, info: &[u8], -) -> Result> +) -> Result> where - &'a IC: 'a + IntoIterator>, + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + &'a IC: 'a + IntoIterator>, <&'a IC as IntoIterator>::IntoIter: ExactSizeIterator, - &'a IM: 'a + IntoIterator>, + &'a IM: 'a + IntoIterator>, <&'a IM as IntoIterator>::IntoIter: ExactSizeIterator, { // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.4.2-2 - let context_string = get_context_string::(Mode::Verifiable); + let context_string = get_context_string::(Mode::Verifiable); // context = "Context-" || contextString || I2OSP(len(info), 2) || info let context = GenericArray::from(STR_CONTEXT) @@ -909,66 +909,65 @@ where .concat(i2osp_2(info.len())?); let context = [&context, info]; - let m = G::hash_to_scalar::(&context, Mode::Verifiable)?; + let m = CS::Group::hash_to_scalar::(&context, Mode::Verifiable)?; - let g = G::base_elem(); + let g = CS::Group::base_elem(); let t = g * &m; let u = t + &pk; let blinds = clients .into_iter() // Convert to `fn` pointer to make a return type possible. - .map() -> _>::from(|x| x.blind)); + .map() -> _>::from(|x| x.blind)); let evaluation_elements = messages.into_iter().map(EvaluationElement::copy); - let blinded_elements = clients.into_iter().map(|client| BlindedElement { - value: client.blinded_element, - hash: PhantomData, - }); + let blinded_elements = clients + .into_iter() + .map(|client| BlindedElement(client.blinded_element)); verify_proof(g, u, evaluation_elements, blinded_elements, proof)?; Ok(blinds .zip(messages.into_iter()) - .map(|(blind, x)| x.value * &G::invert_scalar(blind))) + .map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind))) } #[allow(clippy::many_single_char_names)] -fn generate_proof< - G: Group, - H: BlockSizeUser + Digest + FixedOutputReset, - R: RngCore + CryptoRng, ->( +fn generate_proof( rng: &mut R, - k: G::Scalar, - a: G::Elem, - b: G::Elem, - cs: impl Iterator> + ExactSizeIterator, - ds: impl Iterator> + ExactSizeIterator, -) -> Result> { + k: ::Scalar, + a: ::Elem, + b: ::Elem, + cs: impl Iterator> + ExactSizeIterator, + ds: impl Iterator> + ExactSizeIterator, +) -> Result> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.2.2-1 let (m, z) = compute_composites(Some(k), b, cs, ds)?; - let r = G::random_scalar(rng); + let r = CS::Group::random_scalar(rng); let t2 = a * &r; let t3 = m * &r; // Bm = GG.SerializeElement(B) - let bm = G::serialize_elem(b); + let bm = CS::Group::serialize_elem(b); // a0 = GG.SerializeElement(M) - let a0 = G::serialize_elem(m); + let a0 = CS::Group::serialize_elem(m); // a1 = GG.SerializeElement(Z) - let a1 = G::serialize_elem(z); + let a1 = CS::Group::serialize_elem(z); // a2 = GG.SerializeElement(t2) - let a2 = G::serialize_elem(t2); + let a2 = CS::Group::serialize_elem(t2); // a3 = GG.SerializeElement(t3) - let a3 = G::serialize_elem(t3); + let a3 = CS::Group::serialize_elem(t3); - let elem_len = G::ElemLen::U16.to_be_bytes(); + let elem_len = ::ElemLen::U16.to_be_bytes(); // challengeDST = "Challenge-" || contextString let challenge_dst = - GenericArray::from(STR_CHALLENGE).concat(get_context_string::(Mode::Verifiable)); + GenericArray::from(STR_CHALLENGE).concat(get_context_string::(Mode::Verifiable)); let challenge_dst_len = i2osp_2_array(challenge_dst); // h2Input = I2OSP(len(Bm), 2) || Bm || // I2OSP(len(a0), 2) || a0 || @@ -991,45 +990,45 @@ fn generate_proof< &challenge_dst, ]; - let c_scalar = G::hash_to_scalar::(&h2_input, Mode::Verifiable)?; + let c_scalar = CS::Group::hash_to_scalar::(&h2_input, Mode::Verifiable)?; let s_scalar = r - &(c_scalar * &k); - Ok(Proof { - c_scalar, - s_scalar, - hash: PhantomData, - }) + Ok(Proof { c_scalar, s_scalar }) } #[allow(clippy::many_single_char_names)] -fn verify_proof( - a: G::Elem, - b: G::Elem, - cs: impl Iterator> + ExactSizeIterator, - ds: impl Iterator> + ExactSizeIterator, - proof: &Proof, -) -> Result<()> { +fn verify_proof( + a: ::Elem, + b: ::Elem, + cs: impl Iterator> + ExactSizeIterator, + ds: impl Iterator> + ExactSizeIterator, + proof: &Proof, +) -> Result<()> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.4.1-2 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); // Bm = GG.SerializeElement(B) - let bm = G::serialize_elem(b); + let bm = CS::Group::serialize_elem(b); // a0 = GG.SerializeElement(M) - let a0 = G::serialize_elem(m); + let a0 = CS::Group::serialize_elem(m); // a1 = GG.SerializeElement(Z) - let a1 = G::serialize_elem(z); + let a1 = CS::Group::serialize_elem(z); // a2 = GG.SerializeElement(t2) - let a2 = G::serialize_elem(t2); + let a2 = CS::Group::serialize_elem(t2); // a3 = GG.SerializeElement(t3) - let a3 = G::serialize_elem(t3); + let a3 = CS::Group::serialize_elem(t3); - let elem_len = G::ElemLen::U16.to_be_bytes(); + let elem_len = ::ElemLen::U16.to_be_bytes(); // challengeDST = "Challenge-" || contextString let challenge_dst = - GenericArray::from(STR_CHALLENGE).concat(get_context_string::(Mode::Verifiable)); + GenericArray::from(STR_CHALLENGE).concat(get_context_string::(Mode::Verifiable)); let challenge_dst_len = i2osp_2_array(challenge_dst); // h2Input = I2OSP(len(Bm), 2) || Bm || // I2OSP(len(a0), 2) || a0 || @@ -1052,7 +1051,7 @@ fn verify_proof( &challenge_dst, ]; - let c = G::hash_to_scalar::(&h2_input, Mode::Verifiable)?; + let c = CS::Group::hash_to_scalar::(&h2_input, Mode::Verifiable)?; match c.ct_eq(&proof.c_scalar).into() { true => Ok(()), @@ -1060,27 +1059,35 @@ fn verify_proof( } } -type FinalizeAfterUnblindResult<'a, G, H, I, IE> = Map< +type FinalizeAfterUnblindResult<'a, C, I, IE> = Map< Zip)>>, - fn(((I, ::Elem), (&'a [u8], GenericArray))) -> Result>, + fn( + ( + (I, <::Group as Group>::Elem), + (&'a [u8], GenericArray), + ), + ) -> Result::Hash>>, >; fn finalize_after_unblind< 'a, - G: Group, - H: BlockSizeUser + Digest + FixedOutputReset, + CS: CipherSuite, I: AsRef<[u8]>, - IE: 'a + Iterator, + IE: 'a + Iterator::Elem)>, >( inputs_and_unblinded_elements: IE, info: &'a [u8], mode: Mode, -) -> Result> { +) -> Result> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.3.2-2 // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.4.3-1 // finalizeDST = "Finalize-" || contextString - let finalize_dst = GenericArray::from(STR_FINALIZE).concat(get_context_string::(mode)); + let finalize_dst = GenericArray::from(STR_FINALIZE).concat(get_context_string::(mode)); Ok(inputs_and_unblinded_elements // To make a return type possible, we have to convert to a `fn` pointer, @@ -1088,35 +1095,44 @@ fn finalize_after_unblind< .zip(iter::repeat((info, finalize_dst))) .map(|((input, unblinded_element), (info, finalize_dst))| { let finalize_dst_len = i2osp_2_array(finalize_dst); - let elem_len = G::ElemLen::U16.to_be_bytes(); + let elem_len = ::ElemLen::U16.to_be_bytes(); // hashInput = I2OSP(len(input), 2) || input || // I2OSP(len(info), 2) || info || // I2OSP(len(unblindedElement), 2) || unblindedElement || // I2OSP(len(finalizeDST), 2) || finalizeDST // return Hash(hashInput) - Ok(H::new() + Ok(CS::Hash::new() .chain_update(i2osp_2(input.as_ref().len())?) .chain_update(input.as_ref()) .chain_update(i2osp_2(info.len())?) .chain_update(info) .chain_update(elem_len) - .chain_update(G::serialize_elem(unblinded_element)) + .chain_update(CS::Group::serialize_elem(unblinded_element)) .chain_update(finalize_dst_len) .chain_update(finalize_dst) .finalize()) })) } -fn compute_composites( - k_option: Option, - b: G::Elem, - c_slice: impl Iterator> + ExactSizeIterator, - d_slice: impl Iterator> + ExactSizeIterator, -) -> Result<(G::Elem, G::Elem)> { +type ComputeCompositesResult = ( + <::Group as Group>::Elem, + <::Group as Group>::Elem, +); + +fn compute_composites( + k_option: Option<::Scalar>, + b: ::Elem, + c_slice: impl Iterator> + ExactSizeIterator, + d_slice: impl Iterator> + ExactSizeIterator, +) -> Result> +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ // https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html#section-3.3.2.3-2 - let elem_len = G::ElemLen::U16.to_be_bytes(); + let elem_len = ::ElemLen::U16.to_be_bytes(); if c_slice.len() != d_slice.len() { return Err(Error::MismatchedLengthsForCompositeInputs); @@ -1124,27 +1140,27 @@ fn compute_composites( let len = u16::try_from(c_slice.len()).map_err(|_| Error::SerializationError)?; - 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)); let composite_dst_len = i2osp_2_array(composite_dst); - let seed = H::new() + let seed = CS::Hash::new() .chain_update(&elem_len) - .chain_update(G::serialize_elem(b)) + .chain_update(CS::Group::serialize_elem(b)) .chain_update(i2osp_2_array(seed_dst)) .chain_update(seed_dst) .finalize(); let seed_len = i2osp_2(seed.len())?; - let mut m = G::identity_elem(); - let mut z = G::identity_elem(); + let mut m = CS::Group::identity_elem(); + let mut z = CS::Group::identity_elem(); for (i, (c, d)) in (0..len).zip(c_slice.zip(d_slice)) { // Ci = GG.SerializeElement(Cs[i]) - let ci = G::serialize_elem(c.value); + let ci = CS::Group::serialize_elem(c.0); // Di = GG.SerializeElement(Ds[i]) - let di = G::serialize_elem(d.value); + let di = CS::Group::serialize_elem(d.0); // h2Input = I2OSP(len(seed), 2) || seed || I2OSP(i, 2) || // I2OSP(len(Ci), 2) || Ci || // I2OSP(len(Di), 2) || Di || @@ -1160,11 +1176,11 @@ fn compute_composites( &composite_dst_len, &composite_dst, ]; - let di = G::hash_to_scalar::(&h2_input, Mode::Verifiable)?; - m = c.value * &di + &m; + let di = CS::Group::hash_to_scalar::(&h2_input, Mode::Verifiable)?; + m = c.0 * &di + &m; z = match k_option { Some(_) => z, - None => d.value * &di + &z, + None => d.0 * &di + &z, }; } @@ -1178,10 +1194,14 @@ fn compute_composites( /// Generates the contextString parameter as defined in /// -pub(crate) fn get_context_string(mode: Mode) -> GenericArray { +pub(crate) fn get_context_string(mode: Mode) -> GenericArray +where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, +{ GenericArray::from(STR_VOPRF) .concat([mode.to_u8()].into()) - .concat(G::SUITE_ID.to_be_bytes().into()) + .concat(CS::ID.to_be_bytes().into()) } /////////// @@ -1203,35 +1223,43 @@ mod tests { use super::*; use crate::Group; - fn prf( + fn prf( input: &[u8], - key: G::Scalar, + key: ::Scalar, info: &[u8], mode: Mode, - ) -> Output { - let point = G::hash_to_curve::(&[input], mode).unwrap(); + ) -> Output + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { + let point = CS::Group::hash_to_curve::(&[input], mode).unwrap(); - let context_string = get_context_string::(mode); + let context_string = get_context_string::(mode); let info_len = i2osp_2(info.len()).unwrap(); let context = [&STR_CONTEXT, context_string.as_slice(), &info_len, info]; - let m = G::hash_to_scalar::(&context, mode).unwrap(); + let m = CS::Group::hash_to_scalar::(&context, mode).unwrap(); - let res = point * &G::invert_scalar(key + &m); + let res = point * &CS::Group::invert_scalar(key + &m); - finalize_after_unblind::(Some((input, res)).into_iter(), info, mode) + finalize_after_unblind::(Some((input, res)).into_iter(), info, mode) .unwrap() .next() .unwrap() .unwrap() } - fn base_retrieval() { + fn base_retrieval() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { let input = b"input"; let info = b"info"; let mut rng = OsRng; - let client_blind_result = NonVerifiableClient::::blind(input, &mut rng).unwrap(); - let server = NonVerifiableServer::::new(&mut rng).unwrap(); + let client_blind_result = NonVerifiableClient::::blind(input, &mut rng).unwrap(); + let server = NonVerifiableServer::::new(&mut rng).unwrap(); let server_result = server .evaluate(&client_blind_result.message, Some(info)) .unwrap(); @@ -1239,16 +1267,20 @@ mod tests { .state .finalize(input, &server_result.message, Some(info)) .unwrap(); - let res2 = prf::(input, server.get_private_key(), info, Mode::Base); + let res2 = prf::(input, server.get_private_key(), info, Mode::Base); assert_eq!(client_finalize_result, res2); } - fn verifiable_retrieval() { + fn verifiable_retrieval() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { let input = b"input"; let info = b"info"; let mut rng = OsRng; - let client_blind_result = VerifiableClient::::blind(input, &mut rng).unwrap(); - let server = VerifiableServer::::new(&mut rng).unwrap(); + let client_blind_result = VerifiableClient::::blind(input, &mut rng).unwrap(); + let server = VerifiableServer::::new(&mut rng).unwrap(); let server_result = server .evaluate(&mut rng, &client_blind_result.message, Some(info)) .unwrap(); @@ -1262,22 +1294,26 @@ mod tests { Some(info), ) .unwrap(); - let res2 = prf::(input, server.get_private_key(), info, Mode::Verifiable); + let res2 = prf::(input, server.get_private_key(), info, Mode::Verifiable); assert_eq!(client_finalize_result, res2); } - fn verifiable_bad_public_key() { + fn verifiable_bad_public_key() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { let input = b"input"; let info = b"info"; let mut rng = OsRng; - let client_blind_result = VerifiableClient::::blind(input, &mut rng).unwrap(); - let server = VerifiableServer::::new(&mut rng).unwrap(); + let client_blind_result = VerifiableClient::::blind(input, &mut rng).unwrap(); + let server = VerifiableServer::::new(&mut rng).unwrap(); let server_result = server .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"], Mode::Base).unwrap() + CS::Group::hash_to_curve::(&[b"msg"], Mode::Base).unwrap() }; let client_finalize_result = client_blind_result.state.finalize( input, @@ -1289,7 +1325,11 @@ mod tests { assert!(client_finalize_result.is_err()); } - fn verifiable_batch_retrieval() { + fn verifiable_batch_retrieval() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { let info = b"info"; let mut rng = OsRng; let mut inputs = vec![]; @@ -1299,12 +1339,12 @@ mod tests { for _ in 0..num_iterations { let mut input = [0u8; 32]; rng.fill_bytes(&mut input); - let client_blind_result = VerifiableClient::::blind(&input, &mut rng).unwrap(); + let client_blind_result = VerifiableClient::::blind(&input, &mut rng).unwrap(); inputs.push(input); client_states.push(client_blind_result.state); client_messages.push(client_blind_result.message); } - let server = VerifiableServer::::new(&mut rng).unwrap(); + let server = VerifiableServer::::new(&mut rng).unwrap(); let VerifiableServerBatchEvaluatePrepareResult { prepared_evaluation_elements, t, @@ -1334,13 +1374,17 @@ mod tests { .unwrap(); let mut res2 = vec![]; for input in inputs.iter().take(num_iterations) { - let output = prf::(input, server.get_private_key(), info, Mode::Verifiable); + let output = prf::(input, server.get_private_key(), info, Mode::Verifiable); res2.push(output); } assert_eq!(client_finalize_result, res2); } - fn verifiable_batch_bad_public_key() { + fn verifiable_batch_bad_public_key() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { let info = b"info"; let mut rng = OsRng; let mut inputs = vec![]; @@ -1350,12 +1394,12 @@ mod tests { for _ in 0..num_iterations { let mut input = [0u8; 32]; rng.fill_bytes(&mut input); - let client_blind_result = VerifiableClient::::blind(&input, &mut rng).unwrap(); + let client_blind_result = VerifiableClient::::blind(&input, &mut rng).unwrap(); inputs.push(input); client_states.push(client_blind_result.state); client_messages.push(client_blind_result.message); } - let server = VerifiableServer::::new(&mut rng).unwrap(); + let server = VerifiableServer::::new(&mut rng).unwrap(); let VerifiableServerBatchEvaluatePrepareResult { prepared_evaluation_elements, t, @@ -1374,7 +1418,7 @@ mod tests { let messages: Vec<_> = messages.collect(); let wrong_pk = { // Choose a group element that is unlikely to be the right public key - G::hash_to_curve::(&[b"msg"], Mode::Base).unwrap() + CS::Group::hash_to_curve::(&[b"msg"], Mode::Base).unwrap() }; let client_finalize_result = VerifiableClient::batch_finalize( &inputs, @@ -1387,26 +1431,27 @@ mod tests { assert!(client_finalize_result.is_err()); } - fn base_inversion_unsalted() { + fn base_inversion_unsalted() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { let mut rng = OsRng; let mut input = [0u8; 64]; rng.fill_bytes(&mut input); let info = b"info"; - let client_blind_result = NonVerifiableClient::::blind(&input, &mut rng).unwrap(); + let client_blind_result = NonVerifiableClient::::blind(&input, &mut rng).unwrap(); let client_finalize_result = client_blind_result .state .finalize( &input, - &EvaluationElement { - value: client_blind_result.message.value, - hash: PhantomData, - }, + &EvaluationElement(client_blind_result.message.0), Some(info), ) .unwrap(); - let point = G::hash_to_curve::(&[&input], Mode::Base).unwrap(); - let res2 = finalize_after_unblind::( + let point = CS::Group::hash_to_curve::(&[&input], Mode::Base).unwrap(); + let res2 = finalize_after_unblind::( Some((input.as_ref(), point)).into_iter(), info, Mode::Base, @@ -1419,28 +1464,14 @@ mod tests { assert_eq!(client_finalize_result, res2); } - fn zeroize_base_client() { - let input = b"input"; - let mut rng = OsRng; - let client_blind_result = NonVerifiableClient::::blind(input, &mut rng).unwrap(); - - let mut state = client_blind_result.state; - Zeroize::zeroize(&mut state); - assert!(state.serialize().iter().all(|&x| x == 0)); - - let mut message = client_blind_result.message; - Zeroize::zeroize(&mut message); - assert!(message.serialize().iter().all(|&x| x == 0)); - } - - fn zeroize_verifiable_client() + fn zeroize_base_client() where - G::ScalarLen: Add, - Sum: ArrayLength, + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, { let input = b"input"; let mut rng = OsRng; - let client_blind_result = VerifiableClient::::blind(input, &mut rng).unwrap(); + let client_blind_result = NonVerifiableClient::::blind(input, &mut rng).unwrap(); let mut state = client_blind_result.state; Zeroize::zeroize(&mut state); @@ -1451,12 +1482,36 @@ mod tests { assert!(message.serialize().iter().all(|&x| x == 0)); } - fn zeroize_base_server() { + fn zeroize_verifiable_client() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + ::ScalarLen: Add<::ElemLen>, + Sum<::ScalarLen, ::ElemLen>: ArrayLength, + { + let input = b"input"; + let mut rng = OsRng; + let client_blind_result = VerifiableClient::::blind(input, &mut rng).unwrap(); + + let mut state = client_blind_result.state; + Zeroize::zeroize(&mut state); + assert!(state.serialize().iter().all(|&x| x == 0)); + + let mut message = client_blind_result.message; + Zeroize::zeroize(&mut message); + assert!(message.serialize().iter().all(|&x| x == 0)); + } + + fn zeroize_base_server() + where + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + { let input = b"input"; let info = b"info"; let mut rng = OsRng; - let client_blind_result = NonVerifiableClient::::blind(input, &mut rng).unwrap(); - let server = NonVerifiableServer::::new(&mut rng).unwrap(); + let client_blind_result = NonVerifiableClient::::blind(input, &mut rng).unwrap(); + let server = NonVerifiableServer::::new(&mut rng).unwrap(); let server_result = server .evaluate(&client_blind_result.message, Some(info)) .unwrap(); @@ -1470,18 +1525,20 @@ mod tests { assert!(message.serialize().iter().all(|&x| x == 0)); } - fn zeroize_verifiable_server() + fn zeroize_verifiable_server() where - G::ScalarLen: Add, - Sum: ArrayLength, - G::ScalarLen: Add, - Sum: ArrayLength, + ::OutputSize: + IsLess + IsLessOrEqual<::BlockSize>, + ::ScalarLen: Add<::ElemLen>, + Sum<::ScalarLen, ::ElemLen>: ArrayLength, + ::ScalarLen: Add<::ScalarLen>, + Sum<::ScalarLen, ::ScalarLen>: ArrayLength, { let input = b"input"; let info = b"info"; let mut rng = OsRng; - let client_blind_result = VerifiableClient::::blind(input, &mut rng).unwrap(); - let server = VerifiableServer::::new(&mut rng).unwrap(); + let client_blind_result = VerifiableClient::::blind(input, &mut rng).unwrap(); + let server = VerifiableServer::::new(&mut rng).unwrap(); let server_result = server .evaluate(&mut rng, &client_blind_result.message, Some(info)) .unwrap(); @@ -1501,42 +1558,36 @@ mod tests { #[test] fn test_functionality() -> Result<()> { + use p256::NistP256; + #[cfg(feature = "ristretto255")] { - use sha2::Sha512; - use crate::Ristretto255; - 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")] - { - use p256_::NistP256; - use sha2::Sha256; + 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::(); Ok(()) }