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
|
||||
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:
|
||||
name: cargo fmt
|
||||
|
||||
+1
-20
@@ -6,14 +6,13 @@
|
||||
// of this source tree.
|
||||
|
||||
//! A list of error types which are produced during an execution of the protocol
|
||||
use core::fmt::Debug;
|
||||
#[cfg(feature = "std")]
|
||||
use std::error::Error;
|
||||
|
||||
use displaydoc::Display;
|
||||
|
||||
/// 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 {
|
||||
/// Could not parse byte sequence for key
|
||||
InvalidByteSequence,
|
||||
@@ -38,24 +37,6 @@ pub enum InternalError {
|
||||
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_attr(docsrs, doc(cfg(feature = "std")))]
|
||||
impl Error for InternalError {}
|
||||
|
||||
+15
-11
@@ -9,7 +9,7 @@ use crate::errors::InternalError;
|
||||
use crate::serialization::i2osp;
|
||||
use alloc::vec::Vec;
|
||||
use digest::{BlockInput, Digest};
|
||||
use generic_array::typenum::Unsigned;
|
||||
use generic_array::typenum::{Unsigned, U1, U2};
|
||||
|
||||
// Computes ceil(x / y)
|
||||
fn div_ceil(x: usize, y: usize) -> usize {
|
||||
@@ -32,23 +32,27 @@ pub fn expand_message_xmd<H: BlockInput + Digest>(
|
||||
dst: &[u8],
|
||||
len_in_bytes: usize,
|
||||
) -> Result<Vec<u8>, InternalError> {
|
||||
let b_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);
|
||||
let ell = div_ceil(len_in_bytes, <H as Digest>::OutputSize::USIZE);
|
||||
if ell > 255 {
|
||||
return Err(InternalError::HashToCurveError);
|
||||
}
|
||||
let dst_prime = [dst, &i2osp(dst.len(), 1)?].concat();
|
||||
let z_pad = i2osp(0, r_in_bytes)?;
|
||||
let l_i_b_str = i2osp(len_in_bytes, 2)?;
|
||||
let msg_prime = [&z_pad, msg, &l_i_b_str, &i2osp(0, 1)?, &dst_prime].concat();
|
||||
let dst_prime = [dst, &i2osp::<U1>(dst.len())?].concat();
|
||||
let z_pad = i2osp::<<H as BlockInput>::BlockSize>(0)?;
|
||||
let l_i_b_str = i2osp::<U2>(len_in_bytes)?;
|
||||
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 h = H::new();
|
||||
h.update(&b[0]);
|
||||
h.update(&i2osp(1, 1)?);
|
||||
h.update(&i2osp::<U1>(1)?);
|
||||
h.update(&dst_prime);
|
||||
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) {
|
||||
h.update(xor(&b[0], &b[i - 1])?);
|
||||
h.update(&i2osp(i, 1)?);
|
||||
h.update(&i2osp::<U1>(i)?);
|
||||
h.update(&dst_prime);
|
||||
b.push(h.finalize_reset().to_vec()); // 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
|
||||
/// is zero, then return an error.
|
||||
fn from_scalar_slice(
|
||||
scalar_bits: &GenericArray<u8, Self::ScalarLen>,
|
||||
fn from_scalar_slice<'a>(
|
||||
scalar_bits: impl Into<&'a GenericArray<u8, Self::ScalarLen>>,
|
||||
) -> 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()) {
|
||||
return Err(InternalError::ZeroScalarError);
|
||||
}
|
||||
@@ -88,10 +88,10 @@ pub trait Group:
|
||||
|
||||
/// Return an element from its fixed-length bytes representation. If the element
|
||||
/// is the identity element, return an error.
|
||||
fn from_element_slice(
|
||||
element_bits: &GenericArray<u8, Self::ElemLen>,
|
||||
fn from_element_slice<'a>(
|
||||
element_bits: impl Into<&'a GenericArray<u8, Self::ElemLen>>,
|
||||
) -> 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()) {
|
||||
// found the identity element
|
||||
|
||||
+88
-146
@@ -5,139 +5,122 @@
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
macro_rules! impl_debug_eq_hash_for {
|
||||
(struct $name:ident$(<$($gen:ident$(: $bound1:tt$( + $bound2:tt)*)?),+$(,)?>)?, [$field1:ident$(, $field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => {
|
||||
impl$(<$($gen$(: $bound1$( + $bound2)*)?),+>)? core::fmt::Debug for $name$(<$($gen),+>)?
|
||||
$(where $($type: core::fmt::Debug,)+)?
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
|
||||
impl$(<$($gen$(: $bound1$( + $bound2)*)?),+>)? Eq for $name$(<$($gen),+>)?
|
||||
$(where $($type: Eq,)+)?
|
||||
{}
|
||||
|
||||
impl$(<$($gen$(: $bound1$( + $bound2)*)?),+>)? PartialEq for $name$(<$($gen),+>)?
|
||||
$(where $($type: PartialEq,)+)?
|
||||
{
|
||||
},
|
||||
Eq => { },
|
||||
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,)+)?
|
||||
{
|
||||
},
|
||||
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);)*
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_clone_for {
|
||||
(struct $name:ident$(<$($gen:ident$(: $bound1:tt$( + $bound2:tt)*)?),+$(,)?>)?, [$field1:ident$(, $field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => {
|
||||
impl$(<$($gen$(: $bound1$(+ $bound2)*)?),+>)? Clone for $name$(<$($gen),+>)?
|
||||
$(where $($type: Clone,)+)?
|
||||
{
|
||||
},
|
||||
Clone => {
|
||||
fn clone(&self) -> Self {
|
||||
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_.$field.zeroize();
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_zeroize_on_drop_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),+>)?
|
||||
impl$(<$($gen$(: $bound1 $(+ $bound2)*)?),+>)? zeroize::Zeroize for $name$(<$($gen),+>)?
|
||||
{
|
||||
fn zeroize(&mut self) {
|
||||
impl_zeroize_field_skip_pd!(self, $field1$(, $pd1)?);
|
||||
$(impl_zeroize_field_skip_pd!(self, $field2$(, $pd2)?);)*
|
||||
impl_internal_zeroize!(self, $(#$attr1)? $field1);
|
||||
$(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) {
|
||||
#[allow(unused_imports)]
|
||||
use zeroize::Zeroize;
|
||||
impl_zeroize_field_skip_pd!(self, $field1$(, $pd1)?);
|
||||
$(impl_zeroize_field_skip_pd!(self, $field2$(, $pd2)?);)*
|
||||
zeroize::Zeroize::zeroize(self);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// 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_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>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
@@ -152,62 +135,21 @@ macro_rules! impl_serialize_and_deserialize_for {
|
||||
|
||||
#[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>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::de::Error;
|
||||
|
||||
if deserializer.is_human_readable() {
|
||||
let s = <&str>::deserialize(deserializer)?;
|
||||
$name$(::<$($gen),+>)?::deserialize(&base64::decode(s).map_err(serde::de::Error::custom)?)
|
||||
.map_err(serde::de::Error::custom)
|
||||
Self::deserialize(&base64::decode(s).map_err(Error::custom)?)
|
||||
} else {
|
||||
struct ByteVisitor$(<$($gen$(: $bound1$(+ $bound2)*)?),+> (
|
||||
#[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,
|
||||
))?)
|
||||
Self::deserialize(<&[u8]>::deserialize(deserializer)?)
|
||||
}
|
||||
.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
|
||||
//! [NonVerifiableServer::evaluate] (an [EvaluationElement]), and runs
|
||||
//! [NonVerifiableClient::finalize] to produce a
|
||||
//! [NonVerifiableClientFinalizeResult], which consists of an
|
||||
//! output for the protocol.
|
||||
//! [NonVerifiableClient::finalize] to produce an output for the protocol.
|
||||
//!
|
||||
//! ```
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -243,9 +241,7 @@
|
||||
//! In the final step, the client takes as input the message from
|
||||
//! [VerifiableServer::evaluate] (an [EvaluationElement]),
|
||||
//! the proof, and the server's public key, and runs
|
||||
//! [VerifiableClient::finalize] to produce a
|
||||
//! [VerifiableClientFinalizeResult], which consists of an
|
||||
//! output for the protocol.
|
||||
//! [VerifiableClient::finalize] to produce an output for the protocol.
|
||||
//!
|
||||
//! ```
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -435,10 +431,9 @@ extern crate alloc;
|
||||
|
||||
#[macro_use]
|
||||
mod impls;
|
||||
#[macro_use]
|
||||
mod serialization;
|
||||
pub mod errors;
|
||||
pub mod group;
|
||||
mod serialization;
|
||||
mod voprf;
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+37
-41
@@ -19,7 +19,7 @@ use crate::{
|
||||
use alloc::vec::Vec;
|
||||
use core::marker::PhantomData;
|
||||
use digest::{BlockInput, Digest};
|
||||
use generic_array::{typenum::Unsigned, GenericArray};
|
||||
use generic_array::{typenum::Unsigned, ArrayLength, GenericArray};
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
// 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> {
|
||||
/// Serialization into bytes
|
||||
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
|
||||
@@ -39,7 +39,7 @@ impl<G: Group, H: BlockInput + Digest> NonVerifiableClient<G, H> {
|
||||
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();
|
||||
|
||||
Ok(Self {
|
||||
@@ -54,9 +54,9 @@ impl<G: Group, H: BlockInput + Digest> VerifiableClient<G, H> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
[
|
||||
G::scalar_as_bytes(self.blind).to_vec(),
|
||||
self.blinded_element.to_arr().to_vec(),
|
||||
self.data.clone(),
|
||||
G::scalar_as_bytes(self.blind).as_slice(),
|
||||
&self.blinded_element.to_arr(),
|
||||
&self.data,
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
@@ -69,10 +69,8 @@ impl<G: Group, H: BlockInput + Digest> VerifiableClient<G, H> {
|
||||
return Err(InternalError::SizeError);
|
||||
}
|
||||
|
||||
let blind = G::from_scalar_slice(GenericArray::from_slice(&input[..scalar_len]))?;
|
||||
let blinded_element = G::from_element_slice(GenericArray::from_slice(
|
||||
&input[scalar_len..scalar_len + elem_len],
|
||||
))?;
|
||||
let blind = G::from_scalar_slice(&input[..scalar_len])?;
|
||||
let blinded_element = G::from_element_slice(&input[scalar_len..scalar_len + elem_len])?;
|
||||
let data = input[scalar_len + elem_len..].to_vec();
|
||||
|
||||
Ok(Self {
|
||||
@@ -97,7 +95,7 @@ impl<G: Group, H: BlockInput + Digest> NonVerifiableServer<G, H> {
|
||||
return Err(InternalError::SizeError);
|
||||
}
|
||||
|
||||
let sk = G::from_scalar_slice(GenericArray::from_slice(input))?;
|
||||
let sk = G::from_scalar_slice(input)?;
|
||||
|
||||
Ok(Self {
|
||||
sk,
|
||||
@@ -109,11 +107,7 @@ impl<G: Group, H: BlockInput + Digest> NonVerifiableServer<G, H> {
|
||||
impl<G: Group, H: BlockInput + Digest> VerifiableServer<G, H> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
[
|
||||
G::scalar_as_bytes(self.sk).to_vec(),
|
||||
self.pk.to_arr().to_vec(),
|
||||
]
|
||||
.concat()
|
||||
[G::scalar_as_bytes(self.sk).as_slice(), &self.pk.to_arr()].concat()
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
@@ -124,8 +118,8 @@ impl<G: Group, H: BlockInput + Digest> VerifiableServer<G, H> {
|
||||
return Err(InternalError::SizeError);
|
||||
}
|
||||
|
||||
let sk = G::from_scalar_slice(GenericArray::from_slice(&input[..scalar_len]))?;
|
||||
let pk = G::from_element_slice(GenericArray::from_slice(&input[scalar_len..]))?;
|
||||
let sk = G::from_scalar_slice(&input[..scalar_len])?;
|
||||
let pk = G::from_element_slice(&input[scalar_len..])?;
|
||||
|
||||
Ok(Self {
|
||||
sk,
|
||||
@@ -152,8 +146,8 @@ impl<G: Group, H: BlockInput + Digest> Proof<G, H> {
|
||||
return Err(InternalError::SizeError);
|
||||
}
|
||||
Ok(Proof {
|
||||
c_scalar: G::from_scalar_slice(GenericArray::from_slice(&input[..scalar_len]))?,
|
||||
s_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(&input[scalar_len..])?,
|
||||
hash: PhantomData,
|
||||
})
|
||||
}
|
||||
@@ -168,7 +162,7 @@ impl<G: Group, H: BlockInput + Digest> BlindedElement<G, H> {
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
|
||||
Ok(Self {
|
||||
value: G::from_element_slice(GenericArray::from_slice(input))?,
|
||||
value: G::from_element_slice(input)?,
|
||||
hash: PhantomData,
|
||||
})
|
||||
}
|
||||
@@ -183,7 +177,7 @@ impl<G: Group, H: BlockInput + Digest> EvaluationElement<G, H> {
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
|
||||
Ok(Self {
|
||||
value: G::from_element_slice(GenericArray::from_slice(input))?,
|
||||
value: G::from_element_slice(input)?,
|
||||
hash: PhantomData,
|
||||
})
|
||||
}
|
||||
@@ -195,46 +189,48 @@ impl<G: Group, H: BlockInput + Digest> EvaluationElement<G, H> {
|
||||
//////////////////////
|
||||
|
||||
// Corresponds to the I2OSP() function from RFC8017
|
||||
pub(crate) fn i2osp(input: usize, length: usize) -> Result<alloc::vec::Vec<u8>, InternalError> {
|
||||
let sizeof_usize = core::mem::size_of::<usize>();
|
||||
pub(crate) fn i2osp<L: ArrayLength<u8>>(
|
||||
input: usize,
|
||||
) -> Result<GenericArray<u8, L>, InternalError> {
|
||||
const SIZEOF_USIZE: usize = core::mem::size_of::<usize>();
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
if length <= sizeof_usize {
|
||||
return Ok((&input.to_be_bytes()[sizeof_usize - length..]).to_vec());
|
||||
if L::USIZE <= SIZEOF_USIZE {
|
||||
return Ok(GenericArray::clone_from_slice(
|
||||
&input.to_be_bytes()[SIZEOF_USIZE - L::USIZE..],
|
||||
));
|
||||
}
|
||||
|
||||
let mut output = alloc::vec![0u8; length];
|
||||
output.splice(
|
||||
length - sizeof_usize..length,
|
||||
input.to_be_bytes().iter().cloned(),
|
||||
);
|
||||
let mut output = GenericArray::default();
|
||||
output[L::USIZE - SIZEOF_USIZE..L::USIZE].copy_from_slice(&input.to_be_bytes());
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
// Computes I2OSP(len(input), max_bytes) || input
|
||||
pub(crate) fn serialize(input: &[u8], max_bytes: usize) -> Result<Vec<u8>, InternalError> {
|
||||
Ok([&i2osp(input.len(), max_bytes)?, input].concat())
|
||||
pub(crate) fn serialize<L: ArrayLength<u8>>(input: &[u8]) -> Result<Vec<u8>, InternalError> {
|
||||
Ok([&i2osp::<L>(input.len())?, input].concat())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_tests {
|
||||
use super::*;
|
||||
use generic_array::typenum::{U1, U2};
|
||||
|
||||
// Test the error condition for I2OSP
|
||||
#[test]
|
||||
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(256, 1).is_err());
|
||||
assert!(i2osp(257, 1).is_err());
|
||||
assert!(i2osp::<U1>(255).is_ok());
|
||||
assert!(i2osp::<U1>(256).is_err());
|
||||
assert!(i2osp::<U1>(257).is_err());
|
||||
|
||||
assert!(i2osp(256 * 256 - 1, 2).is_ok());
|
||||
assert!(i2osp(256 * 256, 2).is_err());
|
||||
assert!(i2osp(256 * 256 + 1, 2).is_err());
|
||||
assert!(i2osp::<U2>(256 * 256 - 1).is_ok());
|
||||
assert!(i2osp::<U2>(256 * 256).is_err());
|
||||
assert!(i2osp::<U2>(256 * 256 + 1).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
+255
-91
@@ -16,7 +16,10 @@ use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::marker::PhantomData;
|
||||
use digest::{BlockInput, Digest};
|
||||
use generic_array::{typenum::Unsigned, GenericArray};
|
||||
use generic_array::{
|
||||
typenum::{Unsigned, U1, U2},
|
||||
GenericArray,
|
||||
};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
///////////////
|
||||
@@ -46,99 +49,94 @@ enum Mode {
|
||||
// ====================== //
|
||||
////////////////////////////
|
||||
|
||||
/// A client which engages with a [NonVerifiableServer]
|
||||
/// in base mode, meaning that the OPRF outputs are not
|
||||
/// verifiable.
|
||||
pub struct NonVerifiableClient<G: Group, H: BlockInput + Digest> {
|
||||
impl_traits_for! {
|
||||
/// A client which engages with a [NonVerifiableServer]
|
||||
/// in base mode, meaning that the OPRF outputs are not
|
||||
/// verifiable.
|
||||
pub struct NonVerifiableClient<G: Group, H: BlockInput + Digest> {
|
||||
#[bind]
|
||||
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]
|
||||
/// in verifiable mode, meaning that the OPRF outputs
|
||||
/// can be checked against a server public key.
|
||||
pub struct VerifiableClient<G: Group, H: BlockInput + Digest> {
|
||||
impl_traits_for! {
|
||||
/// A client which engages with a [VerifiableServer]
|
||||
/// in verifiable mode, meaning that the OPRF outputs
|
||||
/// can be checked against a server public key.
|
||||
pub struct VerifiableClient<G: Group, H: BlockInput + Digest> {
|
||||
#[bind]
|
||||
pub(crate) blind: <G as Group>::Scalar,
|
||||
#[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]
|
||||
/// in base mode, meaning that the OPRF outputs are not
|
||||
/// verifiable.
|
||||
pub struct NonVerifiableServer<G: Group, H: BlockInput + Digest> {
|
||||
impl_traits_for! {
|
||||
/// A server which engages with a [NonVerifiableClient]
|
||||
/// in base mode, meaning that the OPRF outputs are not
|
||||
/// verifiable.
|
||||
pub struct NonVerifiableServer<G: Group, H: BlockInput + Digest> {
|
||||
#[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]
|
||||
/// in verifiable mode, meaning that the OPRF outputs
|
||||
/// can be checked against a server public key.
|
||||
pub struct VerifiableServer<G: Group, H: BlockInput + Digest> {
|
||||
impl_traits_for! {
|
||||
/// A server which engages with a [VerifiableClient]
|
||||
/// in verifiable mode, meaning that the OPRF outputs
|
||||
/// can be checked against a server public key.
|
||||
pub struct VerifiableServer<G: Group, H: BlockInput + Digest> {
|
||||
#[bind]
|
||||
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
|
||||
/// the OPRF output matches against a server public key.
|
||||
pub struct Proof<G: Group, H: BlockInput + Digest> {
|
||||
impl_traits_for! {
|
||||
/// A proof produced by a [VerifiableServer] that
|
||||
/// the OPRF output matches against a server public key.
|
||||
pub struct Proof<G: Group, H: BlockInput + Digest> {
|
||||
#[bind]
|
||||
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)
|
||||
/// to a server (either verifiable or not).
|
||||
pub struct BlindedElement<G: Group, H: BlockInput + Digest> {
|
||||
impl_traits_for! {
|
||||
/// The first client message sent from a client (either verifiable or not)
|
||||
/// to a server (either verifiable or not).
|
||||
pub struct BlindedElement<G: Group, H: BlockInput + Digest> {
|
||||
#[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
|
||||
/// a client (either verifiable or not)
|
||||
/// to a server (either verifiable or not).
|
||||
pub struct EvaluationElement<G: Group, H: BlockInput + Digest> {
|
||||
impl_traits_for! {
|
||||
/// The server's response to the [BlindedElement] message from
|
||||
/// a client (either verifiable or not)
|
||||
/// to a server (either verifiable or not).
|
||||
pub struct EvaluationElement<G: Group, H: BlockInput + Digest> {
|
||||
#[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 //
|
||||
@@ -197,6 +195,15 @@ impl<G: Group, H: BlockInput + Digest> NonVerifiableClient<G, H> {
|
||||
pub fn get_blind(&self) -> <G as Group>::Scalar {
|
||||
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> {
|
||||
@@ -294,6 +301,16 @@ impl<G: Group, H: BlockInput + Digest> VerifiableClient<G, H> {
|
||||
pub fn get_blind(&self) -> <G as Group>::Scalar {
|
||||
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> {
|
||||
@@ -343,7 +360,7 @@ impl<G: Group, H: BlockInput + Digest> NonVerifiableServer<G, H> {
|
||||
let context = [
|
||||
STR_CONTEXT,
|
||||
&get_context_string::<G>(Mode::Base)?,
|
||||
&serialize(&metadata.0, 2)?,
|
||||
&serialize::<U2>(&metadata.0)?,
|
||||
]
|
||||
.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> {
|
||||
@@ -429,7 +452,7 @@ impl<G: Group, H: BlockInput + Digest> VerifiableServer<G, H> {
|
||||
let context = [
|
||||
STR_CONTEXT,
|
||||
&get_context_string::<G>(Mode::Verifiable)?,
|
||||
&serialize(&metadata.0, 2)?,
|
||||
&serialize::<U2>(&metadata.0)?,
|
||||
]
|
||||
.concat();
|
||||
let dst = [
|
||||
@@ -462,6 +485,15 @@ impl<G: Group, H: BlockInput + Digest> VerifiableServer<G, H> {
|
||||
pub fn get_public_key(&self) -> G {
|
||||
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>,
|
||||
}
|
||||
|
||||
/// 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
|
||||
fn blind<G: Group, H: BlockInput + Digest, R: RngCore + CryptoRng>(
|
||||
input: &[u8],
|
||||
@@ -576,7 +637,7 @@ fn verifiable_unblind<G: Group, H: BlockInput + Digest>(
|
||||
let context = [
|
||||
STR_CONTEXT,
|
||||
&get_context_string::<G>(Mode::Verifiable)?,
|
||||
&serialize(info, 2)?,
|
||||
&serialize::<U2>(info)?,
|
||||
]
|
||||
.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 h2_input = [
|
||||
serialize(&b.to_arr().to_vec(), 2)?,
|
||||
serialize(&m.to_arr().to_vec(), 2)?,
|
||||
serialize(&z.to_arr().to_vec(), 2)?,
|
||||
serialize(&t2.to_arr().to_vec(), 2)?,
|
||||
serialize(&t3.to_arr().to_vec(), 2)?,
|
||||
serialize(&challenge_dst, 2)?,
|
||||
serialize::<U2>(&b.to_arr().to_vec())?,
|
||||
serialize::<U2>(&m.to_arr().to_vec())?,
|
||||
serialize::<U2>(&z.to_arr().to_vec())?,
|
||||
serialize::<U2>(&t2.to_arr().to_vec())?,
|
||||
serialize::<U2>(&t3.to_arr().to_vec())?,
|
||||
serialize::<U2>(&challenge_dst)?,
|
||||
]
|
||||
.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 h2_input = [
|
||||
serialize(&b.to_arr().to_vec(), 2)?,
|
||||
serialize(&m.to_arr().to_vec(), 2)?,
|
||||
serialize(&z.to_arr().to_vec(), 2)?,
|
||||
serialize(&t2.to_arr().to_vec(), 2)?,
|
||||
serialize(&t3.to_arr().to_vec(), 2)?,
|
||||
serialize(&challenge_dst, 2)?,
|
||||
serialize::<U2>(&b.to_arr().to_vec())?,
|
||||
serialize::<U2>(&m.to_arr().to_vec())?,
|
||||
serialize::<U2>(&z.to_arr().to_vec())?,
|
||||
serialize::<U2>(&t2.to_arr().to_vec())?,
|
||||
serialize::<U2>(&t3.to_arr().to_vec())?,
|
||||
serialize::<U2>(&challenge_dst)?,
|
||||
]
|
||||
.concat();
|
||||
|
||||
@@ -702,10 +763,10 @@ fn finalize_after_unblind<G: Group, H: BlockInput + Digest>(
|
||||
for (input, unblinded_element) in inputs_and_unblinded_elements {
|
||||
outputs.push(<H as Digest>::digest(
|
||||
&[
|
||||
serialize(input, 2)?,
|
||||
serialize(info, 2)?,
|
||||
serialize(&unblinded_element.to_arr().to_vec(), 2)?,
|
||||
serialize(&finalize_dst, 2)?,
|
||||
serialize::<U2>(input)?,
|
||||
serialize::<U2>(info)?,
|
||||
serialize::<U2>(&unblinded_element.to_arr().to_vec())?,
|
||||
serialize::<U2>(&finalize_dst)?,
|
||||
]
|
||||
.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 h1_input = [
|
||||
serialize(&b.to_arr().to_vec(), 2)?,
|
||||
serialize(&seed_dst, 2)?,
|
||||
serialize::<U2>(&b.to_arr().to_vec())?,
|
||||
serialize::<U2>(&seed_dst)?,
|
||||
]
|
||||
.concat();
|
||||
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() {
|
||||
let h2_input = [
|
||||
serialize(&seed, 2)?,
|
||||
i2osp(i, 2)?,
|
||||
serialize(&c_slice[i].value.to_arr().to_vec(), 2)?,
|
||||
serialize(&d_slice[i].value.to_arr().to_vec(), 2)?,
|
||||
serialize(&composite_dst, 2)?,
|
||||
serialize::<U2>(&seed)?,
|
||||
i2osp::<U2>(i)?.to_vec(),
|
||||
serialize::<U2>(&c_slice[i].value.to_arr().to_vec())?,
|
||||
serialize::<U2>(&d_slice[i].value.to_arr().to_vec())?,
|
||||
serialize::<U2>(&composite_dst)?,
|
||||
]
|
||||
.concat();
|
||||
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> {
|
||||
Ok([
|
||||
STR_VOPRF,
|
||||
&i2osp(mode as usize, 1)?,
|
||||
&i2osp(G::SUITE_ID, 2)?,
|
||||
&i2osp::<U1>(mode as usize)?,
|
||||
&i2osp::<U2>(G::SUITE_ID)?,
|
||||
]
|
||||
.concat())
|
||||
}
|
||||
@@ -789,6 +850,7 @@ mod tests {
|
||||
use crate::group::Group;
|
||||
use generic_array::GenericArray;
|
||||
use rand::rngs::OsRng;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
fn prf<G: Group, H: BlockInput + Digest>(
|
||||
input: &[u8],
|
||||
@@ -802,7 +864,7 @@ mod tests {
|
||||
let context = [
|
||||
STR_CONTEXT,
|
||||
&get_context_string::<G>(mode).unwrap(),
|
||||
&serialize(info, 2).unwrap(),
|
||||
&serialize::<U2>(info).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);
|
||||
}
|
||||
|
||||
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]
|
||||
fn test_functionality() -> Result<(), InternalError> {
|
||||
use curve25519_dalek::ristretto::RistrettoPoint;
|
||||
@@ -994,6 +1148,11 @@ mod tests {
|
||||
verifiable_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")]
|
||||
{
|
||||
use p256_::ProjectivePoint;
|
||||
@@ -1005,6 +1164,11 @@ mod tests {
|
||||
verifiable_batch_retrieval::<ProjectivePoint, Sha256>();
|
||||
verifiable_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(())
|
||||
|
||||
Reference in New Issue
Block a user