Remove allocations by serialize (#29)
* Changed `expand_message_xmd` input to `Iterator` * Changed `hash_to_scalar` input to `Iterator` * Fix rustfmt * Add documentation for private helper types * Fix documentation * Improve `chain!()` syntax bias * Move helper functions to `mod util`
This commit is contained in:
@@ -100,7 +100,7 @@ jobs:
|
||||
RUSTDOCFLAGS: -D warnings
|
||||
with:
|
||||
command: doc
|
||||
args: --no-deps --document-private-items
|
||||
args: --no-deps --document-private-items --features std,p256
|
||||
|
||||
|
||||
format:
|
||||
|
||||
+25
-16
@@ -6,7 +6,7 @@
|
||||
// of this source tree.
|
||||
|
||||
use crate::errors::InternalError;
|
||||
use crate::serialization::i2osp;
|
||||
use crate::util::i2osp;
|
||||
use core::ops::Add;
|
||||
use digest::{BlockInput, Digest};
|
||||
use generic_array::{
|
||||
@@ -28,11 +28,13 @@ fn xor<L: ArrayLength<u8>>(x: GenericArray<u8, L>, y: GenericArray<u8, L>) -> Ge
|
||||
/// Corresponds to the expand_message_xmd() function defined in
|
||||
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt>
|
||||
pub fn expand_message_xmd<
|
||||
'a,
|
||||
H: BlockInput + Digest,
|
||||
L: ArrayLength<u8>,
|
||||
M: IntoIterator<Item = &'a [u8]>,
|
||||
D: ArrayLength<u8> + Add<U1>,
|
||||
>(
|
||||
msg: &[u8],
|
||||
msg: M,
|
||||
dst: GenericArray<u8, D>,
|
||||
) -> Result<GenericArray<u8, L>, InternalError>
|
||||
where
|
||||
@@ -46,19 +48,20 @@ where
|
||||
let dst_prime = dst.concat(i2osp::<U1>(D::USIZE)?);
|
||||
let z_pad = i2osp::<<H as BlockInput>::BlockSize>(0)?;
|
||||
let l_i_b_str = i2osp::<U2>(L::USIZE)?;
|
||||
let msg_0 = i2osp::<U1>(0)?;
|
||||
let msg_prime =
|
||||
core::array::IntoIter::new([z_pad.as_slice(), msg, &l_i_b_str, &msg_0, &dst_prime]);
|
||||
|
||||
let mut h = H::new();
|
||||
|
||||
// msg_prime = Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime
|
||||
h.update(z_pad);
|
||||
for bytes in msg {
|
||||
h.update(bytes)
|
||||
}
|
||||
h.update(l_i_b_str);
|
||||
h.update(i2osp::<U1>(0)?);
|
||||
h.update(&dst_prime);
|
||||
|
||||
// b[0]
|
||||
let b_0 = msg_prime
|
||||
.into_iter()
|
||||
.fold(&mut h, |h, msg| {
|
||||
h.update(msg);
|
||||
h
|
||||
})
|
||||
.finalize_reset();
|
||||
let b_0 = h.finalize_reset();
|
||||
let mut b_i = GenericArray::default();
|
||||
|
||||
let mut uniform_bytes = GenericArray::default();
|
||||
@@ -192,10 +195,16 @@ mod tests {
|
||||
|
||||
for tv in test_vectors {
|
||||
let uniform_bytes = match tv.len_in_bytes {
|
||||
32 => super::expand_message_xmd::<sha2::Sha256, U32, _>(tv.msg.as_bytes(), dst)
|
||||
.map(|bytes| bytes.to_vec()),
|
||||
128 => super::expand_message_xmd::<sha2::Sha256, U128, _>(tv.msg.as_bytes(), dst)
|
||||
.map(|bytes| bytes.to_vec()),
|
||||
32 => super::expand_message_xmd::<sha2::Sha256, U32, _, _>(
|
||||
Some(tv.msg.as_bytes()),
|
||||
dst,
|
||||
)
|
||||
.map(|bytes| bytes.to_vec()),
|
||||
128 => super::expand_message_xmd::<sha2::Sha256, U128, _, _>(
|
||||
Some(tv.msg.as_bytes()),
|
||||
dst,
|
||||
)
|
||||
.map(|bytes| bytes.to_vec()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
.unwrap();
|
||||
|
||||
+7
-2
@@ -57,8 +57,13 @@ pub trait Group:
|
||||
<D as Add<U1>>::Output: ArrayLength<u8>;
|
||||
|
||||
/// Hashes a slice of pseudo-random bytes to a scalar
|
||||
fn hash_to_scalar<H: BlockInput + Digest, D: ArrayLength<u8> + Add<U1>>(
|
||||
input: &[u8],
|
||||
fn hash_to_scalar<
|
||||
'a,
|
||||
H: BlockInput + Digest,
|
||||
D: ArrayLength<u8> + Add<U1>,
|
||||
I: IntoIterator<Item = &'a [u8]>,
|
||||
>(
|
||||
input: I,
|
||||
dst: GenericArray<u8, D>,
|
||||
) -> Result<Self::Scalar, InternalError>
|
||||
where
|
||||
|
||||
+16
-10
@@ -75,7 +75,7 @@ impl Group for ProjectivePoint {
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
|
||||
// `hash_to_field` calls `expand_message` with a `len_in_bytes` of `count * L`
|
||||
let uniform_bytes =
|
||||
super::expand::expand_message_xmd::<H, <L as Mul<U2>>::Output, _>(msg, dst)?;
|
||||
super::expand::expand_message_xmd::<H, <L as Mul<U2>>::Output, _, _>(Some(msg), dst)?;
|
||||
|
||||
// hash to curve
|
||||
let (q0x, q0y) = hash_to_curve_simple_swu(&uniform_bytes[..L::USIZE], &A, &B, &P, &Z);
|
||||
@@ -97,8 +97,13 @@ impl Group for ProjectivePoint {
|
||||
|
||||
// Implements the `HashToScalar()` function from
|
||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.3
|
||||
fn hash_to_scalar<H: BlockInput + Digest, D: ArrayLength<u8> + Add<U1>>(
|
||||
input: &[u8],
|
||||
fn hash_to_scalar<
|
||||
'a,
|
||||
H: BlockInput + Digest,
|
||||
D: ArrayLength<u8> + Add<U1>,
|
||||
I: IntoIterator<Item = &'a [u8]>,
|
||||
>(
|
||||
input: I,
|
||||
dst: GenericArray<u8, D>,
|
||||
) -> Result<Self::Scalar, InternalError>
|
||||
where
|
||||
@@ -115,7 +120,7 @@ impl Group for ProjectivePoint {
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
|
||||
// `HashToScalar` is `hash_to_field`
|
||||
let uniform_bytes = super::expand::expand_message_xmd::<H, L, _>(input, dst)?;
|
||||
let uniform_bytes = super::expand::expand_message_xmd::<H, L, _, _>(input, dst)?;
|
||||
let bytes = BigInt::from_bytes_be(Sign::Plus, &uniform_bytes)
|
||||
.mod_floor(&N)
|
||||
.to_bytes_be()
|
||||
@@ -180,7 +185,7 @@ impl Group for ProjectivePoint {
|
||||
///
|
||||
/// `cmov`, `mod_floor` and `modpow` needs to be made constant-time, which
|
||||
/// will be supported after crypto-bigint is no longer experimental. See
|
||||
/// https://github.com/novifinancial/voprf/issues/13 for more context.
|
||||
/// <https://github.com/novifinancial/voprf/issues/13> for more context.
|
||||
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
fn hash_to_curve_simple_swu<N: ArrayLength<u8>>(
|
||||
@@ -536,11 +541,12 @@ mod tests {
|
||||
let dst = GenericArray::from(*b"QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_");
|
||||
|
||||
for tv in test_vectors {
|
||||
let uniform_bytes = super::super::expand::expand_message_xmd::<sha2::Sha256, U96, _>(
|
||||
tv.msg.as_bytes(),
|
||||
dst,
|
||||
)
|
||||
.unwrap();
|
||||
let uniform_bytes =
|
||||
super::super::expand::expand_message_xmd::<sha2::Sha256, U96, _, _>(
|
||||
Some(tv.msg.as_bytes()),
|
||||
dst,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let u0 = BigInt::from_bytes_be(Sign::Plus, &uniform_bytes[..48]).mod_floor(&P);
|
||||
let u1 = BigInt::from_bytes_be(Sign::Plus, &uniform_bytes[48..]).mod_floor(&P);
|
||||
|
||||
@@ -42,7 +42,7 @@ impl Group for RistrettoPoint {
|
||||
where
|
||||
<D as Add<U1>>::Output: ArrayLength<u8>,
|
||||
{
|
||||
let uniform_bytes = super::expand::expand_message_xmd::<H, U64, _>(msg, dst)?;
|
||||
let uniform_bytes = super::expand::expand_message_xmd::<H, U64, _, _>(Some(msg), dst)?;
|
||||
|
||||
Ok(RistrettoPoint::from_uniform_bytes(
|
||||
uniform_bytes
|
||||
@@ -54,14 +54,19 @@ impl Group for RistrettoPoint {
|
||||
|
||||
// Implements the `HashToScalar()` function from
|
||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.1
|
||||
fn hash_to_scalar<H: BlockInput + Digest, D: ArrayLength<u8> + Add<U1>>(
|
||||
input: &[u8],
|
||||
fn hash_to_scalar<
|
||||
'a,
|
||||
H: BlockInput + Digest,
|
||||
D: ArrayLength<u8> + Add<U1>,
|
||||
I: IntoIterator<Item = &'a [u8]>,
|
||||
>(
|
||||
input: I,
|
||||
dst: GenericArray<u8, D>,
|
||||
) -> Result<Self::Scalar, InternalError>
|
||||
where
|
||||
<D as Add<U1>>::Output: ArrayLength<u8>,
|
||||
{
|
||||
let uniform_bytes = super::expand::expand_message_xmd::<H, U64, _>(input, dst)?;
|
||||
let uniform_bytes = super::expand::expand_message_xmd::<H, U64, _, _>(input, dst)?;
|
||||
|
||||
Ok(Scalar::from_bytes_mod_order_wide(
|
||||
uniform_bytes
|
||||
|
||||
@@ -425,6 +425,8 @@ extern crate alloc;
|
||||
|
||||
#[macro_use]
|
||||
mod impls;
|
||||
#[macro_use]
|
||||
mod util;
|
||||
pub mod errors;
|
||||
pub mod group;
|
||||
mod serialization;
|
||||
|
||||
+1
-94
@@ -19,7 +19,7 @@ use crate::{
|
||||
use alloc::vec::Vec;
|
||||
use core::marker::PhantomData;
|
||||
use digest::{BlockInput, Digest};
|
||||
use generic_array::{typenum::Unsigned, ArrayLength, GenericArray};
|
||||
use generic_array::typenum::Unsigned;
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
// Serialization and Deserialization for High-Level API //
|
||||
@@ -190,96 +190,3 @@ impl<G: Group, H: BlockInput + Digest> EvaluationElement<G, H> {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
// Helper Functions //
|
||||
// ================ //
|
||||
//////////////////////
|
||||
|
||||
// Corresponds to the I2OSP() function from RFC8017
|
||||
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) > L::U32 {
|
||||
return Err(InternalError::SerializationError);
|
||||
}
|
||||
|
||||
if L::USIZE <= SIZEOF_USIZE {
|
||||
return Ok(GenericArray::clone_from_slice(
|
||||
&input.to_be_bytes()[SIZEOF_USIZE - L::USIZE..],
|
||||
));
|
||||
}
|
||||
|
||||
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<L: ArrayLength<u8>>(input: &[u8]) -> Result<Vec<u8>, InternalError> {
|
||||
Ok([&i2osp::<L>(input.len())?, input].concat())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_tests {
|
||||
use super::*;
|
||||
use curve25519_dalek::ristretto::RistrettoPoint;
|
||||
use generic_array::typenum::{U1, U2};
|
||||
use proptest::{collection::vec, prelude::*};
|
||||
use sha2::Sha512;
|
||||
|
||||
// Test the error condition for I2OSP
|
||||
#[test]
|
||||
fn test_i2osp_err_check() {
|
||||
assert!(i2osp::<U1>(0).is_ok());
|
||||
|
||||
assert!(i2osp::<U1>(255).is_ok());
|
||||
assert!(i2osp::<U1>(256).is_err());
|
||||
assert!(i2osp::<U1>(257).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());
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_nocrash_nonverifiable_client(bytes in vec(any::<u8>(), 0..200)) {
|
||||
NonVerifiableClient::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_verifiable_client(bytes in vec(any::<u8>(), 0..200)) {
|
||||
VerifiableClient::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_nonverifiable_server(bytes in vec(any::<u8>(), 0..200)) {
|
||||
NonVerifiableServer::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_verifiable_server(bytes in vec(any::<u8>(), 0..200)) {
|
||||
VerifiableServer::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_blinded_element(bytes in vec(any::<u8>(), 0..200)) {
|
||||
BlindedElement::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_evaluation_element(bytes in vec(any::<u8>(), 0..200)) {
|
||||
EvaluationElement::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_proof(bytes in vec(any::<u8>(), 0..200)) {
|
||||
Proof::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
//! Helper functions
|
||||
|
||||
use crate::errors::InternalError;
|
||||
use core::array::IntoIter;
|
||||
use generic_array::{typenum::U0, ArrayLength, GenericArray};
|
||||
|
||||
// Corresponds to the I2OSP() function from RFC8017
|
||||
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) > L::U32 {
|
||||
return Err(InternalError::SerializationError);
|
||||
}
|
||||
|
||||
if L::USIZE <= SIZEOF_USIZE {
|
||||
return Ok(GenericArray::clone_from_slice(
|
||||
&input.to_be_bytes()[SIZEOF_USIZE - L::USIZE..],
|
||||
));
|
||||
}
|
||||
|
||||
let mut output = GenericArray::default();
|
||||
output[L::USIZE - SIZEOF_USIZE..L::USIZE].copy_from_slice(&input.to_be_bytes());
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// Simplifies handling of [`serialize()`] output and implements [`Iterator`].
|
||||
pub(crate) struct Serialized<'a, L1: ArrayLength<u8>, L2: ArrayLength<u8>> {
|
||||
octet: GenericArray<u8, L1>,
|
||||
input: Input<'a, L2>,
|
||||
}
|
||||
|
||||
enum Input<'a, L: ArrayLength<u8>> {
|
||||
Owned(GenericArray<u8, L>),
|
||||
Borrowed(&'a [u8]),
|
||||
}
|
||||
|
||||
impl<'a, L1: ArrayLength<u8>, L2: ArrayLength<u8>> IntoIterator for &'a Serialized<'a, L1, L2> {
|
||||
type Item = &'a [u8];
|
||||
|
||||
type IntoIter = IntoIter<&'a [u8], 2>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
IntoIter::new([
|
||||
&self.octet,
|
||||
match self.input {
|
||||
Input::Owned(ref bytes) => bytes,
|
||||
Input::Borrowed(bytes) => bytes,
|
||||
},
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
// Computes I2OSP(len(input), max_bytes) || input
|
||||
pub(crate) fn serialize<L: ArrayLength<u8>>(
|
||||
input: &[u8],
|
||||
) -> Result<Serialized<L, U0>, InternalError> {
|
||||
Ok(Serialized {
|
||||
octet: i2osp::<L>(input.len())?,
|
||||
input: Input::Borrowed(input),
|
||||
})
|
||||
}
|
||||
|
||||
// Variation of `serialize` that takes an owned `input`
|
||||
pub(crate) fn serialize_owned<L1: ArrayLength<u8>, L2: ArrayLength<u8>>(
|
||||
input: GenericArray<u8, L2>,
|
||||
) -> Result<Serialized<'static, L1, L2>, InternalError> {
|
||||
Ok(Serialized {
|
||||
octet: i2osp::<L1>(input.len())?,
|
||||
input: Input::Owned(input),
|
||||
})
|
||||
}
|
||||
|
||||
macro_rules! chain_name {
|
||||
($var:ident, $mod:ident) => {
|
||||
$mod
|
||||
};
|
||||
($var:ident) => {
|
||||
$var
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! chain_skip {
|
||||
($var:ident, $feed:expr) => {
|
||||
$feed
|
||||
};
|
||||
($var:ident) => {
|
||||
&$var
|
||||
};
|
||||
}
|
||||
|
||||
/// The purpose of this macro is to simplify [`concat`](alloc::slice::Concat::concat)ing
|
||||
/// slices into an [`Iterator`] to avoid allocation
|
||||
macro_rules! chain {
|
||||
(
|
||||
$var:ident,
|
||||
$item1:expr $(=> |$mod1:ident| $feed1:expr)?,
|
||||
$($item2:expr $(=> |$mod2:ident| $feed2:expr)?),+$(,)?
|
||||
) => {
|
||||
let chain_name!(__temp$(, $mod1)?) = $item1;
|
||||
let $var = (chain_skip!(__temp$(, $feed1)?)).into_iter();
|
||||
$(
|
||||
let chain_name!(__temp$(, $mod2)?) = $item2;
|
||||
let $var = $var.chain(chain_skip!(__temp$(, $feed2)?));
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_tests {
|
||||
use super::*;
|
||||
use crate::voprf::{
|
||||
BlindedElement, EvaluationElement, NonVerifiableClient, NonVerifiableServer, Proof,
|
||||
VerifiableClient, VerifiableServer,
|
||||
};
|
||||
use curve25519_dalek::ristretto::RistrettoPoint;
|
||||
use generic_array::typenum::{U1, U2};
|
||||
use proptest::{collection::vec, prelude::*};
|
||||
use sha2::Sha512;
|
||||
|
||||
// Test the error condition for I2OSP
|
||||
#[test]
|
||||
fn test_i2osp_err_check() {
|
||||
assert!(i2osp::<U1>(0).is_ok());
|
||||
|
||||
assert!(i2osp::<U1>(255).is_ok());
|
||||
assert!(i2osp::<U1>(256).is_err());
|
||||
assert!(i2osp::<U1>(257).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());
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_nocrash_nonverifiable_client(bytes in vec(any::<u8>(), 0..200)) {
|
||||
NonVerifiableClient::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_verifiable_client(bytes in vec(any::<u8>(), 0..200)) {
|
||||
VerifiableClient::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_nonverifiable_server(bytes in vec(any::<u8>(), 0..200)) {
|
||||
NonVerifiableServer::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_verifiable_server(bytes in vec(any::<u8>(), 0..200)) {
|
||||
VerifiableServer::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_blinded_element(bytes in vec(any::<u8>(), 0..200)) {
|
||||
BlindedElement::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_evaluation_element(bytes in vec(any::<u8>(), 0..200)) {
|
||||
EvaluationElement::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_proof(bytes in vec(any::<u8>(), 0..200)) {
|
||||
Proof::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
}
|
||||
}
|
||||
+76
-71
@@ -10,7 +10,7 @@
|
||||
use crate::{
|
||||
errors::InternalError,
|
||||
group::Group,
|
||||
serialization::{i2osp, serialize},
|
||||
util::{i2osp, serialize, serialize_owned},
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use core::convert::TryInto;
|
||||
@@ -348,7 +348,7 @@ impl<G: Group, H: BlockInput + Digest> NonVerifiableServer<G, H> {
|
||||
pub fn new_from_seed(seed: &[u8]) -> Result<Self, InternalError> {
|
||||
let dst =
|
||||
GenericArray::from(*STR_HASH_TO_SCALAR).concat(get_context_string::<G>(Mode::Base)?);
|
||||
let sk = G::hash_to_scalar::<H, _>(seed, dst)?;
|
||||
let sk = G::hash_to_scalar::<H, _, _>(Some(seed), dst)?;
|
||||
Ok(Self {
|
||||
sk,
|
||||
hash: PhantomData,
|
||||
@@ -368,15 +368,15 @@ impl<G: Group, H: BlockInput + Digest> NonVerifiableServer<G, H> {
|
||||
blinded_element: BlindedElement<G, H>,
|
||||
metadata: Option<&[u8]>,
|
||||
) -> Result<NonVerifiableServerEvaluateResult<G, H>, InternalError> {
|
||||
let context = [
|
||||
STR_CONTEXT,
|
||||
&get_context_string::<G>(Mode::Base)?,
|
||||
&serialize::<U2>(metadata.unwrap_or_default())?,
|
||||
]
|
||||
.concat();
|
||||
chain!(
|
||||
context,
|
||||
STR_CONTEXT => |x| Some(x),
|
||||
get_context_string::<G>(Mode::Base)? => |x| Some(x.as_slice()),
|
||||
serialize::<U2>(metadata.unwrap_or_default())?,
|
||||
);
|
||||
let dst =
|
||||
GenericArray::from(*STR_HASH_TO_SCALAR).concat(get_context_string::<G>(Mode::Base)?);
|
||||
let m = G::hash_to_scalar::<H, _>(&context, dst)?;
|
||||
let m = G::hash_to_scalar::<H, _, _>(context, dst)?;
|
||||
let t = self.sk + &m;
|
||||
let evaluation_element = blinded_element.value * &G::scalar_invert(&t);
|
||||
Ok(NonVerifiableServerEvaluateResult {
|
||||
@@ -415,7 +415,7 @@ impl<G: Group, H: BlockInput + Digest> VerifiableServer<G, H> {
|
||||
pub fn new_from_seed(seed: &[u8]) -> Result<Self, InternalError> {
|
||||
let dst = GenericArray::from(*STR_HASH_TO_SCALAR)
|
||||
.concat(get_context_string::<G>(Mode::Verifiable)?);
|
||||
let sk = G::hash_to_scalar::<H, _>(seed, dst)?;
|
||||
let sk = G::hash_to_scalar::<H, _, _>(Some(seed), dst)?;
|
||||
let pk = G::base_point() * &sk;
|
||||
Ok(Self {
|
||||
sk,
|
||||
@@ -458,15 +458,14 @@ impl<G: Group, H: BlockInput + Digest> VerifiableServer<G, H> {
|
||||
&'a I: IntoIterator<Item = &'a BlindedElement<G, H>>,
|
||||
<&'a I as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let context = [
|
||||
STR_CONTEXT,
|
||||
&get_context_string::<G>(Mode::Verifiable)?,
|
||||
&serialize::<U2>(metadata.unwrap_or_default())?,
|
||||
]
|
||||
.concat();
|
||||
chain!(context,
|
||||
STR_CONTEXT => |x| Some(x),
|
||||
get_context_string::<G>(Mode::Verifiable)? => |x| Some(x.as_slice()),
|
||||
serialize::<U2>(metadata.unwrap_or_default())?,
|
||||
);
|
||||
let dst = GenericArray::from(*STR_HASH_TO_SCALAR)
|
||||
.concat(get_context_string::<G>(Mode::Verifiable)?);
|
||||
let m = G::hash_to_scalar::<H, _>(&context, dst)?;
|
||||
let m = G::hash_to_scalar::<H, _, _>(context, dst)?;
|
||||
let t = self.sk + &m;
|
||||
let evaluation_elements: Vec<EvaluationElement<G, H>> = blinded_elements
|
||||
.into_iter()
|
||||
@@ -639,16 +638,15 @@ where
|
||||
&'a I: IntoIterator<Item = BatchItems<G, H>>,
|
||||
<&'a I as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let context = [
|
||||
STR_CONTEXT,
|
||||
&get_context_string::<G>(Mode::Verifiable)?,
|
||||
&serialize::<U2>(info)?,
|
||||
]
|
||||
.concat();
|
||||
chain!(context,
|
||||
STR_CONTEXT => |x| Some(x),
|
||||
get_context_string::<G>(Mode::Verifiable)? => |x| Some(x.as_slice()),
|
||||
serialize::<U2>(info)?,
|
||||
);
|
||||
|
||||
let dst =
|
||||
GenericArray::from(*STR_HASH_TO_SCALAR).concat(get_context_string::<G>(Mode::Verifiable)?);
|
||||
let m = G::hash_to_scalar::<H, _>(&context, dst)?;
|
||||
let m = G::hash_to_scalar::<H, _, _>(context, dst)?;
|
||||
|
||||
let g = G::base_point();
|
||||
let t = g * &m;
|
||||
@@ -684,20 +682,20 @@ fn generate_proof<G: Group, H: BlockInput + Digest, R: RngCore + CryptoRng>(
|
||||
|
||||
let challenge_dst =
|
||||
GenericArray::from(*STR_CHALLENGE).concat(get_context_string::<G>(Mode::Verifiable)?);
|
||||
let h2_input = [
|
||||
serialize::<U2>(&b.to_arr())?,
|
||||
serialize::<U2>(&m.to_arr())?,
|
||||
serialize::<U2>(&z.to_arr())?,
|
||||
serialize::<U2>(&t2.to_arr())?,
|
||||
serialize::<U2>(&t3.to_arr())?,
|
||||
serialize::<U2>(&challenge_dst)?,
|
||||
]
|
||||
.concat();
|
||||
chain!(
|
||||
h2_input,
|
||||
serialize_owned::<U2, _>(b.to_arr())?,
|
||||
serialize_owned::<U2, _>(m.to_arr())?,
|
||||
serialize_owned::<U2, _>(z.to_arr())?,
|
||||
serialize_owned::<U2, _>(t2.to_arr())?,
|
||||
serialize_owned::<U2, _>(t3.to_arr())?,
|
||||
serialize_owned::<U2, _>(challenge_dst)?,
|
||||
);
|
||||
|
||||
let hash_to_scalar_dst =
|
||||
GenericArray::from(*STR_HASH_TO_SCALAR).concat(get_context_string::<G>(Mode::Verifiable)?);
|
||||
|
||||
let c_scalar = G::hash_to_scalar::<H, _>(&h2_input, hash_to_scalar_dst)?;
|
||||
let c_scalar = G::hash_to_scalar::<H, _, _>(h2_input, hash_to_scalar_dst)?;
|
||||
let s_scalar = r - &(c_scalar * &k);
|
||||
|
||||
Ok(Proof {
|
||||
@@ -721,19 +719,19 @@ fn verify_proof<G: Group, H: BlockInput + Digest>(
|
||||
|
||||
let challenge_dst =
|
||||
GenericArray::from(*STR_CHALLENGE).concat(get_context_string::<G>(Mode::Verifiable)?);
|
||||
let h2_input = [
|
||||
serialize::<U2>(&b.to_arr())?,
|
||||
serialize::<U2>(&m.to_arr())?,
|
||||
serialize::<U2>(&z.to_arr())?,
|
||||
serialize::<U2>(&t2.to_arr())?,
|
||||
serialize::<U2>(&t3.to_arr())?,
|
||||
serialize::<U2>(&challenge_dst)?,
|
||||
]
|
||||
.concat();
|
||||
chain!(
|
||||
h2_input,
|
||||
serialize_owned::<U2, _>(b.to_arr())?,
|
||||
serialize_owned::<U2, _>(m.to_arr())?,
|
||||
serialize_owned::<U2, _>(z.to_arr())?,
|
||||
serialize_owned::<U2, _>(t2.to_arr())?,
|
||||
serialize_owned::<U2, _>(t3.to_arr())?,
|
||||
serialize_owned::<U2, _>(challenge_dst)?,
|
||||
);
|
||||
|
||||
let hash_to_scalar_dst =
|
||||
GenericArray::from(*STR_HASH_TO_SCALAR).concat(get_context_string::<G>(Mode::Verifiable)?);
|
||||
let c = G::hash_to_scalar::<H, _>(&h2_input, hash_to_scalar_dst)?;
|
||||
let c = G::hash_to_scalar::<H, _, _>(h2_input, hash_to_scalar_dst)?;
|
||||
|
||||
match c.ct_eq(&proof.c_scalar).into() {
|
||||
true => Ok(()),
|
||||
@@ -755,15 +753,17 @@ fn finalize_after_unblind<
|
||||
|
||||
inputs_and_unblinded_elements
|
||||
.map(|(input, unblinded_element)| {
|
||||
Ok(<H as Digest>::digest(
|
||||
&[
|
||||
serialize::<U2>(input)?,
|
||||
serialize::<U2>(info)?,
|
||||
serialize::<U2>(&unblinded_element.to_arr())?,
|
||||
serialize::<U2>(&finalize_dst)?,
|
||||
]
|
||||
.concat(),
|
||||
))
|
||||
chain!(
|
||||
hash_input,
|
||||
serialize::<U2>(input)?,
|
||||
serialize::<U2>(info)?,
|
||||
serialize_owned::<U2, _>(unblinded_element.to_arr())?,
|
||||
serialize_owned::<U2, _>(finalize_dst)?,
|
||||
);
|
||||
|
||||
Ok(hash_input
|
||||
.fold(<H as Digest>::new(), |h, bytes| h.chain(bytes))
|
||||
.finalize())
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -782,24 +782,29 @@ fn compute_composites<G: Group, H: BlockInput + Digest>(
|
||||
let composite_dst =
|
||||
GenericArray::from(*STR_COMPOSITE).concat(get_context_string::<G>(Mode::Verifiable)?);
|
||||
|
||||
let h1_input = [serialize::<U2>(&b.to_arr())?, serialize::<U2>(&seed_dst)?].concat();
|
||||
let seed = <H as Digest>::digest(&h1_input);
|
||||
chain!(
|
||||
h1_input,
|
||||
serialize_owned::<U2, _>(b.to_arr())?,
|
||||
serialize_owned::<U2, _>(seed_dst)?,
|
||||
);
|
||||
let seed = h1_input
|
||||
.fold(<H as Digest>::new(), |h, bytes| h.chain(bytes))
|
||||
.finalize();
|
||||
|
||||
let mut m = G::identity();
|
||||
let mut z = G::identity();
|
||||
|
||||
for (i, (c, d)) in c_slice.zip(d_slice).enumerate() {
|
||||
let h2_input = [
|
||||
serialize::<U2>(&seed)?.as_slice(),
|
||||
&i2osp::<U2>(i)?,
|
||||
&serialize::<U2>(&c.value.to_arr())?,
|
||||
&serialize::<U2>(&d.value.to_arr())?,
|
||||
&serialize::<U2>(&composite_dst)?,
|
||||
]
|
||||
.concat();
|
||||
chain!(h2_input,
|
||||
serialize_owned::<U2, _>(seed.clone())?,
|
||||
i2osp::<U2>(i)? => |x| Some(x.as_slice()),
|
||||
serialize_owned::<U2, _>(c.value.to_arr())?,
|
||||
serialize_owned::<U2, _>(d.value.to_arr())?,
|
||||
serialize_owned::<U2, _>(composite_dst)?,
|
||||
);
|
||||
let dst = GenericArray::from(*STR_HASH_TO_SCALAR)
|
||||
.concat(get_context_string::<G>(Mode::Verifiable)?);
|
||||
let di = G::hash_to_scalar::<H, _>(&h2_input, dst)?;
|
||||
let di = G::hash_to_scalar::<H, _, _>(h2_input, dst)?;
|
||||
m = c.value * &di + &m;
|
||||
z = match k_option {
|
||||
Some(_) => z,
|
||||
@@ -846,15 +851,15 @@ mod tests {
|
||||
GenericArray::from(*STR_HASH_TO_GROUP).concat(get_context_string::<G>(mode).unwrap());
|
||||
let point = G::hash_to_curve::<H, _>(input, dst).unwrap();
|
||||
|
||||
let context = [
|
||||
STR_CONTEXT,
|
||||
&get_context_string::<G>(mode).unwrap(),
|
||||
&serialize::<U2>(info).unwrap(),
|
||||
]
|
||||
.concat();
|
||||
chain!(context,
|
||||
STR_CONTEXT => |x| Some(x),
|
||||
get_context_string::<G>(mode).unwrap() => |x| Some(x.as_slice()),
|
||||
serialize::<U2>(info).unwrap(),
|
||||
);
|
||||
|
||||
let dst =
|
||||
GenericArray::from(*STR_HASH_TO_SCALAR).concat(get_context_string::<G>(mode).unwrap());
|
||||
let m = <G as Group>::hash_to_scalar::<H, _>(&context, dst).unwrap();
|
||||
let m = <G as Group>::hash_to_scalar::<H, _, _>(context, dst).unwrap();
|
||||
|
||||
let res = point * &<G as Group>::scalar_invert(&(key + &m));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user