General improvements (#65)

* Relax `hash_to_scalar` and `hash_to_group` bounds

* Rename `util` to `common` and shuffle some stuff around

* Don't generate unnecessary public key

* Simplify 'elliptic-curve` serializing element implementation

* Fix new Clippy 1.59 warnings

* Simplify `Ristretto255::random_scalar` implementation

* Update `derive-where`

* Fix panic during Ristretto255 deserialization

* Remove iteration during de/serialization
This commit is contained in:
daxpedda
2022-04-01 12:18:32 -07:00
committed by GitHub
parent a366a14125
commit 1a61401272
11 changed files with 231 additions and 244 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ std = ["alloc"]
[dependencies]
curve25519-dalek = { version = "=4.0.0-pre.1", default-features = false, optional = true }
derive-where = { version = "=1.0.0-rc.2", features = ["zeroize-on-drop"] }
derive-where = { version = "=1.0.0-rc.3", features = ["zeroize-on-drop"] }
digest = "0.10"
displaydoc = { version = "0.2", default-features = false }
elliptic-curve = { version = "=0.12.0-pre.1", features = [
+44 -97
View File
@@ -5,7 +5,7 @@
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
//! Helper functions
//! Common functionality between multiple OPRF modes.
use core::convert::TryFrom;
@@ -18,7 +18,6 @@ use generic_array::{ArrayLength, GenericArray};
use rand_core::{CryptoRng, RngCore};
use subtle::ConstantTimeEq;
use crate::group::{STR_HASH_TO_GROUP, STR_HASH_TO_SCALAR};
#[cfg(feature = "serde")]
use crate::serialization::serde::{Element, Scalar};
use crate::{CipherSuite, Error, Group, InternalError, Result};
@@ -35,6 +34,8 @@ pub(crate) const STR_COMPOSITE: [u8; 9] = *b"Composite";
pub(crate) const STR_CHALLENGE: [u8; 9] = *b"Challenge";
pub(crate) const STR_INFO: [u8; 4] = *b"Info";
pub(crate) const STR_VOPRF: [u8; 8] = *b"VOPRF09-";
pub(crate) const STR_HASH_TO_SCALAR: [u8; 13] = *b"HashToScalar-";
pub(crate) const STR_HASH_TO_GROUP: [u8; 12] = *b"HashToGroup-";
/// Determines the mode of operation (either base mode or verifiable mode). This
/// is only used for custom implementations for [`Group`].
@@ -195,7 +196,7 @@ where
let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(mode));
// This can't fail, the size of the `input` is known.
let c_scalar = CS::Group::hash_to_scalar::<CS>(&h2_input, &dst).unwrap();
let c_scalar = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst).unwrap();
let s_scalar = r - &(c_scalar * &k);
Ok(Proof { c_scalar, s_scalar })
@@ -255,7 +256,7 @@ where
let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(mode));
// This can't fail, the size of the `input` is known.
let c = CS::Group::hash_to_scalar::<CS>(&h2_input, &dst).unwrap();
let c = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst).unwrap();
match c.ct_eq(&proof.c_scalar).into() {
true => Ok(()),
@@ -333,7 +334,7 @@ where
let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(mode));
// This can't fail, the size of the `input` is known.
let di = CS::Group::hash_to_scalar::<CS>(&h2_input, &dst).unwrap();
let di = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst).unwrap();
m = c * &di + &m;
z = match k_option {
Some(_) => z,
@@ -354,6 +355,39 @@ where
// =============== //
/////////////////////
/// Can only fail with [`Error::DeriveKeyPair`] and [`Error::Protocol`].
pub(crate) fn derive_key<CS: CipherSuite>(
seed: &[u8],
info: &[u8],
mode: Mode,
) -> Result<<CS::Group as Group>::Scalar, Error>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let context_string = create_context_string::<CS>(mode);
let dst = GenericArray::from(STR_DERIVE_KEYPAIR).concat(context_string);
let info_len = i2osp_2(info.len()).map_err(|_| Error::DeriveKeyPair)?;
for counter in 0_u8..=u8::MAX {
// deriveInput = seed || I2OSP(len(info), 2) || info
// skS = G.HashToScalar(deriveInput || I2OSP(counter, 1), DST = "DeriveKeyPair"
// || contextString)
let sk_s = CS::Group::hash_to_scalar::<CS::Hash>(
&[seed, &info_len, info, &counter.to_be_bytes()],
&dst,
)
.map_err(|_| Error::DeriveKeyPair)?;
if !bool::from(CS::Group::is_zero_scalar(sk_s)) {
return Ok(sk_s);
}
}
Err(Error::Protocol)
}
type DeriveKeypairResult<CS> = (
<<CS as CipherSuite>::Group as Group>::Scalar,
<<CS as CipherSuite>::Group as Group>::Elem,
@@ -369,28 +403,10 @@ where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let context_string = create_context_string::<CS>(mode);
let dst = GenericArray::from(STR_DERIVE_KEYPAIR).concat(context_string);
let sk_s = derive_key::<CS>(seed, info, mode)?;
let pk_s = CS::Group::base_elem() * &sk_s;
let info_len = i2osp_2(info.len()).map_err(|_| Error::DeriveKeyPair)?;
for counter in 0_u8..=u8::MAX {
// deriveInput = seed || I2OSP(len(info), 2) || info
// skS = G.HashToScalar(deriveInput || I2OSP(counter, 1), DST = "DeriveKeyPair"
// || contextString)
let sk_s = <CS::Group as Group>::hash_to_scalar::<CS>(
&[seed, &info_len, info, &counter.to_be_bytes()],
&dst,
)
.map_err(|_| Error::DeriveKeyPair)?;
if !bool::from(CS::Group::is_zero_scalar(sk_s)) {
let pk_s = CS::Group::base_elem() * &sk_s;
return Ok((sk_s, pk_s));
}
}
Err(Error::Protocol)
Ok((sk_s, pk_s))
}
/// Inner function for blind that assumes that the blinding factor has already
@@ -408,7 +424,8 @@ where
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
let hashed_point = CS::Group::hash_to_curve::<CS>(&[input], &dst).map_err(|_| Error::Input)?;
let hashed_point =
CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst).map_err(|_| Error::Input)?;
Ok(hashed_point * blind)
}
@@ -440,73 +457,3 @@ pub(crate) fn i2osp_2_array<L: ArrayLength<u8> + IsLess<U256>>(
) -> GenericArray<u8, U2> {
L::U16.to_be_bytes().into()
}
#[cfg(test)]
mod unit_tests {
use proptest::collection::vec;
use proptest::prelude::*;
use crate::{
BlindedElement, EvaluationElement, OprfClient, OprfServer, PoprfClient, PoprfServer, Proof,
VoprfClient, VoprfServer,
};
macro_rules! test_deserialize {
($item:ident, $bytes:ident) => {
#[cfg(feature = "ristretto255")]
{
let _ = $item::<crate::Ristretto255>::deserialize(&$bytes[..]);
}
let _ = $item::<p256::NistP256>::deserialize(&$bytes[..]);
};
}
proptest! {
#[test]
fn test_nocrash_oprf_client(bytes in vec(any::<u8>(), 0..200)) {
test_deserialize!(OprfClient, bytes);
}
#[test]
fn test_nocrash_voprf_client(bytes in vec(any::<u8>(), 0..200)) {
test_deserialize!(VoprfClient, bytes);
}
#[test]
fn test_nocrash_poprf_client(bytes in vec(any::<u8>(), 0..200)) {
test_deserialize!(PoprfClient, bytes);
}
#[test]
fn test_nocrash_oprf_server(bytes in vec(any::<u8>(), 0..200)) {
test_deserialize!(OprfServer, bytes);
}
#[test]
fn test_nocrash_voprf_server(bytes in vec(any::<u8>(), 0..200)) {
test_deserialize!(VoprfServer, bytes);
}
#[test]
fn test_nocrash_poprf_server(bytes in vec(any::<u8>(), 0..200)) {
test_deserialize!(PoprfServer, bytes);
}
#[test]
fn test_nocrash_blinded_element(bytes in vec(any::<u8>(), 0..200)) {
test_deserialize!(BlindedElement, bytes);
}
#[test]
fn test_nocrash_evaluation_element(bytes in vec(any::<u8>(), 0..200)) {
test_deserialize!(EvaluationElement, bytes);
}
#[test]
fn test_nocrash_proof(bytes in vec(any::<u8>(), 0..200)) {
test_deserialize!(Proof, bytes);
}
}
}
+12 -20
View File
@@ -6,7 +6,7 @@
// of this source tree.
use digest::core_api::BlockSizeUser;
use digest::OutputSizeUser;
use digest::Digest;
use elliptic_curve::group::cofactor::CofactorGroup;
use elliptic_curve::hash2curve::{ExpandMsgXmd, FromOkm, GroupDigest};
use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint};
@@ -18,12 +18,12 @@ use generic_array::GenericArray;
use rand_core::{CryptoRng, RngCore};
use super::Group;
use crate::{CipherSuite, Error, InternalError, Result};
use crate::{Error, InternalError, Result};
impl<C> Group for C
where
C: GroupDigest,
ProjectivePoint<Self>: CofactorGroup,
ProjectivePoint<Self>: CofactorGroup + ToEncodedPoint<Self>,
FieldSize<Self>: ModulusSize,
AffinePoint<Self>: FromEncodedPoint<Self> + ToEncodedPoint<Self>,
Scalar<Self>: FromOkm,
@@ -38,28 +38,21 @@ where
// 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<CS: CipherSuite>(
input: &[&[u8]],
dst: &[u8],
) -> Result<Self::Elem, InternalError>
fn hash_to_curve<H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Elem, InternalError>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
H: Digest + BlockSizeUser,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
{
Self::hash_from_bytes::<ExpandMsgXmd<CS::Hash>>(input, dst)
.map_err(|_| InternalError::Input)
Self::hash_from_bytes::<ExpandMsgXmd<H>>(input, dst).map_err(|_| InternalError::Input)
}
// Implements the `HashToScalar()` function
fn hash_to_scalar<CS: CipherSuite>(
input: &[&[u8]],
dst: &[u8],
) -> Result<Self::Scalar, InternalError>
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Scalar, InternalError>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
H: Digest + BlockSizeUser,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
{
<Self as GroupDigest>::hash_to_scalar::<ExpandMsgXmd<CS::Hash>>(input, dst)
<Self as GroupDigest>::hash_to_scalar::<ExpandMsgXmd<H>>(input, dst)
.map_err(|_| InternalError::Input)
}
@@ -72,8 +65,7 @@ where
}
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> {
let point: AffinePoint<Self> = elem.into();
let bytes = point.to_encoded_point(true);
let bytes = elem.to_encoded_point(true);
let bytes = bytes.as_bytes();
let mut result = GenericArray::default();
result[..bytes.len()].copy_from_slice(bytes);
+8 -17
View File
@@ -14,7 +14,7 @@ mod ristretto;
use core::ops::{Add, Mul, Sub};
use digest::core_api::BlockSizeUser;
use digest::OutputSizeUser;
use digest::Digest;
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
use generic_array::{ArrayLength, GenericArray};
use rand_core::{CryptoRng, RngCore};
@@ -23,10 +23,7 @@ pub use ristretto::Ristretto255;
use subtle::{Choice, ConstantTimeEq};
use zeroize::Zeroize;
use crate::{CipherSuite, InternalError, Result};
pub(crate) const STR_HASH_TO_SCALAR: [u8; 13] = *b"HashToScalar-";
pub(crate) const STR_HASH_TO_GROUP: [u8; 12] = *b"HashToGroup-";
use crate::{InternalError, Result};
/// A prime-order subgroup of a base field (EC, prime-order field ...). This
/// subgroup is noted additively — as in the draft RFC — in this trait.
@@ -57,26 +54,20 @@ pub trait Group {
/// # Errors
/// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer
/// then [`u16::MAX`].
fn hash_to_curve<CS: CipherSuite>(
input: &[&[u8]],
dst: &[u8],
) -> Result<Self::Elem, InternalError>
fn hash_to_curve<H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Elem, InternalError>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>;
H: Digest + BlockSizeUser,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>;
/// Hashes a slice of pseudo-random bytes to a scalar
///
/// # Errors
/// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer
/// then [`u16::MAX`].
fn hash_to_scalar<CS: CipherSuite>(
input: &[&[u8]],
dst: &[u8],
) -> Result<Self::Scalar, InternalError>
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Scalar, InternalError>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>;
H: Digest + BlockSizeUser,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>;
/// Get the base point for the group
fn base_elem() -> Self::Elem;
+15 -21
View File
@@ -10,7 +10,7 @@ use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
use curve25519_dalek::scalar::Scalar;
use curve25519_dalek::traits::Identity;
use digest::core_api::BlockSizeUser;
use digest::OutputSizeUser;
use digest::Digest;
use elliptic_curve::hash2curve::{ExpandMsg, ExpandMsgXmd, Expander};
use generic_array::typenum::{IsLess, IsLessOrEqual, U256, U32, U64};
use generic_array::GenericArray;
@@ -18,7 +18,7 @@ use rand_core::{CryptoRng, RngCore};
use subtle::ConstantTimeEq;
use super::Group;
use crate::{CipherSuite, Error, InternalError, Result};
use crate::{Error, InternalError, Result};
/// [`Group`] implementation for Ristretto255.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
@@ -48,16 +48,13 @@ 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<CS: CipherSuite>(
input: &[&[u8]],
dst: &[u8],
) -> Result<Self::Elem, InternalError>
fn hash_to_curve<H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Elem, InternalError>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
H: Digest + BlockSizeUser,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
{
let mut uniform_bytes = GenericArray::<_, U64>::default();
ExpandMsgXmd::<CS::Hash>::expand_message(input, dst, 64)
ExpandMsgXmd::<H>::expand_message(input, dst, 64)
.map_err(|_| InternalError::Input)?
.fill_bytes(&mut uniform_bytes);
@@ -66,16 +63,13 @@ impl Group for Ristretto255 {
// Implements the `HashToScalar()` function from
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.1
fn hash_to_scalar<CS: CipherSuite>(
input: &[&[u8]],
dst: &[u8],
) -> Result<Self::Scalar, InternalError>
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Scalar, InternalError>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
H: Digest + BlockSizeUser,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
{
let mut uniform_bytes = GenericArray::<_, U64>::default();
ExpandMsgXmd::<CS::Hash>::expand_message(input, dst, 64)
ExpandMsgXmd::<H>::expand_message(input, dst, 64)
.map_err(|_| InternalError::Input)?
.fill_bytes(&mut uniform_bytes);
@@ -96,6 +90,10 @@ impl Group for Ristretto255 {
}
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem> {
if element_bits.len() != 32 {
return Err(Error::Deserialization);
}
CompressedRistretto::from_slice(element_bits)
.decompress()
.filter(|point| point != &RistrettoPoint::identity())
@@ -104,11 +102,7 @@ impl Group for Ristretto255 {
fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
loop {
let scalar = {
let mut scalar_bytes = [0u8; 64];
rng.fill_bytes(&mut scalar_bytes);
Scalar::from_bytes_mod_order_wide(&scalar_bytes)
};
let scalar = Scalar::random(rng);
if scalar != Scalar::zero() {
break scalar;
+4 -2
View File
@@ -483,12 +483,12 @@ extern crate std;
extern crate serde_ as serde;
mod ciphersuite;
mod common;
mod error;
mod group;
mod oprf;
mod poprf;
mod serialization;
mod util;
mod voprf;
#[cfg(test)]
@@ -497,6 +497,9 @@ mod tests;
// Exports
pub use crate::ciphersuite::CipherSuite;
pub use crate::common::{
BlindedElement, EvaluationElement, Mode, PreparedEvaluationElement, Proof,
};
pub use crate::error::{Error, InternalError, Result};
pub use crate::group::Group;
#[cfg(feature = "ristretto255")]
@@ -513,7 +516,6 @@ pub use crate::serialization::{
BlindedElementLen, EvaluationElementLen, OprfClientLen, OprfServerLen, PoprfClientLen,
PoprfServerLen, ProofLen, VoprfClientLen, VoprfServerLen,
};
pub use crate::util::{BlindedElement, EvaluationElement, Mode, PreparedEvaluationElement, Proof};
#[cfg(feature = "alloc")]
pub use crate::voprf::VoprfServerBatchEvaluateResult;
pub use crate::voprf::{
+8 -9
View File
@@ -16,12 +16,12 @@ use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256};
use generic_array::GenericArray;
use rand_core::{CryptoRng, RngCore};
use crate::common::{
derive_key, deterministic_blind_unchecked, i2osp_2, BlindedElement, EvaluationElement, Mode,
STR_FINALIZE,
};
#[cfg(feature = "serde")]
use crate::serialization::serde::Scalar;
use crate::util::{
derive_keypair, deterministic_blind_unchecked, i2osp_2, BlindedElement, EvaluationElement,
Mode, STR_FINALIZE,
};
use crate::{CipherSuite, Error, Group, Result};
///////////////
@@ -189,7 +189,7 @@ where
/// then `u16::MAX - 3`.
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
pub fn new_from_seed(seed: &[u8], info: &[u8]) -> Result<Self> {
let (sk, _) = derive_keypair::<CS>(seed, info, Mode::Oprf)?;
let sk = derive_key::<CS>(seed, info, Mode::Oprf)?;
Ok(Self { sk })
}
@@ -279,8 +279,7 @@ mod tests {
use rand::rngs::OsRng;
use super::*;
use crate::group::STR_HASH_TO_GROUP;
use crate::util::create_context_string;
use crate::common::{create_context_string, STR_HASH_TO_GROUP};
use crate::Group;
fn prf<CS: CipherSuite>(
@@ -294,7 +293,7 @@ mod tests {
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
let point = CS::Group::hash_to_curve::<CS>(&[input], &dst).unwrap();
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst).unwrap();
let res = point * &key;
@@ -335,7 +334,7 @@ mod tests {
let dst =
GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(Mode::Oprf));
let point = CS::Group::hash_to_curve::<CS>(&[&input], &dst).unwrap();
let point = CS::Group::hash_to_curve::<CS::Hash>(&[&input], &dst).unwrap();
let res2 = finalize_after_unblind::<CS, _, _>(iter::once((input.as_ref(), point)), &[])
.next()
.unwrap()
+9 -10
View File
@@ -19,14 +19,13 @@ use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256};
use generic_array::GenericArray;
use rand_core::{CryptoRng, RngCore};
use crate::group::STR_HASH_TO_SCALAR;
#[cfg(feature = "serde")]
use crate::serialization::serde::{Element, Scalar};
use crate::util::{
use crate::common::{
create_context_string, derive_keypair, deterministic_blind_unchecked, generate_proof, i2osp_2,
verify_proof, BlindedElement, EvaluationElement, Mode, PreparedEvaluationElement, Proof,
STR_FINALIZE, STR_INFO,
STR_FINALIZE, STR_HASH_TO_SCALAR, STR_INFO,
};
#[cfg(feature = "serde")]
use crate::serialization::serde::{Element, Scalar};
use crate::{CipherSuite, Error, Group, Result};
////////////////////////////
@@ -596,7 +595,7 @@ where
let dst =
GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(Mode::Poprf));
// This can't fail, the size of the `input` is known.
let m = CS::Group::hash_to_scalar::<CS>(&framed_info, &dst).unwrap();
let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst).unwrap();
let t = CS::Group::base_elem() * &m;
let tweaked_key = t + &pk;
@@ -634,7 +633,7 @@ where
let dst =
GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(Mode::Poprf));
// This can't fail, the size of the `input` is known.
let m = CS::Group::hash_to_scalar::<CS>(&framed_info, &dst).unwrap();
let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst).unwrap();
let t = sk + &m;
@@ -772,7 +771,7 @@ mod tests {
use rand::rngs::OsRng;
use super::*;
use crate::group::STR_HASH_TO_GROUP;
use crate::common::STR_HASH_TO_GROUP;
use crate::Group;
fn prf<CS: CipherSuite>(
@@ -788,7 +787,7 @@ mod tests {
let t = compute_tweak::<CS>(key, Some(info)).unwrap();
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
let point = CS::Group::hash_to_curve::<CS>(&[input], &dst).unwrap();
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst).unwrap();
// evaluatedElement = G.ScalarInverse(t) * blindedElement
let res = point * &CS::Group::invert_scalar(t);
@@ -844,7 +843,7 @@ mod tests {
let dst = GenericArray::from(STR_HASH_TO_GROUP)
.concat(create_context_string::<CS>(Mode::Oprf));
// Choose a group element that is unlikely to be the right public key
CS::Group::hash_to_curve::<CS>(&[b"msg"], &dst).unwrap()
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst).unwrap()
};
let client_finalize_result = client_blind_result.state.finalize(
input,
+119 -51
View File
@@ -43,10 +43,8 @@ where
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied();
let blind = deserialize_scalar::<CS::Group, _>(&mut input)?;
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let blind = deserialize_scalar::<CS::Group>(&mut input)?;
Ok(Self { blind })
}
@@ -77,11 +75,9 @@ where
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied();
let blind = deserialize_scalar::<CS::Group, _>(&mut input)?;
let blinded_element = deserialize_elem::<CS::Group, _>(&mut input)?;
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let blind = deserialize_scalar::<CS::Group>(&mut input)?;
let blinded_element = deserialize_elem::<CS::Group>(&mut input)?;
Ok(Self {
blind,
@@ -115,11 +111,9 @@ where
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied();
let blind = deserialize_scalar::<CS::Group, _>(&mut input)?;
let blinded_element = deserialize_elem::<CS::Group, _>(&mut input)?;
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let blind = deserialize_scalar::<CS::Group>(&mut input)?;
let blinded_element = deserialize_elem::<CS::Group>(&mut input)?;
Ok(Self {
blind,
@@ -145,10 +139,8 @@ where
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied();
let sk = deserialize_scalar::<CS::Group, _>(&mut input)?;
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let sk = deserialize_scalar::<CS::Group>(&mut input)?;
Ok(Self { sk })
}
@@ -178,11 +170,9 @@ where
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied();
let sk = deserialize_scalar::<CS::Group, _>(&mut input)?;
let pk = deserialize_elem::<CS::Group, _>(&mut input)?;
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let sk = deserialize_scalar::<CS::Group>(&mut input)?;
let pk = deserialize_elem::<CS::Group>(&mut input)?;
Ok(Self { sk, pk })
}
@@ -212,11 +202,9 @@ where
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied();
let sk = deserialize_scalar::<CS::Group, _>(&mut input)?;
let pk = deserialize_elem::<CS::Group, _>(&mut input)?;
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let sk = deserialize_scalar::<CS::Group>(&mut input)?;
let pk = deserialize_elem::<CS::Group>(&mut input)?;
Ok(Self { sk, pk })
}
@@ -247,11 +235,9 @@ where
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied();
let c_scalar = deserialize_scalar::<CS::Group, _>(&mut input)?;
let s_scalar = deserialize_scalar::<CS::Group, _>(&mut input)?;
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let c_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
let s_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
Ok(Proof { c_scalar, s_scalar })
}
@@ -274,10 +260,8 @@ where
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied();
let value = deserialize_elem::<CS::Group, _>(&mut input)?;
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let value = deserialize_elem::<CS::Group>(&mut input)?;
Ok(Self(value))
}
@@ -300,27 +284,41 @@ where
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(input: &[u8]) -> Result<Self> {
let mut input = input.iter().copied();
let value = deserialize_elem::<CS::Group, _>(&mut input)?;
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let value = deserialize_elem::<CS::Group>(&mut input)?;
Ok(Self(value))
}
}
fn deserialize_elem<G: Group, I: Iterator<Item = u8>>(input: &mut I) -> Result<G::Elem> {
let input = input.by_ref().take(G::ElemLen::USIZE);
GenericArray::<_, G::ElemLen>::from_exact_iter(input)
.ok_or(Error::Deserialization)
.and_then(|bytes| G::deserialize_elem(&bytes))
fn deserialize_elem<G: Group>(input: &mut &[u8]) -> Result<G::Elem> {
let input = input
.take_ext(G::ElemLen::USIZE)
.ok_or(Error::Deserialization)?;
G::deserialize_elem(input)
}
fn deserialize_scalar<G: Group, I: Iterator<Item = u8>>(input: &mut I) -> Result<G::Scalar> {
let input = input.by_ref().take(G::ScalarLen::USIZE);
GenericArray::<_, G::ScalarLen>::from_exact_iter(input)
.ok_or(Error::Deserialization)
.and_then(|bytes| G::deserialize_scalar(&bytes))
fn deserialize_scalar<G: Group>(input: &mut &[u8]) -> Result<G::Scalar> {
let input = input
.take_ext(G::ScalarLen::USIZE)
.ok_or(Error::Deserialization)?;
G::deserialize_scalar(input)
}
trait SliceExt {
fn take_ext(self: &mut &Self, take: usize) -> Option<&Self>;
}
impl<T> SliceExt for [T] {
fn take_ext(self: &mut &Self, take: usize) -> Option<&Self> {
if take > self.len() {
return None;
}
let (front, back) = self.split_at(take);
*self = back;
Some(front)
}
}
#[cfg(feature = "serde")]
@@ -372,3 +370,73 @@ pub(crate) mod serde {
}
}
}
#[cfg(test)]
mod test {
use proptest::collection::vec;
use proptest::prelude::*;
use crate::{
BlindedElement, EvaluationElement, OprfClient, OprfServer, PoprfClient, PoprfServer, Proof,
VoprfClient, VoprfServer,
};
macro_rules! test_deserialize {
($item:ident, $bytes:ident) => {
#[cfg(feature = "ristretto255")]
{
let _ = $item::<crate::Ristretto255>::deserialize(&$bytes[..]);
}
let _ = $item::<p256::NistP256>::deserialize(&$bytes[..]);
};
}
proptest! {
#[test]
fn test_nocrash_oprf_client(bytes in vec(any::<u8>(), 0..200)) {
test_deserialize!(OprfClient, bytes);
}
#[test]
fn test_nocrash_voprf_client(bytes in vec(any::<u8>(), 0..200)) {
test_deserialize!(VoprfClient, bytes);
}
#[test]
fn test_nocrash_poprf_client(bytes in vec(any::<u8>(), 0..200)) {
test_deserialize!(PoprfClient, bytes);
}
#[test]
fn test_nocrash_oprf_server(bytes in vec(any::<u8>(), 0..200)) {
test_deserialize!(OprfServer, bytes);
}
#[test]
fn test_nocrash_voprf_server(bytes in vec(any::<u8>(), 0..200)) {
test_deserialize!(VoprfServer, bytes);
}
#[test]
fn test_nocrash_poprf_server(bytes in vec(any::<u8>(), 0..200)) {
test_deserialize!(PoprfServer, bytes);
}
#[test]
fn test_nocrash_blinded_element(bytes in vec(any::<u8>(), 0..200)) {
test_deserialize!(BlindedElement, bytes);
}
#[test]
fn test_nocrash_evaluation_element(bytes in vec(any::<u8>(), 0..200)) {
test_deserialize!(EvaluationElement, bytes);
}
#[test]
fn test_nocrash_proof(bytes in vec(any::<u8>(), 0..200)) {
test_deserialize!(Proof, bytes);
}
}
}
+4 -8
View File
@@ -5,7 +5,7 @@
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
use alloc::string::{String, ToString};
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use core::ops::Add;
@@ -60,19 +60,15 @@ fn populate_test_vectors(values: &JsonValue) -> VOPRFTestVectorParameters {
fn decode(values: &JsonValue, key: &str) -> Vec<u8> {
values[key]
.as_str()
.and_then(|s| hex::decode(&s.to_string()).ok())
.and_then(|s| hex::decode(&s).ok())
.unwrap_or_default()
}
fn decode_vec(values: &JsonValue, key: &str) -> Vec<Vec<u8>> {
let s = values[key].as_str().unwrap();
let res = match s.contains(',') {
true => Some(
s.split(',')
.map(|x| hex::decode(&x.to_string()).unwrap())
.collect(),
),
false => Some(vec![hex::decode(&s.to_string()).unwrap()]),
true => Some(s.split(',').map(|x| hex::decode(&x).unwrap()).collect()),
false => Some(vec![hex::decode(&s).unwrap()]),
};
res.unwrap()
}
+7 -8
View File
@@ -18,12 +18,12 @@ use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256};
use generic_array::GenericArray;
use rand_core::{CryptoRng, RngCore};
#[cfg(feature = "serde")]
use crate::serialization::serde::{Element, Scalar};
use crate::util::{
use crate::common::{
derive_keypair, deterministic_blind_unchecked, generate_proof, i2osp_2, verify_proof,
BlindedElement, EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE,
};
#[cfg(feature = "serde")]
use crate::serialization::serde::{Element, Scalar};
use crate::{CipherSuite, Error, Group, Result};
////////////////////////////
@@ -576,8 +576,7 @@ mod tests {
use rand::rngs::OsRng;
use super::*;
use crate::group::STR_HASH_TO_GROUP;
use crate::util::create_context_string;
use crate::common::{create_context_string, STR_HASH_TO_GROUP};
use crate::Group;
fn prf<CS: CipherSuite>(
@@ -590,7 +589,7 @@ mod tests {
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
let point = CS::Group::hash_to_curve::<CS>(&[input], &dst).unwrap();
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst).unwrap();
let res = point * &key;
@@ -705,7 +704,7 @@ mod tests {
let dst = GenericArray::from(STR_HASH_TO_GROUP)
.concat(create_context_string::<CS>(Mode::Oprf));
// Choose a group element that is unlikely to be the right public key
CS::Group::hash_to_curve::<CS>(&[b"msg"], &dst).unwrap()
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst).unwrap()
};
let client_finalize_result =
VoprfClient::batch_finalize(&inputs, &client_states, &messages, &proof, wrong_pk);
@@ -726,7 +725,7 @@ mod tests {
let dst = GenericArray::from(STR_HASH_TO_GROUP)
.concat(create_context_string::<CS>(Mode::Oprf));
// Choose a group element that is unlikely to be the right public key
CS::Group::hash_to_curve::<CS>(&[b"msg"], &dst).unwrap()
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst).unwrap()
};
let client_finalize_result = client_blind_result.state.finalize(
input,