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:
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user