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