Improvements (#24)
* Remove manual `Debug` impl for `InternalError` * Remove unnecessary `#[macro_use]` * Remove unnecessary `tuple` handling in macro * Improve serialization macro impl * Improve `impl_traits_for` macro * Re-direct `Drop` implementation * Improve macro readability * Fix rustdoc warnings * Rust 1.51 has no support for `rustdoc` lints * Change `i2osp` output to `GenericArray` from `Vec` * Reduce calls to `to_vec()` and simplify conversion * Adding zeroize tests Co-authored-by: Kevin Lewi <[email protected]>
This commit is contained in:
@@ -91,6 +91,14 @@ jobs:
|
|||||||
command: clippy
|
command: clippy
|
||||||
args: --all-targets -- -D warnings
|
args: --all-targets -- -D warnings
|
||||||
|
|
||||||
|
- name: Run cargo doc
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
env:
|
||||||
|
RUSTDOCFLAGS: -D warnings
|
||||||
|
with:
|
||||||
|
command: doc
|
||||||
|
args: --no-deps --document-private-items
|
||||||
|
|
||||||
|
|
||||||
format:
|
format:
|
||||||
name: cargo fmt
|
name: cargo fmt
|
||||||
|
|||||||
+1
-20
@@ -6,14 +6,13 @@
|
|||||||
// of this source tree.
|
// of this source tree.
|
||||||
|
|
||||||
//! A list of error types which are produced during an execution of the protocol
|
//! A list of error types which are produced during an execution of the protocol
|
||||||
use core::fmt::Debug;
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::error::Error;
|
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, Display, Eq, Hash, PartialEq)]
|
#[derive(Clone, Debug, Display, Eq, Hash, PartialEq)]
|
||||||
pub enum InternalError {
|
pub enum InternalError {
|
||||||
/// Could not parse byte sequence for key
|
/// Could not parse byte sequence for key
|
||||||
InvalidByteSequence,
|
InvalidByteSequence,
|
||||||
@@ -38,24 +37,6 @@ pub enum InternalError {
|
|||||||
ZeroScalarError,
|
ZeroScalarError,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for InternalError {
|
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::InvalidByteSequence => f.debug_tuple("InvalidByteSequence").finish(),
|
|
||||||
Self::PointError => f.debug_tuple("PointError").finish(),
|
|
||||||
Self::HashToCurveError => f.debug_tuple("HashToCurveError").finish(),
|
|
||||||
Self::SerializationError => f.debug_tuple("SerializationError").finish(),
|
|
||||||
Self::IncompatibleModeError => f.debug_tuple("IncompatibleModeError").finish(),
|
|
||||||
Self::MismatchedLengthsForCompositeInputs => f
|
|
||||||
.debug_tuple("MismatchedLengthsForCompositeInputs")
|
|
||||||
.finish(),
|
|
||||||
Self::ProofVerificationError => f.debug_tuple("ProofVerificationError").finish(),
|
|
||||||
Self::SizeError => f.debug_tuple("SizeError").finish(),
|
|
||||||
Self::ZeroScalarError => f.debug_tuple("ZeroScalarError").finish(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
||||||
impl Error for InternalError {}
|
impl Error for InternalError {}
|
||||||
|
|||||||
+15
-11
@@ -9,7 +9,7 @@ use crate::errors::InternalError;
|
|||||||
use crate::serialization::i2osp;
|
use crate::serialization::i2osp;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use digest::{BlockInput, Digest};
|
use digest::{BlockInput, Digest};
|
||||||
use generic_array::typenum::Unsigned;
|
use generic_array::typenum::{Unsigned, U1, U2};
|
||||||
|
|
||||||
// Computes ceil(x / y)
|
// Computes ceil(x / y)
|
||||||
fn div_ceil(x: usize, y: usize) -> usize {
|
fn div_ceil(x: usize, y: usize) -> usize {
|
||||||
@@ -32,23 +32,27 @@ pub fn expand_message_xmd<H: BlockInput + Digest>(
|
|||||||
dst: &[u8],
|
dst: &[u8],
|
||||||
len_in_bytes: usize,
|
len_in_bytes: usize,
|
||||||
) -> Result<Vec<u8>, InternalError> {
|
) -> Result<Vec<u8>, InternalError> {
|
||||||
let b_in_bytes = <H as Digest>::OutputSize::USIZE;
|
let ell = div_ceil(len_in_bytes, <H as Digest>::OutputSize::USIZE);
|
||||||
let r_in_bytes = <H as BlockInput>::BlockSize::USIZE;
|
|
||||||
|
|
||||||
let ell = div_ceil(len_in_bytes, b_in_bytes);
|
|
||||||
if ell > 255 {
|
if ell > 255 {
|
||||||
return Err(InternalError::HashToCurveError);
|
return Err(InternalError::HashToCurveError);
|
||||||
}
|
}
|
||||||
let dst_prime = [dst, &i2osp(dst.len(), 1)?].concat();
|
let dst_prime = [dst, &i2osp::<U1>(dst.len())?].concat();
|
||||||
let z_pad = i2osp(0, r_in_bytes)?;
|
let z_pad = i2osp::<<H as BlockInput>::BlockSize>(0)?;
|
||||||
let l_i_b_str = i2osp(len_in_bytes, 2)?;
|
let l_i_b_str = i2osp::<U2>(len_in_bytes)?;
|
||||||
let msg_prime = [&z_pad, msg, &l_i_b_str, &i2osp(0, 1)?, &dst_prime].concat();
|
let msg_prime = [
|
||||||
|
&z_pad,
|
||||||
|
msg,
|
||||||
|
&l_i_b_str,
|
||||||
|
i2osp::<U1>(0)?.as_slice(),
|
||||||
|
&dst_prime,
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
|
||||||
let mut b: Vec<Vec<u8>> = alloc::vec![H::digest(&msg_prime).to_vec()]; // b[0]
|
let mut b: Vec<Vec<u8>> = alloc::vec![H::digest(&msg_prime).to_vec()]; // b[0]
|
||||||
|
|
||||||
let mut h = H::new();
|
let mut h = H::new();
|
||||||
h.update(&b[0]);
|
h.update(&b[0]);
|
||||||
h.update(&i2osp(1, 1)?);
|
h.update(&i2osp::<U1>(1)?);
|
||||||
h.update(&dst_prime);
|
h.update(&dst_prime);
|
||||||
b.push(h.finalize_reset().to_vec()); // b[1]
|
b.push(h.finalize_reset().to_vec()); // b[1]
|
||||||
|
|
||||||
@@ -57,7 +61,7 @@ pub fn expand_message_xmd<H: BlockInput + Digest>(
|
|||||||
|
|
||||||
for i in 2..(ell + 1) {
|
for i in 2..(ell + 1) {
|
||||||
h.update(xor(&b[0], &b[i - 1])?);
|
h.update(xor(&b[0], &b[i - 1])?);
|
||||||
h.update(&i2osp(i, 1)?);
|
h.update(&i2osp::<U1>(i)?);
|
||||||
h.update(&dst_prime);
|
h.update(&dst_prime);
|
||||||
b.push(h.finalize_reset().to_vec()); // b[i]
|
b.push(h.finalize_reset().to_vec()); // b[i]
|
||||||
uniform_bytes.extend_from_slice(&b[i]);
|
uniform_bytes.extend_from_slice(&b[i]);
|
||||||
|
|||||||
+6
-6
@@ -59,10 +59,10 @@ pub trait Group:
|
|||||||
|
|
||||||
/// Return a scalar from its fixed-length bytes representation. If the scalar
|
/// Return a scalar from its fixed-length bytes representation. If the scalar
|
||||||
/// is zero, then return an error.
|
/// is zero, then return an error.
|
||||||
fn from_scalar_slice(
|
fn from_scalar_slice<'a>(
|
||||||
scalar_bits: &GenericArray<u8, Self::ScalarLen>,
|
scalar_bits: impl Into<&'a GenericArray<u8, Self::ScalarLen>>,
|
||||||
) -> Result<Self::Scalar, InternalError> {
|
) -> Result<Self::Scalar, InternalError> {
|
||||||
let scalar = Self::from_scalar_slice_unchecked(scalar_bits)?;
|
let scalar = Self::from_scalar_slice_unchecked(scalar_bits.into())?;
|
||||||
if Self::ct_equal_scalar(&scalar, &Self::scalar_zero()) {
|
if Self::ct_equal_scalar(&scalar, &Self::scalar_zero()) {
|
||||||
return Err(InternalError::ZeroScalarError);
|
return Err(InternalError::ZeroScalarError);
|
||||||
}
|
}
|
||||||
@@ -88,10 +88,10 @@ pub trait Group:
|
|||||||
|
|
||||||
/// Return an element from its fixed-length bytes representation. If the element
|
/// Return an element from its fixed-length bytes representation. If the element
|
||||||
/// is the identity element, return an error.
|
/// is the identity element, return an error.
|
||||||
fn from_element_slice(
|
fn from_element_slice<'a>(
|
||||||
element_bits: &GenericArray<u8, Self::ElemLen>,
|
element_bits: impl Into<&'a GenericArray<u8, Self::ElemLen>>,
|
||||||
) -> Result<Self, InternalError> {
|
) -> Result<Self, InternalError> {
|
||||||
let elem = Self::from_element_slice_unchecked(element_bits)?;
|
let elem = Self::from_element_slice_unchecked(element_bits.into())?;
|
||||||
|
|
||||||
if Self::ct_equal(&elem, &<Self as Group>::identity()) {
|
if Self::ct_equal(&elem, &<Self as Group>::identity()) {
|
||||||
// found the identity element
|
// found the identity element
|
||||||
|
|||||||
+98
-156
@@ -5,139 +5,122 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
macro_rules! impl_debug_eq_hash_for {
|
/// Implement multiple similar traits at the same time. Additionally used to
|
||||||
(struct $name:ident$(<$($gen:ident$(: $bound1:tt$( + $bound2:tt)*)?),+$(,)?>)?, [$field1:ident$(, $field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => {
|
/// find `#[bind]` markers to build `while` constraint.
|
||||||
impl$(<$($gen$(: $bound1$( + $bound2)*)?),+>)? core::fmt::Debug for $name$(<$($gen),+>)?
|
macro_rules! impl_with_bounds {
|
||||||
$(where $($type: core::fmt::Debug,)+)?
|
(
|
||||||
|
$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,)?)+
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
$($fn1)?
|
||||||
f.debug_struct("$name")
|
|
||||||
.field("$field1", &self.$field1)
|
|
||||||
$(.field("$field2", &self.$field2))*
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl$(<$($gen$(: $bound1$( + $bound2)*)?),+>)? Eq for $name$(<$($gen),+>)?
|
impl_with_bounds!(
|
||||||
$(where $($type: Eq,)+)?
|
$name$(<$($gen$(: $bound1 $(+ $bound2)*)?),+>)?
|
||||||
{}
|
$(|$(@#bind: $type|,)? $(@#pd: $_1|,)? $(@$_2|,)?)+
|
||||||
|
$($trait2 => { $($fn2)? },)*
|
||||||
impl$(<$($gen$(: $bound1$( + $bound2)*)?),+>)? PartialEq for $name$(<$($gen),+>)?
|
);
|
||||||
$(where $($type: PartialEq,)+)?
|
|
||||||
{
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
PartialEq::eq(&self.$field1, &other.$field1)
|
|
||||||
$(&& PartialEq::eq(&self.$field2, &other.$field2))*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl$(<$($gen$(: $bound1$( + $bound2)*)?),+>)? core::hash::Hash for $name$(<$($gen),+>)?
|
|
||||||
$(where $($type: 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);)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(tuple $name:ident$(<$($gen:ident$(: $bound1:tt$( + $bound2:tt)*)?),+$(,)?>)?, [$field1:tt$(, $field2:tt)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => {
|
|
||||||
impl$(<$($gen$(: $bound1$(+ $bound2)*)?),+>)? core::fmt::Debug for $name$(<$($gen),+>)?
|
|
||||||
$(where $($type: core::fmt::Debug,)+)?
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
||||||
f.debug_tuple("$name")
|
|
||||||
.field(&self.$field1)
|
|
||||||
$(.field(&self.$field2))*
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl$(<$($gen$(: $bound1$(+ $bound2)*)?),+>)? Eq for $name$(<$($gen),+>)?
|
|
||||||
$(where $($type: Eq,)+)?
|
|
||||||
{}
|
|
||||||
|
|
||||||
impl$(<$($gen$(: $bound1$(+ $bound2)*)?),+>)? PartialEq for $name$(<$($gen),+>)?
|
|
||||||
$(where $($type: PartialEq,)+)?
|
|
||||||
{
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
PartialEq::eq(&self.$field1, &other.$field1)
|
|
||||||
$(&& PartialEq::eq(&self.$field2, &other.$field2))*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl$(<$($gen$(: $bound1$(+ $bound2)*)?),+>)? core::hash::Hash for $name$(<$($gen),+>)?
|
|
||||||
$(where $($type: 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);)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
// signature triggered when all traits are exhausted
|
||||||
|
(
|
||||||
|
$name:ident$(<$($gen:ident$(: $bound1:tt$( + $bound2:tt)*)?),+>)?
|
||||||
|
$(|$(@#bind: $type:ty|,)? $(@#pd: $_1:ty|,)? $(@$_2:ty|,)?)+
|
||||||
|
) => { };
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_clone_for {
|
/// Skips attempt to call [`zeroize()`](zeroize::Zeroize::zeroize) on
|
||||||
(struct $name:ident$(<$($gen:ident$(: $bound1:tt$( + $bound2:tt)*)?),+$(,)?>)?, [$field1:ident$(, $field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => {
|
/// [`PhantomData`](core::marker::PhantomData).
|
||||||
impl$(<$($gen$(: $bound1$(+ $bound2)*)?),+>)? Clone for $name$(<$($gen),+>)?
|
macro_rules! impl_internal_zeroize {
|
||||||
$(where $($type: Clone,)+)?
|
($self_:ident, #pd $field:ident) => {};
|
||||||
{
|
($self_:ident, #bind $field:ident) => {
|
||||||
fn clone(&self) -> Self {
|
$self_.$field.zeroize();
|
||||||
Self {
|
|
||||||
$field1: self.$field1.clone(),
|
|
||||||
$($field2: self.$field2.clone(),)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
(tuple $name:ident$(<$($gen:ident$(: $bound1:tt$( + $bound2:tt)*)?),+$(,)?>)?, [$field1:tt$(, $field2:tt)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => {
|
|
||||||
impl$(<$($gen$(: $bound1$(+ $bound2)*)?),+>)? Clone for $name$(<$($gen),+>)?
|
|
||||||
$(where $($type: Clone,)+)?
|
|
||||||
{
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self(
|
|
||||||
self.$field1.clone(),
|
|
||||||
$(self.$field2.clone(),)*
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_zeroize_field_skip_pd {
|
|
||||||
($self_:ident, $field:ident, PH) => {};
|
|
||||||
($self_:ident, $field:ident) => {
|
($self_:ident, $field:ident) => {
|
||||||
$self_.$field.zeroize();
|
$self_.$field.zeroize();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_zeroize_on_drop_for {
|
macro_rules! impl_traits_for {
|
||||||
(struct $name:ident$(<$($gen:ident$(: $bound1:tt$( + $bound2:tt)*)?),+$(,)?>)?, [$(#[$pd1:ident] )?$field1:ident$(, $(#[$pd2:ident] )?$field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => {
|
(
|
||||||
impl$(<$($gen$(: $bound1$(+ $bound2)*)?),+>)? zeroize::Zeroize for $name$(<$($gen),+>)?
|
// 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) {
|
fn zeroize(&mut self) {
|
||||||
impl_zeroize_field_skip_pd!(self, $field1$(, $pd1)?);
|
impl_internal_zeroize!(self, $(#$attr1)? $field1);
|
||||||
$(impl_zeroize_field_skip_pd!(self, $field2$(, $pd2)?);)*
|
$(impl_internal_zeroize!(self, $(#$attr2)? $field2);)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl$(<$($gen$(: $bound1$(+ $bound2)*)?),+>)? Drop for $name$(<$($gen),+>)?
|
impl$(<$($gen$(: $bound1 $(+ $bound2)*)?),+>)? Drop for $name$(<$($gen),+>)?
|
||||||
{
|
{
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
#[allow(unused_imports)]
|
zeroize::Zeroize::zeroize(self);
|
||||||
use zeroize::Zeroize;
|
|
||||||
impl_zeroize_field_skip_pd!(self, $field1$(, $pd1)?);
|
|
||||||
$(impl_zeroize_field_skip_pd!(self, $field2$(, $pd2)?);)*
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inner macro used for deriving `serde`'s `Serialize` and `Deserialize` traits.
|
|
||||||
macro_rules! impl_serialize_and_deserialize_for {
|
|
||||||
($name:ident$(<$($gen:ident$(: $bound1:tt$( + $bound2:tt)*)?),+$(,)?>)?) => {
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||||
impl$(<$($gen$(: $bound1$(+ $bound2)*)?),+>)? serde::Serialize for $name$(<$($gen),+>)? {
|
impl$(<$($gen$(: $bound1 $(+ $bound2)*)?),+>)? serde::Serialize for $name$(<$($gen),+>)? {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: serde::Serializer,
|
S: serde::Serializer,
|
||||||
@@ -152,62 +135,21 @@ macro_rules! impl_serialize_and_deserialize_for {
|
|||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||||
impl<'de$(, $($gen$(: $bound1$(+ $bound2)*)?),+)?> serde::Deserialize<'de> for $name$(<$($gen),+>)? {
|
impl<'de, $($($gen$(: $bound1 $(+ $bound2)*)?),+)?> serde::Deserialize<'de> for $name$(<$($gen),+>)? {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
D: serde::Deserializer<'de>,
|
D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
|
use serde::de::Error;
|
||||||
|
|
||||||
if deserializer.is_human_readable() {
|
if deserializer.is_human_readable() {
|
||||||
let s = <&str>::deserialize(deserializer)?;
|
let s = <&str>::deserialize(deserializer)?;
|
||||||
$name$(::<$($gen),+>)?::deserialize(&base64::decode(s).map_err(serde::de::Error::custom)?)
|
Self::deserialize(&base64::decode(s).map_err(Error::custom)?)
|
||||||
.map_err(serde::de::Error::custom)
|
|
||||||
} else {
|
} else {
|
||||||
struct ByteVisitor$(<$($gen$(: $bound1$(+ $bound2)*)?),+> (
|
Self::deserialize(<&[u8]>::deserialize(deserializer)?)
|
||||||
#[allow(unused_parens)]
|
|
||||||
core::marker::PhantomData<($($gen),+)>,
|
|
||||||
))?;
|
|
||||||
impl<'de$(, $($gen$(: $bound1$(+ $bound2)*)?),+)?> serde::de::Visitor<'de> for ByteVisitor$(<$($gen),+>)? {
|
|
||||||
type Value = $name$(<$($gen),+>)?;
|
|
||||||
fn expecting(
|
|
||||||
&self,
|
|
||||||
formatter: &mut core::fmt::Formatter,
|
|
||||||
) -> core::fmt::Result {
|
|
||||||
formatter.write_str(core::concat!(
|
|
||||||
"the byte representation of a ",
|
|
||||||
core::stringify!($name)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
$name$(::<$($gen),+>)?::deserialize(value).map_err(|_| {
|
|
||||||
serde::de::Error::invalid_value(
|
|
||||||
serde::de::Unexpected::Bytes(value),
|
|
||||||
&core::concat!(
|
|
||||||
"invalid byte sequence for ",
|
|
||||||
core::stringify!($name)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
deserializer.deserialize_bytes(ByteVisitor$(::<$($gen),+> (
|
|
||||||
core::marker::PhantomData,
|
|
||||||
))?)
|
|
||||||
}
|
}
|
||||||
|
.map_err(Error::custom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convenience macro for implementing all of the above traits
|
|
||||||
macro_rules! impl_traits_for {
|
|
||||||
(struct $name:ident$(<$($gen:ident$(: $bound1:tt$( + $bound2:tt)*)?),+$(,)?>)?, [$(#[$pd1:ident] )?$field1:ident$(, $(#[$pd2:ident] )?$field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => {
|
|
||||||
impl_debug_eq_hash_for!(struct $name$(<$($gen$(: $bound1$(+ $bound2)*)?),+>)?, [$field1$(, $field2)*], $([$($type),+])?);
|
|
||||||
impl_clone_for!(struct $name$(<$($gen$(: $bound1$(+ $bound2)*)?),+>)?, [$field1$(, $field2)*], $([$($type),+])?);
|
|
||||||
impl_zeroize_on_drop_for!(struct $name$(<$($gen$(: $bound1$(+ $bound2)*)?),+>)?, [$(#[$pd1] )?$field1$(, $(#[$pd2] )?$field2)*], $([$($type),+])?);
|
|
||||||
impl_serialize_and_deserialize_for!($name$(<$($gen$(: $bound1$(+ $bound2)*)?),+>)?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+3
-8
@@ -117,9 +117,7 @@
|
|||||||
//!
|
//!
|
||||||
//! In the final step, the client takes as input the message from
|
//! In the final step, the client takes as input the message from
|
||||||
//! [NonVerifiableServer::evaluate] (an [EvaluationElement]), and runs
|
//! [NonVerifiableServer::evaluate] (an [EvaluationElement]), and runs
|
||||||
//! [NonVerifiableClient::finalize] to produce a
|
//! [NonVerifiableClient::finalize] to produce an output for the protocol.
|
||||||
//! [NonVerifiableClientFinalizeResult], which consists of an
|
|
||||||
//! output for the protocol.
|
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||||
@@ -243,9 +241,7 @@
|
|||||||
//! In the final step, the client takes as input the message from
|
//! In the final step, the client takes as input the message from
|
||||||
//! [VerifiableServer::evaluate] (an [EvaluationElement]),
|
//! [VerifiableServer::evaluate] (an [EvaluationElement]),
|
||||||
//! the proof, and the server's public key, and runs
|
//! the proof, and the server's public key, and runs
|
||||||
//! [VerifiableClient::finalize] to produce a
|
//! [VerifiableClient::finalize] to produce an output for the protocol.
|
||||||
//! [VerifiableClientFinalizeResult], which consists of an
|
|
||||||
//! output for the protocol.
|
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||||
@@ -435,10 +431,9 @@ extern crate alloc;
|
|||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod impls;
|
mod impls;
|
||||||
#[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)]
|
||||||
|
|||||||
+37
-41
@@ -19,7 +19,7 @@ use crate::{
|
|||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use digest::{BlockInput, Digest};
|
use digest::{BlockInput, Digest};
|
||||||
use generic_array::{typenum::Unsigned, GenericArray};
|
use generic_array::{typenum::Unsigned, ArrayLength, GenericArray};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////
|
||||||
// Serialization and Deserialization for High-Level API //
|
// Serialization and Deserialization for High-Level API //
|
||||||
@@ -29,7 +29,7 @@ use generic_array::{typenum::Unsigned, GenericArray};
|
|||||||
impl<G: Group, H: BlockInput + Digest> NonVerifiableClient<G, H> {
|
impl<G: Group, H: BlockInput + Digest> NonVerifiableClient<G, H> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
[G::scalar_as_bytes(self.blind).to_vec(), self.data.clone()].concat()
|
[G::scalar_as_bytes(self.blind).as_slice(), &self.data].concat()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialization from bytes
|
/// Deserialization from bytes
|
||||||
@@ -39,7 +39,7 @@ impl<G: Group, H: BlockInput + Digest> NonVerifiableClient<G, H> {
|
|||||||
return Err(InternalError::SizeError);
|
return Err(InternalError::SizeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
let blind = G::from_scalar_slice(GenericArray::from_slice(&input[..scalar_len]))?;
|
let blind = G::from_scalar_slice(&input[..scalar_len])?;
|
||||||
let data = input[scalar_len..].to_vec();
|
let data = input[scalar_len..].to_vec();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@@ -54,9 +54,9 @@ impl<G: Group, H: BlockInput + Digest> VerifiableClient<G, H> {
|
|||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
[
|
[
|
||||||
G::scalar_as_bytes(self.blind).to_vec(),
|
G::scalar_as_bytes(self.blind).as_slice(),
|
||||||
self.blinded_element.to_arr().to_vec(),
|
&self.blinded_element.to_arr(),
|
||||||
self.data.clone(),
|
&self.data,
|
||||||
]
|
]
|
||||||
.concat()
|
.concat()
|
||||||
}
|
}
|
||||||
@@ -69,10 +69,8 @@ impl<G: Group, H: BlockInput + Digest> VerifiableClient<G, H> {
|
|||||||
return Err(InternalError::SizeError);
|
return Err(InternalError::SizeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
let blind = G::from_scalar_slice(GenericArray::from_slice(&input[..scalar_len]))?;
|
let blind = G::from_scalar_slice(&input[..scalar_len])?;
|
||||||
let blinded_element = G::from_element_slice(GenericArray::from_slice(
|
let blinded_element = G::from_element_slice(&input[scalar_len..scalar_len + elem_len])?;
|
||||||
&input[scalar_len..scalar_len + elem_len],
|
|
||||||
))?;
|
|
||||||
let data = input[scalar_len + elem_len..].to_vec();
|
let data = input[scalar_len + elem_len..].to_vec();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@@ -97,7 +95,7 @@ impl<G: Group, H: BlockInput + Digest> NonVerifiableServer<G, H> {
|
|||||||
return Err(InternalError::SizeError);
|
return Err(InternalError::SizeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
let sk = G::from_scalar_slice(GenericArray::from_slice(input))?;
|
let sk = G::from_scalar_slice(input)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
sk,
|
sk,
|
||||||
@@ -109,11 +107,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> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
[
|
[G::scalar_as_bytes(self.sk).as_slice(), &self.pk.to_arr()].concat()
|
||||||
G::scalar_as_bytes(self.sk).to_vec(),
|
|
||||||
self.pk.to_arr().to_vec(),
|
|
||||||
]
|
|
||||||
.concat()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialization from bytes
|
/// Deserialization from bytes
|
||||||
@@ -124,8 +118,8 @@ impl<G: Group, H: BlockInput + Digest> VerifiableServer<G, H> {
|
|||||||
return Err(InternalError::SizeError);
|
return Err(InternalError::SizeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
let sk = G::from_scalar_slice(GenericArray::from_slice(&input[..scalar_len]))?;
|
let sk = G::from_scalar_slice(&input[..scalar_len])?;
|
||||||
let pk = G::from_element_slice(GenericArray::from_slice(&input[scalar_len..]))?;
|
let pk = G::from_element_slice(&input[scalar_len..])?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
sk,
|
sk,
|
||||||
@@ -152,8 +146,8 @@ impl<G: Group, H: BlockInput + Digest> Proof<G, H> {
|
|||||||
return Err(InternalError::SizeError);
|
return Err(InternalError::SizeError);
|
||||||
}
|
}
|
||||||
Ok(Proof {
|
Ok(Proof {
|
||||||
c_scalar: G::from_scalar_slice(GenericArray::from_slice(&input[..scalar_len]))?,
|
c_scalar: G::from_scalar_slice(&input[..scalar_len])?,
|
||||||
s_scalar: G::from_scalar_slice(GenericArray::from_slice(&input[scalar_len..]))?,
|
s_scalar: G::from_scalar_slice(&input[scalar_len..])?,
|
||||||
hash: PhantomData,
|
hash: PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -168,7 +162,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> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
value: G::from_element_slice(GenericArray::from_slice(input))?,
|
value: G::from_element_slice(input)?,
|
||||||
hash: PhantomData,
|
hash: PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -183,7 +177,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> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
value: G::from_element_slice(GenericArray::from_slice(input))?,
|
value: G::from_element_slice(input)?,
|
||||||
hash: PhantomData,
|
hash: PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -195,46 +189,48 @@ impl<G: Group, H: BlockInput + Digest> EvaluationElement<G, H> {
|
|||||||
//////////////////////
|
//////////////////////
|
||||||
|
|
||||||
// Corresponds to the I2OSP() function from RFC8017
|
// Corresponds to the I2OSP() function from RFC8017
|
||||||
pub(crate) fn i2osp(input: usize, length: usize) -> Result<alloc::vec::Vec<u8>, InternalError> {
|
pub(crate) fn i2osp<L: ArrayLength<u8>>(
|
||||||
let sizeof_usize = core::mem::size_of::<usize>();
|
input: usize,
|
||||||
|
) -> Result<GenericArray<u8, L>, InternalError> {
|
||||||
|
const SIZEOF_USIZE: usize = core::mem::size_of::<usize>();
|
||||||
|
|
||||||
// Check if input >= 256^length
|
// Check if input >= 256^length
|
||||||
if (sizeof_usize as u32 - input.leading_zeros() / 8) > length as u32 {
|
if (SIZEOF_USIZE as u32 - input.leading_zeros() / 8) > L::U32 {
|
||||||
return Err(InternalError::SerializationError);
|
return Err(InternalError::SerializationError);
|
||||||
}
|
}
|
||||||
|
|
||||||
if length <= sizeof_usize {
|
if L::USIZE <= SIZEOF_USIZE {
|
||||||
return Ok((&input.to_be_bytes()[sizeof_usize - length..]).to_vec());
|
return Ok(GenericArray::clone_from_slice(
|
||||||
|
&input.to_be_bytes()[SIZEOF_USIZE - L::USIZE..],
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut output = alloc::vec![0u8; length];
|
let mut output = GenericArray::default();
|
||||||
output.splice(
|
output[L::USIZE - SIZEOF_USIZE..L::USIZE].copy_from_slice(&input.to_be_bytes());
|
||||||
length - sizeof_usize..length,
|
|
||||||
input.to_be_bytes().iter().cloned(),
|
|
||||||
);
|
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computes I2OSP(len(input), max_bytes) || input
|
// Computes I2OSP(len(input), max_bytes) || input
|
||||||
pub(crate) fn serialize(input: &[u8], max_bytes: usize) -> Result<Vec<u8>, InternalError> {
|
pub(crate) fn serialize<L: ArrayLength<u8>>(input: &[u8]) -> Result<Vec<u8>, InternalError> {
|
||||||
Ok([&i2osp(input.len(), max_bytes)?, input].concat())
|
Ok([&i2osp::<L>(input.len())?, input].concat())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod unit_tests {
|
mod unit_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use generic_array::typenum::{U1, U2};
|
||||||
|
|
||||||
// Test the error condition for I2OSP
|
// Test the error condition for I2OSP
|
||||||
#[test]
|
#[test]
|
||||||
fn test_i2osp_err_check() {
|
fn test_i2osp_err_check() {
|
||||||
assert!(i2osp(0, 1).is_ok());
|
assert!(i2osp::<U1>(0).is_ok());
|
||||||
|
|
||||||
assert!(i2osp(255, 1).is_ok());
|
assert!(i2osp::<U1>(255).is_ok());
|
||||||
assert!(i2osp(256, 1).is_err());
|
assert!(i2osp::<U1>(256).is_err());
|
||||||
assert!(i2osp(257, 1).is_err());
|
assert!(i2osp::<U1>(257).is_err());
|
||||||
|
|
||||||
assert!(i2osp(256 * 256 - 1, 2).is_ok());
|
assert!(i2osp::<U2>(256 * 256 - 1).is_ok());
|
||||||
assert!(i2osp(256 * 256, 2).is_err());
|
assert!(i2osp::<U2>(256 * 256).is_err());
|
||||||
assert!(i2osp(256 * 256 + 1, 2).is_err());
|
assert!(i2osp::<U2>(256 * 256 + 1).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+274
-110
@@ -16,7 +16,10 @@ use alloc::vec;
|
|||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use digest::{BlockInput, Digest};
|
use digest::{BlockInput, Digest};
|
||||||
use generic_array::{typenum::Unsigned, GenericArray};
|
use generic_array::{
|
||||||
|
typenum::{Unsigned, U1, U2},
|
||||||
|
GenericArray,
|
||||||
|
};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
|
|
||||||
///////////////
|
///////////////
|
||||||
@@ -46,99 +49,94 @@ enum Mode {
|
|||||||
// ====================== //
|
// ====================== //
|
||||||
////////////////////////////
|
////////////////////////////
|
||||||
|
|
||||||
/// A client which engages with a [NonVerifiableServer]
|
impl_traits_for! {
|
||||||
/// in base mode, meaning that the OPRF outputs are not
|
/// A client which engages with a [NonVerifiableServer]
|
||||||
/// verifiable.
|
/// in base mode, meaning that the OPRF outputs are not
|
||||||
pub struct NonVerifiableClient<G: Group, H: BlockInput + Digest> {
|
/// verifiable.
|
||||||
pub(crate) blind: <G as Group>::Scalar,
|
pub struct NonVerifiableClient<G: Group, H: BlockInput + Digest> {
|
||||||
pub(crate) data: Vec<u8>,
|
#[bind]
|
||||||
pub(crate) hash: PhantomData<H>,
|
pub(crate) blind: <G as Group>::Scalar,
|
||||||
|
pub(crate) data: Vec<u8>,
|
||||||
|
#[pd]
|
||||||
|
pub(crate) hash: PhantomData<H>,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl_traits_for!(
|
|
||||||
struct NonVerifiableClient<G: Group, H: BlockInput + Digest>,
|
|
||||||
[blind, data, #[PH] hash],
|
|
||||||
[<G as Group>::Scalar],
|
|
||||||
);
|
|
||||||
|
|
||||||
/// A client which engages with a [VerifiableServer]
|
impl_traits_for! {
|
||||||
/// 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.
|
||||||
pub(crate) blind: <G as Group>::Scalar,
|
pub struct VerifiableClient<G: Group, H: BlockInput + Digest> {
|
||||||
pub(crate) blinded_element: G,
|
#[bind]
|
||||||
pub(crate) data: alloc::vec::Vec<u8>,
|
pub(crate) blind: <G as Group>::Scalar,
|
||||||
pub(crate) hash: PhantomData<H>,
|
#[bind]
|
||||||
|
pub(crate) blinded_element: G,
|
||||||
|
pub(crate) data: alloc::vec::Vec<u8>,
|
||||||
|
#[pd]
|
||||||
|
pub(crate) hash: PhantomData<H>,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl_traits_for!(
|
|
||||||
struct VerifiableClient<G: Group, H: BlockInput + Digest>,
|
|
||||||
[blind, blinded_element, data, #[PH] hash],
|
|
||||||
[<G as Group>::Scalar, G],
|
|
||||||
);
|
|
||||||
|
|
||||||
/// A server which engages with a [NonVerifiableClient]
|
impl_traits_for! {
|
||||||
/// 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.
|
||||||
pub(crate) sk: <G as Group>::Scalar,
|
pub struct NonVerifiableServer<G: Group, H: BlockInput + Digest> {
|
||||||
pub(crate) hash: PhantomData<H>,
|
#[bind]
|
||||||
|
pub(crate) sk: <G as Group>::Scalar,
|
||||||
|
#[pd]
|
||||||
|
pub(crate) hash: PhantomData<H>,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl_traits_for!(
|
|
||||||
struct NonVerifiableServer<G: Group, H: BlockInput + Digest>,
|
|
||||||
[sk, #[PH] hash],
|
|
||||||
[<G as Group>::Scalar],
|
|
||||||
);
|
|
||||||
|
|
||||||
/// A server which engages with a [VerifiableClient]
|
impl_traits_for! {
|
||||||
/// 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.
|
||||||
pub(crate) sk: <G as Group>::Scalar,
|
pub struct VerifiableServer<G: Group, H: BlockInput + Digest> {
|
||||||
pub(crate) pk: G,
|
#[bind]
|
||||||
pub(crate) hash: PhantomData<H>,
|
pub(crate) sk: <G as Group>::Scalar,
|
||||||
|
#[bind]
|
||||||
|
pub(crate) pk: G,
|
||||||
|
#[pd]
|
||||||
|
pub(crate) hash: PhantomData<H>,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl_traits_for!(
|
|
||||||
struct VerifiableServer<G: Group, H: BlockInput + Digest>,
|
|
||||||
[sk, pk, #[PH] hash],
|
|
||||||
[<G as Group>::Scalar, G],
|
|
||||||
);
|
|
||||||
|
|
||||||
/// A proof produced by a [VerifiableServer] that
|
impl_traits_for! {
|
||||||
/// 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.
|
||||||
pub(crate) c_scalar: <G as Group>::Scalar,
|
pub struct Proof<G: Group, H: BlockInput + Digest> {
|
||||||
pub(crate) s_scalar: <G as Group>::Scalar,
|
#[bind]
|
||||||
pub(crate) hash: PhantomData<H>,
|
pub(crate) c_scalar: <G as Group>::Scalar,
|
||||||
|
pub(crate) s_scalar: <G as Group>::Scalar,
|
||||||
|
#[pd]
|
||||||
|
pub(crate) hash: PhantomData<H>,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl_traits_for!(
|
|
||||||
struct Proof<G: Group, H: BlockInput + Digest>,
|
|
||||||
[c_scalar, s_scalar, #[PH] hash],
|
|
||||||
[<G as Group>::Scalar],
|
|
||||||
);
|
|
||||||
|
|
||||||
/// The first client message sent from a client (either verifiable or not)
|
impl_traits_for! {
|
||||||
/// 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).
|
||||||
pub(crate) value: G,
|
pub struct BlindedElement<G: Group, H: BlockInput + Digest> {
|
||||||
pub(crate) hash: PhantomData<H>,
|
#[bind]
|
||||||
|
pub(crate) value: G,
|
||||||
|
#[pd]
|
||||||
|
pub(crate) hash: PhantomData<H>,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl_traits_for!(
|
|
||||||
struct BlindedElement<G: Group, H: BlockInput + Digest>,
|
|
||||||
[value, #[PH] hash],
|
|
||||||
[G],
|
|
||||||
);
|
|
||||||
|
|
||||||
/// The server's response to the [BlindedElement] message from
|
impl_traits_for! {
|
||||||
/// 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).
|
||||||
pub(crate) value: G,
|
pub struct EvaluationElement<G: Group, H: BlockInput + Digest> {
|
||||||
pub(crate) hash: PhantomData<H>,
|
#[bind]
|
||||||
|
pub(crate) value: G,
|
||||||
|
#[pd]
|
||||||
|
pub(crate) hash: PhantomData<H>,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl_traits_for!(
|
|
||||||
struct EvaluationElement<G: Group, H: BlockInput + Digest>,
|
|
||||||
[value, #[PH] hash],
|
|
||||||
[G],
|
|
||||||
);
|
|
||||||
|
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
// API Implementations //
|
// API Implementations //
|
||||||
@@ -197,6 +195,15 @@ impl<G: Group, H: BlockInput + Digest> NonVerifiableClient<G, H> {
|
|||||||
pub fn get_blind(&self) -> <G as Group>::Scalar {
|
pub fn get_blind(&self) -> <G as Group>::Scalar {
|
||||||
self.blind
|
self.blind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
/// Only used for testing zeroize
|
||||||
|
pub fn as_ptrs(&self) -> Vec<Vec<u8>> {
|
||||||
|
vec![
|
||||||
|
self.data.clone(),
|
||||||
|
<G as Group>::scalar_as_bytes(self.blind).to_vec(),
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G: Group, H: BlockInput + Digest> VerifiableClient<G, H> {
|
impl<G: Group, H: BlockInput + Digest> VerifiableClient<G, H> {
|
||||||
@@ -294,6 +301,16 @@ impl<G: Group, H: BlockInput + Digest> VerifiableClient<G, H> {
|
|||||||
pub fn get_blind(&self) -> <G as Group>::Scalar {
|
pub fn get_blind(&self) -> <G as Group>::Scalar {
|
||||||
self.blind
|
self.blind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
/// Only used for testing zeroize
|
||||||
|
pub fn as_ptrs(&self) -> Vec<Vec<u8>> {
|
||||||
|
vec![
|
||||||
|
self.data.clone(),
|
||||||
|
<G as Group>::scalar_as_bytes(self.blind).to_vec(),
|
||||||
|
self.blinded_element.to_arr().to_vec(),
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G: Group, H: BlockInput + Digest> NonVerifiableServer<G, H> {
|
impl<G: Group, H: BlockInput + Digest> NonVerifiableServer<G, H> {
|
||||||
@@ -343,7 +360,7 @@ impl<G: Group, H: BlockInput + Digest> NonVerifiableServer<G, H> {
|
|||||||
let context = [
|
let context = [
|
||||||
STR_CONTEXT,
|
STR_CONTEXT,
|
||||||
&get_context_string::<G>(Mode::Base)?,
|
&get_context_string::<G>(Mode::Base)?,
|
||||||
&serialize(&metadata.0, 2)?,
|
&serialize::<U2>(&metadata.0)?,
|
||||||
]
|
]
|
||||||
.concat();
|
.concat();
|
||||||
let dst = [STR_HASH_TO_SCALAR, &get_context_string::<G>(Mode::Base)?].concat();
|
let dst = [STR_HASH_TO_SCALAR, &get_context_string::<G>(Mode::Base)?].concat();
|
||||||
@@ -357,6 +374,12 @@ impl<G: Group, H: BlockInput + Digest> NonVerifiableServer<G, H> {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
/// Only used for testing zeroize
|
||||||
|
pub fn as_ptrs(&self) -> Vec<Vec<u8>> {
|
||||||
|
vec![<G as Group>::scalar_as_bytes(self.sk).to_vec()]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G: Group, H: BlockInput + Digest> VerifiableServer<G, H> {
|
impl<G: Group, H: BlockInput + Digest> VerifiableServer<G, H> {
|
||||||
@@ -429,7 +452,7 @@ impl<G: Group, H: BlockInput + Digest> VerifiableServer<G, H> {
|
|||||||
let context = [
|
let context = [
|
||||||
STR_CONTEXT,
|
STR_CONTEXT,
|
||||||
&get_context_string::<G>(Mode::Verifiable)?,
|
&get_context_string::<G>(Mode::Verifiable)?,
|
||||||
&serialize(&metadata.0, 2)?,
|
&serialize::<U2>(&metadata.0)?,
|
||||||
]
|
]
|
||||||
.concat();
|
.concat();
|
||||||
let dst = [
|
let dst = [
|
||||||
@@ -462,6 +485,15 @@ impl<G: Group, H: BlockInput + Digest> VerifiableServer<G, H> {
|
|||||||
pub fn get_public_key(&self) -> G {
|
pub fn get_public_key(&self) -> G {
|
||||||
self.pk
|
self.pk
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
/// Only used for testing zeroize
|
||||||
|
pub fn as_ptrs(&self) -> Vec<Vec<u8>> {
|
||||||
|
vec![
|
||||||
|
<G as Group>::scalar_as_bytes(self.sk).to_vec(),
|
||||||
|
self.pk.to_arr().to_vec(),
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
@@ -553,6 +585,35 @@ struct BatchItems<G: Group, H: BlockInput + Digest> {
|
|||||||
blinded_element: BlindedElement<G, H>,
|
blinded_element: BlindedElement<G, H>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience test functions for [BlindedElement], [EvaluationElement], and [Proof]
|
||||||
|
|
||||||
|
impl<G: Group, H: BlockInput + Digest> BlindedElement<G, H> {
|
||||||
|
#[cfg(test)]
|
||||||
|
/// Only used for testing zeroize
|
||||||
|
pub fn as_ptrs(&self) -> Vec<Vec<u8>> {
|
||||||
|
vec![self.value.to_arr().to_vec()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group, H: BlockInput + Digest> EvaluationElement<G, H> {
|
||||||
|
#[cfg(test)]
|
||||||
|
/// Only used for testing zeroize
|
||||||
|
pub fn as_ptrs(&self) -> Vec<Vec<u8>> {
|
||||||
|
vec![self.value.to_arr().to_vec()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group, H: BlockInput + Digest> Proof<G, H> {
|
||||||
|
#[cfg(test)]
|
||||||
|
/// Only used for testing zeroize
|
||||||
|
pub fn as_ptrs(&self) -> Vec<Vec<u8>> {
|
||||||
|
vec![
|
||||||
|
<G as Group>::scalar_as_bytes(self.c_scalar).to_vec(),
|
||||||
|
<G as Group>::scalar_as_bytes(self.s_scalar).to_vec(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Inner function for blind. Returns the blind scalar and the blinded element
|
// Inner function for blind. Returns the blind scalar and the blinded element
|
||||||
fn blind<G: Group, H: BlockInput + Digest, R: RngCore + CryptoRng>(
|
fn blind<G: Group, H: BlockInput + Digest, R: RngCore + CryptoRng>(
|
||||||
input: &[u8],
|
input: &[u8],
|
||||||
@@ -576,7 +637,7 @@ fn verifiable_unblind<G: Group, H: BlockInput + Digest>(
|
|||||||
let context = [
|
let context = [
|
||||||
STR_CONTEXT,
|
STR_CONTEXT,
|
||||||
&get_context_string::<G>(Mode::Verifiable)?,
|
&get_context_string::<G>(Mode::Verifiable)?,
|
||||||
&serialize(info, 2)?,
|
&serialize::<U2>(info)?,
|
||||||
]
|
]
|
||||||
.concat();
|
.concat();
|
||||||
|
|
||||||
@@ -628,12 +689,12 @@ fn generate_proof<G: Group, H: BlockInput + Digest, R: RngCore + CryptoRng>(
|
|||||||
|
|
||||||
let challenge_dst = [STR_CHALLENGE, &get_context_string::<G>(Mode::Verifiable)?].concat();
|
let challenge_dst = [STR_CHALLENGE, &get_context_string::<G>(Mode::Verifiable)?].concat();
|
||||||
let h2_input = [
|
let h2_input = [
|
||||||
serialize(&b.to_arr().to_vec(), 2)?,
|
serialize::<U2>(&b.to_arr().to_vec())?,
|
||||||
serialize(&m.to_arr().to_vec(), 2)?,
|
serialize::<U2>(&m.to_arr().to_vec())?,
|
||||||
serialize(&z.to_arr().to_vec(), 2)?,
|
serialize::<U2>(&z.to_arr().to_vec())?,
|
||||||
serialize(&t2.to_arr().to_vec(), 2)?,
|
serialize::<U2>(&t2.to_arr().to_vec())?,
|
||||||
serialize(&t3.to_arr().to_vec(), 2)?,
|
serialize::<U2>(&t3.to_arr().to_vec())?,
|
||||||
serialize(&challenge_dst, 2)?,
|
serialize::<U2>(&challenge_dst)?,
|
||||||
]
|
]
|
||||||
.concat();
|
.concat();
|
||||||
|
|
||||||
@@ -667,12 +728,12 @@ fn verify_proof<G: Group, H: BlockInput + Digest>(
|
|||||||
|
|
||||||
let challenge_dst = [STR_CHALLENGE, &get_context_string::<G>(Mode::Verifiable)?].concat();
|
let challenge_dst = [STR_CHALLENGE, &get_context_string::<G>(Mode::Verifiable)?].concat();
|
||||||
let h2_input = [
|
let h2_input = [
|
||||||
serialize(&b.to_arr().to_vec(), 2)?,
|
serialize::<U2>(&b.to_arr().to_vec())?,
|
||||||
serialize(&m.to_arr().to_vec(), 2)?,
|
serialize::<U2>(&m.to_arr().to_vec())?,
|
||||||
serialize(&z.to_arr().to_vec(), 2)?,
|
serialize::<U2>(&z.to_arr().to_vec())?,
|
||||||
serialize(&t2.to_arr().to_vec(), 2)?,
|
serialize::<U2>(&t2.to_arr().to_vec())?,
|
||||||
serialize(&t3.to_arr().to_vec(), 2)?,
|
serialize::<U2>(&t3.to_arr().to_vec())?,
|
||||||
serialize(&challenge_dst, 2)?,
|
serialize::<U2>(&challenge_dst)?,
|
||||||
]
|
]
|
||||||
.concat();
|
.concat();
|
||||||
|
|
||||||
@@ -702,10 +763,10 @@ fn finalize_after_unblind<G: Group, H: BlockInput + Digest>(
|
|||||||
for (input, unblinded_element) in inputs_and_unblinded_elements {
|
for (input, unblinded_element) in inputs_and_unblinded_elements {
|
||||||
outputs.push(<H as Digest>::digest(
|
outputs.push(<H as Digest>::digest(
|
||||||
&[
|
&[
|
||||||
serialize(input, 2)?,
|
serialize::<U2>(input)?,
|
||||||
serialize(info, 2)?,
|
serialize::<U2>(info)?,
|
||||||
serialize(&unblinded_element.to_arr().to_vec(), 2)?,
|
serialize::<U2>(&unblinded_element.to_arr().to_vec())?,
|
||||||
serialize(&finalize_dst, 2)?,
|
serialize::<U2>(&finalize_dst)?,
|
||||||
]
|
]
|
||||||
.concat(),
|
.concat(),
|
||||||
));
|
));
|
||||||
@@ -728,8 +789,8 @@ fn compute_composites<G: Group, H: BlockInput + Digest>(
|
|||||||
let composite_dst = [STR_COMPOSITE, &get_context_string::<G>(Mode::Verifiable)?].concat();
|
let composite_dst = [STR_COMPOSITE, &get_context_string::<G>(Mode::Verifiable)?].concat();
|
||||||
|
|
||||||
let h1_input = [
|
let h1_input = [
|
||||||
serialize(&b.to_arr().to_vec(), 2)?,
|
serialize::<U2>(&b.to_arr().to_vec())?,
|
||||||
serialize(&seed_dst, 2)?,
|
serialize::<U2>(&seed_dst)?,
|
||||||
]
|
]
|
||||||
.concat();
|
.concat();
|
||||||
let seed = <H as Digest>::digest(&h1_input);
|
let seed = <H as Digest>::digest(&h1_input);
|
||||||
@@ -739,11 +800,11 @@ fn compute_composites<G: Group, H: BlockInput + Digest>(
|
|||||||
|
|
||||||
for i in 0..c_slice.len() {
|
for i in 0..c_slice.len() {
|
||||||
let h2_input = [
|
let h2_input = [
|
||||||
serialize(&seed, 2)?,
|
serialize::<U2>(&seed)?,
|
||||||
i2osp(i, 2)?,
|
i2osp::<U2>(i)?.to_vec(),
|
||||||
serialize(&c_slice[i].value.to_arr().to_vec(), 2)?,
|
serialize::<U2>(&c_slice[i].value.to_arr().to_vec())?,
|
||||||
serialize(&d_slice[i].value.to_arr().to_vec(), 2)?,
|
serialize::<U2>(&d_slice[i].value.to_arr().to_vec())?,
|
||||||
serialize(&composite_dst, 2)?,
|
serialize::<U2>(&composite_dst)?,
|
||||||
]
|
]
|
||||||
.concat();
|
.concat();
|
||||||
let dst = [
|
let dst = [
|
||||||
@@ -772,8 +833,8 @@ fn compute_composites<G: Group, H: BlockInput + Digest>(
|
|||||||
fn get_context_string<G: Group>(mode: Mode) -> Result<alloc::vec::Vec<u8>, InternalError> {
|
fn get_context_string<G: Group>(mode: Mode) -> Result<alloc::vec::Vec<u8>, InternalError> {
|
||||||
Ok([
|
Ok([
|
||||||
STR_VOPRF,
|
STR_VOPRF,
|
||||||
&i2osp(mode as usize, 1)?,
|
&i2osp::<U1>(mode as usize)?,
|
||||||
&i2osp(G::SUITE_ID, 2)?,
|
&i2osp::<U2>(G::SUITE_ID)?,
|
||||||
]
|
]
|
||||||
.concat())
|
.concat())
|
||||||
}
|
}
|
||||||
@@ -789,6 +850,7 @@ mod tests {
|
|||||||
use crate::group::Group;
|
use crate::group::Group;
|
||||||
use generic_array::GenericArray;
|
use generic_array::GenericArray;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
fn prf<G: Group, H: BlockInput + Digest>(
|
fn prf<G: Group, H: BlockInput + Digest>(
|
||||||
input: &[u8],
|
input: &[u8],
|
||||||
@@ -802,7 +864,7 @@ mod tests {
|
|||||||
let context = [
|
let context = [
|
||||||
STR_CONTEXT,
|
STR_CONTEXT,
|
||||||
&get_context_string::<G>(mode).unwrap(),
|
&get_context_string::<G>(mode).unwrap(),
|
||||||
&serialize(info, 2).unwrap(),
|
&serialize::<U2>(info).unwrap(),
|
||||||
]
|
]
|
||||||
.concat();
|
.concat();
|
||||||
let dst = [STR_HASH_TO_SCALAR, &get_context_string::<G>(mode).unwrap()].concat();
|
let dst = [STR_HASH_TO_SCALAR, &get_context_string::<G>(mode).unwrap()].concat();
|
||||||
@@ -982,6 +1044,98 @@ mod tests {
|
|||||||
assert_eq!(client_finalize_result, res2);
|
assert_eq!(client_finalize_result, res2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn zeroize_base_client<G: Group, H: BlockInput + Digest>() {
|
||||||
|
let input = b"input";
|
||||||
|
let mut rng = OsRng;
|
||||||
|
let client_blind_result = NonVerifiableClient::<G, H>::blind(&input[..], &mut rng).unwrap();
|
||||||
|
|
||||||
|
let mut state = client_blind_result.state;
|
||||||
|
Zeroize::zeroize(&mut state);
|
||||||
|
for bytes in state.as_ptrs() {
|
||||||
|
assert!(bytes.iter().all(|&x| x == 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut message = client_blind_result.message;
|
||||||
|
Zeroize::zeroize(&mut message);
|
||||||
|
for bytes in message.as_ptrs() {
|
||||||
|
assert!(bytes.iter().all(|&x| x == 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zeroize_verifiable_client<G: Group, H: BlockInput + Digest>() {
|
||||||
|
let input = b"input";
|
||||||
|
let mut rng = OsRng;
|
||||||
|
let client_blind_result = VerifiableClient::<G, H>::blind(&input[..], &mut rng).unwrap();
|
||||||
|
|
||||||
|
let mut state = client_blind_result.state;
|
||||||
|
Zeroize::zeroize(&mut state);
|
||||||
|
for bytes in state.as_ptrs() {
|
||||||
|
assert!(bytes.iter().all(|&x| x == 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut message = client_blind_result.message;
|
||||||
|
Zeroize::zeroize(&mut message);
|
||||||
|
for bytes in message.as_ptrs() {
|
||||||
|
assert!(bytes.iter().all(|&x| x == 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zeroize_base_server<G: Group, H: BlockInput + Digest>() {
|
||||||
|
let input = b"input";
|
||||||
|
let info = b"info";
|
||||||
|
let mut rng = OsRng;
|
||||||
|
let client_blind_result = NonVerifiableClient::<G, H>::blind(&input[..], &mut rng).unwrap();
|
||||||
|
let server = NonVerifiableServer::<G, H>::new(&mut rng).unwrap();
|
||||||
|
let server_result = server
|
||||||
|
.evaluate(client_blind_result.message, &Metadata(info.to_vec()))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut state = server;
|
||||||
|
Zeroize::zeroize(&mut state);
|
||||||
|
for bytes in state.as_ptrs() {
|
||||||
|
assert!(bytes.iter().all(|&x| x == 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut message = server_result.message;
|
||||||
|
Zeroize::zeroize(&mut message);
|
||||||
|
for bytes in message.as_ptrs() {
|
||||||
|
assert!(bytes.iter().all(|&x| x == 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zeroize_verifiable_server<G: Group, H: BlockInput + Digest>() {
|
||||||
|
let input = b"input";
|
||||||
|
let info = b"info";
|
||||||
|
let mut rng = OsRng;
|
||||||
|
let client_blind_result = VerifiableClient::<G, H>::blind(&input[..], &mut rng).unwrap();
|
||||||
|
let server = VerifiableServer::<G, H>::new(&mut rng).unwrap();
|
||||||
|
let server_result = server
|
||||||
|
.evaluate(
|
||||||
|
&mut rng,
|
||||||
|
client_blind_result.message,
|
||||||
|
&Metadata(info.to_vec()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut state = server;
|
||||||
|
Zeroize::zeroize(&mut state);
|
||||||
|
for bytes in state.as_ptrs() {
|
||||||
|
assert!(bytes.iter().all(|&x| x == 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut message = server_result.message;
|
||||||
|
Zeroize::zeroize(&mut message);
|
||||||
|
for bytes in message.as_ptrs() {
|
||||||
|
assert!(bytes.iter().all(|&x| x == 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut proof = server_result.proof;
|
||||||
|
Zeroize::zeroize(&mut proof);
|
||||||
|
for bytes in proof.as_ptrs() {
|
||||||
|
assert!(bytes.iter().all(|&x| x == 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_functionality() -> Result<(), InternalError> {
|
fn test_functionality() -> Result<(), InternalError> {
|
||||||
use curve25519_dalek::ristretto::RistrettoPoint;
|
use curve25519_dalek::ristretto::RistrettoPoint;
|
||||||
@@ -994,6 +1148,11 @@ mod tests {
|
|||||||
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_server::<RistrettoPoint, Sha512>();
|
||||||
|
zeroize_verifiable_client::<RistrettoPoint, Sha512>();
|
||||||
|
zeroize_verifiable_server::<RistrettoPoint, Sha512>();
|
||||||
|
|
||||||
#[cfg(feature = "p256")]
|
#[cfg(feature = "p256")]
|
||||||
{
|
{
|
||||||
use p256_::ProjectivePoint;
|
use p256_::ProjectivePoint;
|
||||||
@@ -1005,6 +1164,11 @@ mod tests {
|
|||||||
verifiable_batch_retrieval::<ProjectivePoint, Sha256>();
|
verifiable_batch_retrieval::<ProjectivePoint, Sha256>();
|
||||||
verifiable_bad_public_key::<ProjectivePoint, Sha256>();
|
verifiable_bad_public_key::<ProjectivePoint, Sha256>();
|
||||||
verifiable_batch_bad_public_key::<ProjectivePoint, Sha256>();
|
verifiable_batch_bad_public_key::<ProjectivePoint, Sha256>();
|
||||||
|
|
||||||
|
zeroize_base_client::<ProjectivePoint, Sha256>();
|
||||||
|
zeroize_base_server::<ProjectivePoint, Sha256>();
|
||||||
|
zeroize_verifiable_client::<ProjectivePoint, Sha256>();
|
||||||
|
zeroize_verifiable_server::<ProjectivePoint, Sha256>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in New Issue
Block a user