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