release: 1.0.0 (#12)
Rust CI / cargo fmt (push) Successful in 3s
Rust CI / cargo clippy (push) Successful in 28s
Rust CI / test (1.87.0 / no backend / no frontend) (push) Successful in 1m26s
Rust CI / test (stable / no backend / no frontend) (push) Successful in 1m18s
Rust CI / test (1.87.0 / no backend / --features danger) (push) Successful in 1m26s
Publish / publish (release) Successful in 17s
Rust CI / test (stable / no backend / --features danger) (push) Successful in 1m17s
Rust CI / test (1.87.0 / no backend / --features serde) (push) Successful in 1m30s
Rust CI / test (stable / no backend / --features serde) (push) Successful in 1m24s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m33s
Rust CI / test (stable / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m28s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m34s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m27s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m40s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m29s
Rust CI / cargo audit (push) Successful in 4s
Rust CI / no-std (thumbv6m-none-eabi / no backend) (push) Successful in 10s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (push) Successful in 11s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (push) Successful in 10s
Rust CI / no-std (thumbv6m-none-eabi / --features ristretto255-ciphersuite) (push) Successful in 13s
Rust CI / cargo fmt (push) Successful in 3s
Rust CI / cargo clippy (push) Successful in 28s
Rust CI / test (1.87.0 / no backend / no frontend) (push) Successful in 1m26s
Rust CI / test (stable / no backend / no frontend) (push) Successful in 1m18s
Rust CI / test (1.87.0 / no backend / --features danger) (push) Successful in 1m26s
Publish / publish (release) Successful in 17s
Rust CI / test (stable / no backend / --features danger) (push) Successful in 1m17s
Rust CI / test (1.87.0 / no backend / --features serde) (push) Successful in 1m30s
Rust CI / test (stable / no backend / --features serde) (push) Successful in 1m24s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m33s
Rust CI / test (stable / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m28s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m34s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m27s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m40s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m29s
Rust CI / cargo audit (push) Successful in 4s
Rust CI / no-std (thumbv6m-none-eabi / no backend) (push) Successful in 10s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (push) Successful in 11s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (push) Successful in 10s
Rust CI / no-std (thumbv6m-none-eabi / --features ristretto255-ciphersuite) (push) Successful in 13s
- Deduplicate serialization with impl_serde_scalar, impl_serde_elem, and impl_serde_scalar_elem macros - Move finalize_after_unblind to common.rs, shared by OPRF and VOPRF - Add shared test helpers (test_all_curves macro, prf function) - Update dependencies to stable releases Reviewed-on: #12 Co-authored-by: UneBaguette <[email protected]> Co-committed-by: UneBaguette <[email protected]>
This commit was merged in pull request #12.
This commit is contained in:
@@ -1,5 +1,12 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 1.0.0 (July 8, 2026)
|
||||||
|
|
||||||
|
* Deduplicated serialization with `impl_serde_scalar`, `impl_serde_elem`, and `impl_serde_scalar_elem` macros
|
||||||
|
* Moved `finalize_after_unblind` to `common.rs`, shared by OPRF and VOPRF
|
||||||
|
* Added shared test helpers (`test_all_curves` macro, `prf` function)
|
||||||
|
* Updated dependencies to stable releases
|
||||||
|
|
||||||
## 1.0.0-rc.1 (July 3, 2026)
|
## 1.0.0-rc.1 (July 3, 2026)
|
||||||
|
|
||||||
* Reject trailing bytes in all `deserialize` methods
|
* Reject trailing bytes in all `deserialize` methods
|
||||||
|
|||||||
Generated
+7
-7
@@ -346,9 +346,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.8.2"
|
version = "2.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
@@ -737,7 +737,7 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "voprf-vx"
|
name = "voprf-vx"
|
||||||
version = "1.0.0-rc.1"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"derive-where",
|
"derive-where",
|
||||||
@@ -813,18 +813,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.8.52"
|
version = "0.8.53"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f"
|
checksum = "75726053136156d419e285b9b7eddaaea9e3fea6ce32eed44a89901f0bd98de1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy-derive",
|
"zerocopy-derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy-derive"
|
name = "zerocopy-derive"
|
||||||
version = "0.8.52"
|
version = "0.8.53"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930"
|
checksum = "4714fd92cf900833d49538023a9b3915155210801d1c1169eba513b2addefd71"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|||||||
+5
-5
@@ -9,7 +9,7 @@ name = "voprf-vx"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/vexahub/voprf-vx/"
|
repository = "https://github.com/vexahub/voprf-vx/"
|
||||||
rust-version = "1.87"
|
rust-version = "1.87"
|
||||||
version = "1.0.0-rc.1"
|
version = "1.0.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
alloc = []
|
alloc = []
|
||||||
@@ -22,7 +22,7 @@ serde = ["curve25519-dalek?/serde", "hybrid-array/serde", "dep:serde"]
|
|||||||
std = ["alloc"]
|
std = ["alloc"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
curve25519-dalek = { version = "5.0.0-rc.1", default-features = false, features = ["rand_core", "zeroize"], optional = true }
|
curve25519-dalek = { version = "5", default-features = false, features = ["rand_core", "zeroize"], optional = true }
|
||||||
derive-where = { version = "1", features = ["zeroize-on-drop"] }
|
derive-where = { version = "1", features = ["zeroize-on-drop"] }
|
||||||
digest = { version = "0.11", features = ["zeroize"] }
|
digest = { version = "0.11", features = ["zeroize"] }
|
||||||
displaydoc = { version = "0.2", default-features = false }
|
displaydoc = { version = "0.2", default-features = false }
|
||||||
@@ -36,17 +36,17 @@ serde = { version = "1", default-features = false, features = [
|
|||||||
"derive",
|
"derive",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
sha2 = { version = "0.11", default-features = false, features = ["zeroize"], optional = true }
|
sha2 = { version = "0.11", default-features = false, features = ["zeroize"], optional = true }
|
||||||
p256 = { version = "0.14.0-rc.15", default-features = false, features = ["hash2curve", "oprf"], optional = true }
|
p256 = { version = "0.14", default-features = false, features = ["hash2curve", "oprf"], optional = true }
|
||||||
subtle = { version = "2.6", default-features = false }
|
subtle = { version = "2.6", default-features = false }
|
||||||
zeroize = { version = "1.5", default-features = false }
|
zeroize = { version = "1.5", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
p256 = { version = "0.14.0-rc.15", default-features = false, features = [
|
p256 = { version = "0.14", default-features = false, features = [
|
||||||
"hash2curve",
|
"hash2curve",
|
||||||
"oprf",
|
"oprf",
|
||||||
] }
|
] }
|
||||||
p384 = { version = "0.14.0-rc.15", default-features = false, features = [
|
p384 = { version = "0.14", default-features = false, features = [
|
||||||
"hash2curve",
|
"hash2curve",
|
||||||
"oprf",
|
"oprf",
|
||||||
] }
|
] }
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Installation
|
|||||||
Add the following line to the dependencies of your `Cargo.toml`:
|
Add the following line to the dependencies of your `Cargo.toml`:
|
||||||
|
|
||||||
```
|
```
|
||||||
voprf = { package = "voprf-vx", version = "1.0.0-rc.1" }
|
voprf-vx = "1.0.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Minimum Supported Rust Version
|
### Minimum Supported Rust Version
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
//! Common functionality between multiple OPRF modes.
|
//! Common functionality between multiple OPRF modes.
|
||||||
|
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
|
use core::iter::Map;
|
||||||
use core::ops::Add;
|
use core::ops::Add;
|
||||||
|
|
||||||
use derive_where::derive_where;
|
use derive_where::derive_where;
|
||||||
@@ -449,6 +450,33 @@ pub(crate) fn server_evaluate_hash_input<CS: CipherSuite>(
|
|||||||
.finalize())
|
.finalize())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) type FinalizeAfterUnblindResult<'a, C, I, IE> = Map<
|
||||||
|
IE,
|
||||||
|
fn((I, <<C as CipherSuite>::Group as Group>::Elem)) -> Result<Output<<C as CipherSuite>::Hash>>,
|
||||||
|
>;
|
||||||
|
|
||||||
|
/// Returned values can only fail with [`Error::Input`].
|
||||||
|
pub(crate) fn finalize_after_unblind<
|
||||||
|
'a,
|
||||||
|
CS: CipherSuite,
|
||||||
|
I: AsRef<[u8]>,
|
||||||
|
IE: 'a + Iterator<Item = (I, <CS::Group as Group>::Elem)>,
|
||||||
|
>(
|
||||||
|
inputs_and_unblinded_elements: IE,
|
||||||
|
) -> FinalizeAfterUnblindResult<'a, CS, I, IE> {
|
||||||
|
inputs_and_unblinded_elements.map(|(input, unblinded_element)| {
|
||||||
|
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
|
||||||
|
|
||||||
|
Ok(CS::Hash::new()
|
||||||
|
.chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?)
|
||||||
|
.chain_update(input.as_ref())
|
||||||
|
.chain_update(elem_len)
|
||||||
|
.chain_update(CS::Group::serialize_elem(unblinded_element))
|
||||||
|
.chain_update(STR_FINALIZE)
|
||||||
|
.finalize())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct Dst<L: ArraySize> {
|
pub(crate) struct Dst<L: ArraySize> {
|
||||||
dst_1: Array<u8, L>,
|
dst_1: Array<u8, L>,
|
||||||
dst_2: &'static [u8],
|
dst_2: &'static [u8],
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ pub enum Error {
|
|||||||
DeriveKeyPair,
|
DeriveKeyPair,
|
||||||
/// Failure to deserialize bytes
|
/// Failure to deserialize bytes
|
||||||
Deserialization,
|
Deserialization,
|
||||||
/// Batched items are more then [`u16::MAX`] or length don't match.
|
/// Batched items are more than [`u16::MAX`] or length don't match.
|
||||||
Batch,
|
Batch,
|
||||||
/// In verifiable mode, occurs when the proof failed to verify
|
/// In verifiable mode, occurs when the proof failed to verify
|
||||||
ProofVerification,
|
ProofVerification,
|
||||||
|
|||||||
+21
-20
@@ -54,16 +54,7 @@ impl Group for Ristretto255 {
|
|||||||
+ IsLessOrEqual<H::BlockSize, Output = True>
|
+ IsLessOrEqual<H::BlockSize, Output = True>
|
||||||
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>,
|
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>,
|
||||||
{
|
{
|
||||||
let mut uniform_bytes = [0u8; 64];
|
let uniform_bytes = expand_uniform_bytes::<H>(input, dst)?;
|
||||||
|
|
||||||
<ExpandMsgXmd<H> as ExpandMsg<U16>>::expand_message(
|
|
||||||
input,
|
|
||||||
dst,
|
|
||||||
NonZeroU16::new(64).unwrap(),
|
|
||||||
)
|
|
||||||
.map_err(|_| InternalError::Input)?
|
|
||||||
.fill_bytes(&mut uniform_bytes)
|
|
||||||
.map_err(|_| InternalError::Input)?;
|
|
||||||
|
|
||||||
Ok(RistrettoPoint::from_uniform_bytes(&uniform_bytes))
|
Ok(RistrettoPoint::from_uniform_bytes(&uniform_bytes))
|
||||||
}
|
}
|
||||||
@@ -77,16 +68,7 @@ impl Group for Ristretto255 {
|
|||||||
+ IsLessOrEqual<H::BlockSize, Output = True>
|
+ IsLessOrEqual<H::BlockSize, Output = True>
|
||||||
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>,
|
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>,
|
||||||
{
|
{
|
||||||
let mut uniform_bytes = [0u8; 64];
|
let uniform_bytes = expand_uniform_bytes::<H>(input, dst)?;
|
||||||
|
|
||||||
<ExpandMsgXmd<H> as ExpandMsg<U16>>::expand_message(
|
|
||||||
input,
|
|
||||||
dst,
|
|
||||||
NonZeroU16::new(64).unwrap(),
|
|
||||||
)
|
|
||||||
.map_err(|_| InternalError::Input)?
|
|
||||||
.fill_bytes(&mut uniform_bytes)
|
|
||||||
.map_err(|_| InternalError::Input)?;
|
|
||||||
|
|
||||||
Ok(Scalar::from_bytes_mod_order_wide(&uniform_bytes))
|
Ok(Scalar::from_bytes_mod_order_wide(&uniform_bytes))
|
||||||
}
|
}
|
||||||
@@ -150,3 +132,22 @@ impl Group for Ristretto255 {
|
|||||||
.ok_or(Error::Deserialization)
|
.ok_or(Error::Deserialization)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HELPERS
|
||||||
|
|
||||||
|
fn expand_uniform_bytes<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<[u8; 64], InternalError>
|
||||||
|
where
|
||||||
|
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||||
|
H::OutputSize: IsLess<U256>
|
||||||
|
+ IsLessOrEqual<H::BlockSize, Output = True>
|
||||||
|
+ IsGreaterOrEqual<Prod<U16, U2>, Output = True>,
|
||||||
|
{
|
||||||
|
let mut uniform_bytes = [0u8; 64];
|
||||||
|
|
||||||
|
<ExpandMsgXmd<H> as ExpandMsg<U16>>::expand_message(input, dst, NonZeroU16::new(64).unwrap())
|
||||||
|
.map_err(|_| InternalError::Input)?
|
||||||
|
.fill_bytes(&mut uniform_bytes)
|
||||||
|
.map_err(|_| InternalError::Input)?;
|
||||||
|
|
||||||
|
Ok(uniform_bytes)
|
||||||
|
}
|
||||||
|
|||||||
+15
-103
@@ -4,17 +4,16 @@
|
|||||||
|
|
||||||
//! Contains the main OPRF API
|
//! Contains the main OPRF API
|
||||||
|
|
||||||
use core::iter::{self, Map};
|
use core::iter::{self};
|
||||||
|
|
||||||
use derive_where::derive_where;
|
use derive_where::derive_where;
|
||||||
use digest::{Digest, Output};
|
use digest::Output;
|
||||||
use hybrid_array::Array;
|
use hybrid_array::Array;
|
||||||
use hybrid_array::typenum::Unsigned;
|
|
||||||
use rand_core::{TryCryptoRng, TryRng};
|
use rand_core::{TryCryptoRng, TryRng};
|
||||||
|
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
BlindedElement, EvaluationElement, Mode, STR_FINALIZE, derive_key_internal,
|
BlindedElement, EvaluationElement, Mode, derive_key_internal, deterministic_blind_unchecked,
|
||||||
deterministic_blind_unchecked, hash_to_group, i2osp_2, server_evaluate_hash_input,
|
finalize_after_unblind, hash_to_group, server_evaluate_hash_input,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use crate::serialization::serde::Scalar;
|
use crate::serialization::serde::Scalar;
|
||||||
@@ -120,7 +119,7 @@ impl<CS: CipherSuite> OprfClient<CS> {
|
|||||||
) -> Result<Output<CS::Hash>> {
|
) -> Result<Output<CS::Hash>> {
|
||||||
let unblinded_element = evaluation_element.0 * &CS::Group::invert_scalar(self.blind);
|
let unblinded_element = evaluation_element.0 * &CS::Group::invert_scalar(self.blind);
|
||||||
let mut outputs =
|
let mut outputs =
|
||||||
finalize_after_unblind::<CS, _, _>(iter::once((input, unblinded_element)), &[]);
|
finalize_after_unblind::<CS, _, _>(iter::once((input, unblinded_element)));
|
||||||
outputs.next().unwrap()
|
outputs.next().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,43 +216,6 @@ pub struct OprfClientBlindResult<CS: CipherSuite> {
|
|||||||
pub message: BlindedElement<CS>,
|
pub message: BlindedElement<CS>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////
|
|
||||||
// Inner functions //
|
|
||||||
// =============== //
|
|
||||||
/////////////////////
|
|
||||||
|
|
||||||
type FinalizeAfterUnblindResult<'a, C, I, IE> = Map<
|
|
||||||
IE,
|
|
||||||
fn((I, <<C as CipherSuite>::Group as Group>::Elem)) -> Result<Output<<C as CipherSuite>::Hash>>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
/// Returned values can only fail with [`Error::Input`].
|
|
||||||
fn finalize_after_unblind<
|
|
||||||
'a,
|
|
||||||
CS: CipherSuite,
|
|
||||||
I: AsRef<[u8]>,
|
|
||||||
IE: 'a + Iterator<Item = (I, <CS::Group as Group>::Elem)>,
|
|
||||||
>(
|
|
||||||
inputs_and_unblinded_elements: IE,
|
|
||||||
_unused: &'a [u8],
|
|
||||||
) -> FinalizeAfterUnblindResult<'a, CS, I, IE> {
|
|
||||||
inputs_and_unblinded_elements.map(|(input, unblinded_element)| {
|
|
||||||
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
|
|
||||||
|
|
||||||
// hashInput = I2OSP(len(input), 2) || input ||
|
|
||||||
// I2OSP(len(unblindedElement), 2) || unblindedElement ||
|
|
||||||
// "Finalize"
|
|
||||||
// return Hash(hashInput)
|
|
||||||
Ok(CS::Hash::new()
|
|
||||||
.chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?)
|
|
||||||
.chain_update(input.as_ref())
|
|
||||||
.chain_update(elem_len)
|
|
||||||
.chain_update(CS::Group::serialize_elem(unblinded_element))
|
|
||||||
.chain_update(STR_FINALIZE)
|
|
||||||
.finalize())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////
|
///////////
|
||||||
// Tests //
|
// Tests //
|
||||||
// ===== //
|
// ===== //
|
||||||
@@ -269,23 +231,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::Group;
|
use crate::Group;
|
||||||
use crate::common::{Dst, STR_HASH_TO_GROUP};
|
use crate::common::{Dst, STR_HASH_TO_GROUP};
|
||||||
|
use crate::tests::helpers::prf;
|
||||||
fn prf<CS: CipherSuite>(
|
|
||||||
input: &[u8],
|
|
||||||
key: <CS::Group as Group>::Scalar,
|
|
||||||
info: &[u8],
|
|
||||||
mode: Mode,
|
|
||||||
) -> Output<CS::Hash> {
|
|
||||||
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, mode);
|
|
||||||
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
|
|
||||||
|
|
||||||
let res = point * &key;
|
|
||||||
|
|
||||||
finalize_after_unblind::<CS, _, _>(iter::once((input, res)), info)
|
|
||||||
.next()
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn base_retrieval<CS: CipherSuite>() {
|
fn base_retrieval<CS: CipherSuite>() {
|
||||||
let input = b"input";
|
let input = b"input";
|
||||||
@@ -294,7 +240,7 @@ mod tests {
|
|||||||
let server = OprfServer::<CS>::new(&mut rng).unwrap();
|
let server = OprfServer::<CS>::new(&mut rng).unwrap();
|
||||||
let message = server.blind_evaluate(&client_blind_result.message);
|
let message = server.blind_evaluate(&client_blind_result.message);
|
||||||
let client_finalize_result = client_blind_result.state.finalize(input, &message).unwrap();
|
let client_finalize_result = client_blind_result.state.finalize(input, &message).unwrap();
|
||||||
let res2 = prf::<CS>(input, server.get_private_key(), &[], Mode::Oprf);
|
let res2 = prf::<CS>(input, server.get_private_key(), Mode::Oprf);
|
||||||
assert_eq!(client_finalize_result, res2);
|
assert_eq!(client_finalize_result, res2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,7 +256,7 @@ mod tests {
|
|||||||
|
|
||||||
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, Mode::Oprf);
|
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, Mode::Oprf);
|
||||||
let point = CS::Group::hash_to_curve::<CS::Hash>(&[&input], &dst.as_dst()).unwrap();
|
let point = CS::Group::hash_to_curve::<CS::Hash>(&[&input], &dst.as_dst()).unwrap();
|
||||||
let res2 = finalize_after_unblind::<CS, _, _>(iter::once((input.as_ref(), point)), &[])
|
let res2 = finalize_after_unblind::<CS, _, _>(iter::once((input.as_ref(), point)))
|
||||||
.next()
|
.next()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -371,45 +317,11 @@ mod tests {
|
|||||||
assert!(message.serialize().iter().all(|&x| x == 0));
|
assert!(message.serialize().iter().all(|&x| x == 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
crate::tests::test_all_curves!(
|
||||||
fn test_functionality() -> Result<()> {
|
base_retrieval,
|
||||||
use p256::NistP256;
|
base_inversion_unsalted,
|
||||||
use p384::NistP384;
|
server_evaluate,
|
||||||
use p521::NistP521;
|
zeroize_oprf_client,
|
||||||
|
zeroize_oprf_server,
|
||||||
#[cfg(feature = "ristretto255")]
|
);
|
||||||
{
|
|
||||||
use crate::Ristretto255;
|
|
||||||
|
|
||||||
base_retrieval::<Ristretto255>();
|
|
||||||
base_inversion_unsalted::<Ristretto255>();
|
|
||||||
server_evaluate::<Ristretto255>();
|
|
||||||
|
|
||||||
zeroize_oprf_client::<Ristretto255>();
|
|
||||||
zeroize_oprf_server::<Ristretto255>();
|
|
||||||
}
|
|
||||||
|
|
||||||
base_retrieval::<NistP256>();
|
|
||||||
base_inversion_unsalted::<NistP256>();
|
|
||||||
server_evaluate::<NistP256>();
|
|
||||||
|
|
||||||
zeroize_oprf_client::<NistP256>();
|
|
||||||
zeroize_oprf_server::<NistP256>();
|
|
||||||
|
|
||||||
base_retrieval::<NistP384>();
|
|
||||||
base_inversion_unsalted::<NistP384>();
|
|
||||||
server_evaluate::<NistP384>();
|
|
||||||
|
|
||||||
zeroize_oprf_client::<NistP384>();
|
|
||||||
zeroize_oprf_server::<NistP384>();
|
|
||||||
|
|
||||||
base_retrieval::<NistP521>();
|
|
||||||
base_inversion_unsalted::<NistP521>();
|
|
||||||
server_evaluate::<NistP521>();
|
|
||||||
|
|
||||||
zeroize_oprf_client::<NistP521>();
|
|
||||||
zeroize_oprf_server::<NistP521>();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-41
@@ -873,45 +873,11 @@ mod tests {
|
|||||||
assert!(proof.serialize().iter().all(|&x| x == 0));
|
assert!(proof.serialize().iter().all(|&x| x == 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
crate::tests::test_all_curves!(
|
||||||
fn test_functionality() -> Result<()> {
|
verifiable_retrieval,
|
||||||
use p256::NistP256;
|
verifiable_bad_public_key,
|
||||||
use p384::NistP384;
|
verifiable_server_evaluate,
|
||||||
use p521::NistP521;
|
zeroize_verifiable_client,
|
||||||
|
zeroize_verifiable_server,
|
||||||
#[cfg(feature = "ristretto255")]
|
);
|
||||||
{
|
|
||||||
use crate::Ristretto255;
|
|
||||||
|
|
||||||
verifiable_retrieval::<Ristretto255>();
|
|
||||||
verifiable_bad_public_key::<Ristretto255>();
|
|
||||||
verifiable_server_evaluate::<Ristretto255>();
|
|
||||||
|
|
||||||
zeroize_verifiable_client::<Ristretto255>();
|
|
||||||
zeroize_verifiable_server::<Ristretto255>();
|
|
||||||
}
|
|
||||||
|
|
||||||
verifiable_retrieval::<NistP256>();
|
|
||||||
verifiable_bad_public_key::<NistP256>();
|
|
||||||
verifiable_server_evaluate::<NistP256>();
|
|
||||||
|
|
||||||
zeroize_verifiable_client::<NistP256>();
|
|
||||||
zeroize_verifiable_server::<NistP256>();
|
|
||||||
|
|
||||||
verifiable_retrieval::<NistP384>();
|
|
||||||
verifiable_bad_public_key::<NistP384>();
|
|
||||||
verifiable_server_evaluate::<NistP384>();
|
|
||||||
|
|
||||||
zeroize_verifiable_client::<NistP384>();
|
|
||||||
zeroize_verifiable_server::<NistP384>();
|
|
||||||
|
|
||||||
verifiable_retrieval::<NistP521>();
|
|
||||||
verifiable_bad_public_key::<NistP521>();
|
|
||||||
verifiable_server_evaluate::<NistP521>();
|
|
||||||
|
|
||||||
zeroize_verifiable_client::<NistP521>();
|
|
||||||
zeroize_verifiable_server::<NistP521>();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+156
-249
@@ -13,255 +13,10 @@ use crate::{
|
|||||||
PoprfClient, PoprfServer, Proof, Result, VoprfClient, VoprfServer,
|
PoprfClient, PoprfServer, Proof, Result, VoprfClient, VoprfServer,
|
||||||
};
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////
|
/////////////////////////////
|
||||||
// Serialization and Deserialization for High-Level API //
|
// Deserialization Helpers //
|
||||||
// ==================================================== //
|
// ======================= //
|
||||||
//////////////////////////////////////////////////////////
|
/////////////////////////////
|
||||||
|
|
||||||
/// Length of [`OprfClient`] in bytes for serialization.
|
|
||||||
pub type OprfClientLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> OprfClient<CS> {
|
|
||||||
/// Serialization into bytes
|
|
||||||
pub fn serialize(&self) -> Array<u8, OprfClientLen<CS>> {
|
|
||||||
CS::Group::serialize_scalar(self.blind)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
|
||||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
|
||||||
let blind = deserialize_scalar::<CS::Group>(&mut input)?;
|
|
||||||
|
|
||||||
if !input.is_empty() {
|
|
||||||
return Err(Error::Deserialization);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self { blind })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Length of [`VoprfClient`] in bytes for serialization.
|
|
||||||
pub type VoprfClientLen<CS> = Sum<
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
|
||||||
>;
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> VoprfClient<CS> {
|
|
||||||
/// Serialization into bytes
|
|
||||||
pub fn serialize(&self) -> Array<u8, VoprfClientLen<CS>> {
|
|
||||||
<CS::Group as Group>::serialize_scalar(self.blind)
|
|
||||||
.concat(<CS::Group as Group>::serialize_elem(self.blinded_element))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
|
||||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
|
||||||
let blind = deserialize_scalar::<CS::Group>(&mut input)?;
|
|
||||||
let blinded_element = deserialize_elem::<CS::Group>(&mut input)?;
|
|
||||||
|
|
||||||
if !input.is_empty() {
|
|
||||||
return Err(Error::Deserialization);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
blind,
|
|
||||||
blinded_element,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Length of [`PoprfClient`] in bytes for serialization.
|
|
||||||
pub type PoprfClientLen<CS> = Sum<
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
|
||||||
>;
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> PoprfClient<CS> {
|
|
||||||
/// Serialization into bytes
|
|
||||||
pub fn serialize(&self) -> Array<u8, PoprfClientLen<CS>> {
|
|
||||||
<CS::Group as Group>::serialize_scalar(self.blind)
|
|
||||||
.concat(<CS::Group as Group>::serialize_elem(self.blinded_element))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
|
||||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
|
||||||
let blind = deserialize_scalar::<CS::Group>(&mut input)?;
|
|
||||||
let blinded_element = deserialize_elem::<CS::Group>(&mut input)?;
|
|
||||||
|
|
||||||
if !input.is_empty() {
|
|
||||||
return Err(Error::Deserialization);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
blind,
|
|
||||||
blinded_element,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Length of [`OprfServer`] in bytes for serialization.
|
|
||||||
pub type OprfServerLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> OprfServer<CS> {
|
|
||||||
/// Serialization into bytes
|
|
||||||
pub fn serialize(&self) -> Array<u8, OprfServerLen<CS>> {
|
|
||||||
CS::Group::serialize_scalar(self.sk)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
|
||||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
|
||||||
let sk = deserialize_scalar::<CS::Group>(&mut input)?;
|
|
||||||
|
|
||||||
if !input.is_empty() {
|
|
||||||
return Err(Error::Deserialization);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self { sk })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Length of [`VoprfServer`] in bytes for serialization.
|
|
||||||
pub type VoprfServerLen<CS> = Sum<
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
|
||||||
>;
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> VoprfServer<CS> {
|
|
||||||
/// Serialization into bytes
|
|
||||||
pub fn serialize(&self) -> Array<u8, VoprfServerLen<CS>> {
|
|
||||||
CS::Group::serialize_scalar(self.sk).concat(CS::Group::serialize_elem(self.pk))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
|
||||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
|
||||||
let sk = deserialize_scalar::<CS::Group>(&mut input)?;
|
|
||||||
let pk = deserialize_elem::<CS::Group>(&mut input)?;
|
|
||||||
|
|
||||||
if !input.is_empty() {
|
|
||||||
return Err(Error::Deserialization);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self { sk, pk })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Length of [`PoprfServer`] in bytes for serialization.
|
|
||||||
pub type PoprfServerLen<CS> = Sum<
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
|
||||||
>;
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> PoprfServer<CS> {
|
|
||||||
/// Serialization into bytes
|
|
||||||
pub fn serialize(&self) -> Array<u8, PoprfServerLen<CS>> {
|
|
||||||
CS::Group::serialize_scalar(self.sk).concat(CS::Group::serialize_elem(self.pk))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
|
||||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
|
||||||
let sk = deserialize_scalar::<CS::Group>(&mut input)?;
|
|
||||||
let pk = deserialize_elem::<CS::Group>(&mut input)?;
|
|
||||||
|
|
||||||
if !input.is_empty() {
|
|
||||||
return Err(Error::Deserialization);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self { sk, pk })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Length of [`Proof`] in bytes for serialization.
|
|
||||||
pub type ProofLen<CS> = Sum<
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
|
||||||
>;
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> Proof<CS> {
|
|
||||||
/// Serialization into bytes
|
|
||||||
pub fn serialize(&self) -> Array<u8, ProofLen<CS>> {
|
|
||||||
CS::Group::serialize_scalar(self.c_scalar)
|
|
||||||
.concat(CS::Group::serialize_scalar(self.s_scalar))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
|
||||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
|
||||||
let c_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
|
|
||||||
let s_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
|
|
||||||
|
|
||||||
if !input.is_empty() {
|
|
||||||
return Err(Error::Deserialization);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Proof { c_scalar, s_scalar })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Length of [`BlindedElement`] in bytes for serialization.
|
|
||||||
pub type BlindedElementLen<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen;
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> BlindedElement<CS> {
|
|
||||||
/// Serialization into bytes
|
|
||||||
pub fn serialize(&self) -> Array<u8, BlindedElementLen<CS>> {
|
|
||||||
CS::Group::serialize_elem(self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
|
||||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
|
||||||
let value = deserialize_elem::<CS::Group>(&mut input)?;
|
|
||||||
|
|
||||||
if !input.is_empty() {
|
|
||||||
return Err(Error::Deserialization);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Length of [`EvaluationElement`] in bytes for serialization.
|
|
||||||
pub type EvaluationElementLen<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen;
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> EvaluationElement<CS> {
|
|
||||||
/// Serialization into bytes
|
|
||||||
pub fn serialize(&self) -> Array<u8, EvaluationElementLen<CS>> {
|
|
||||||
CS::Group::serialize_elem(self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
|
||||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
|
||||||
let value = deserialize_elem::<CS::Group>(&mut input)?;
|
|
||||||
|
|
||||||
if !input.is_empty() {
|
|
||||||
return Err(Error::Deserialization);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_elem<G: Group>(input: &mut &[u8]) -> Result<G::Elem> {
|
fn deserialize_elem<G: Group>(input: &mut &[u8]) -> Result<G::Elem> {
|
||||||
let input = input
|
let input = input
|
||||||
@@ -293,6 +48,158 @@ impl<T> SliceExt for [T] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
// Serialization Macros //
|
||||||
|
// ======================== //
|
||||||
|
//////////////////////////////
|
||||||
|
|
||||||
|
macro_rules! impl_serde_scalar {
|
||||||
|
($ty:ident, $len:ident, $field:ident) => {
|
||||||
|
/// Length in bytes for serialization.
|
||||||
|
pub type $len<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
|
||||||
|
|
||||||
|
impl<CS: CipherSuite> $ty<CS> {
|
||||||
|
/// Serialization into bytes
|
||||||
|
pub fn serialize(&self) -> Array<u8, $len<CS>> {
|
||||||
|
CS::Group::serialize_scalar(self.$field)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialization from bytes
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
||||||
|
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
||||||
|
let $field = deserialize_scalar::<CS::Group>(&mut input)?;
|
||||||
|
|
||||||
|
if !input.is_empty() {
|
||||||
|
return Err(Error::Deserialization);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { $field })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_serde_scalar_elem {
|
||||||
|
($ty:ident, $len:ident, $scalar_field:ident, $elem_field:ident) => {
|
||||||
|
/// Length in bytes for serialization.
|
||||||
|
pub type $len<CS> = Sum<
|
||||||
|
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
||||||
|
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
||||||
|
>;
|
||||||
|
|
||||||
|
impl<CS: CipherSuite> $ty<CS> {
|
||||||
|
/// Serialization into bytes
|
||||||
|
pub fn serialize(&self) -> Array<u8, $len<CS>> {
|
||||||
|
<CS::Group as Group>::serialize_scalar(self.$scalar_field)
|
||||||
|
.concat(<CS::Group as Group>::serialize_elem(self.$elem_field))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialization from bytes
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
||||||
|
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
||||||
|
let $scalar_field = deserialize_scalar::<CS::Group>(&mut input)?;
|
||||||
|
let $elem_field = deserialize_elem::<CS::Group>(&mut input)?;
|
||||||
|
|
||||||
|
if !input.is_empty() {
|
||||||
|
return Err(Error::Deserialization);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
$scalar_field,
|
||||||
|
$elem_field,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_serde_elem {
|
||||||
|
($ty:ident, $len:ident) => {
|
||||||
|
/// Length in bytes for serialization.
|
||||||
|
pub type $len<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen;
|
||||||
|
|
||||||
|
impl<CS: CipherSuite> $ty<CS> {
|
||||||
|
/// Serialization into bytes
|
||||||
|
pub fn serialize(&self) -> Array<u8, $len<CS>> {
|
||||||
|
CS::Group::serialize_elem(self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialization from bytes
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
||||||
|
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
||||||
|
let value = deserialize_elem::<CS::Group>(&mut input)?;
|
||||||
|
|
||||||
|
if !input.is_empty() {
|
||||||
|
return Err(Error::Deserialization);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////
|
||||||
|
// Serialization and Deserialization for High-Level API //
|
||||||
|
// ==================================================== //
|
||||||
|
//////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
impl_serde_scalar!(OprfClient, OprfClientLen, blind);
|
||||||
|
impl_serde_scalar!(OprfServer, OprfServerLen, sk);
|
||||||
|
|
||||||
|
impl_serde_elem!(BlindedElement, BlindedElementLen);
|
||||||
|
impl_serde_elem!(EvaluationElement, EvaluationElementLen);
|
||||||
|
|
||||||
|
impl_serde_scalar_elem!(VoprfClient, VoprfClientLen, blind, blinded_element);
|
||||||
|
impl_serde_scalar_elem!(PoprfClient, PoprfClientLen, blind, blinded_element);
|
||||||
|
impl_serde_scalar_elem!(VoprfServer, VoprfServerLen, sk, pk);
|
||||||
|
impl_serde_scalar_elem!(PoprfServer, PoprfServerLen, sk, pk);
|
||||||
|
|
||||||
|
/////////////////////
|
||||||
|
// Proof (One-Off) //
|
||||||
|
// =============== //
|
||||||
|
/////////////////////
|
||||||
|
|
||||||
|
/// Length of [`Proof`] in bytes for serialization.
|
||||||
|
pub type ProofLen<CS> = Sum<
|
||||||
|
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
||||||
|
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
||||||
|
>;
|
||||||
|
|
||||||
|
impl<CS: CipherSuite> Proof<CS> {
|
||||||
|
/// Serialization into bytes
|
||||||
|
pub fn serialize(&self) -> Array<u8, ProofLen<CS>> {
|
||||||
|
CS::Group::serialize_scalar(self.c_scalar)
|
||||||
|
.concat(CS::Group::serialize_scalar(self.s_scalar))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialization from bytes
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
||||||
|
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
||||||
|
let c_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
|
||||||
|
let s_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
|
||||||
|
|
||||||
|
if !input.is_empty() {
|
||||||
|
return Err(Error::Deserialization);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Proof { c_scalar, s_scalar })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////
|
||||||
|
// Serde Support //
|
||||||
|
// ===================== //
|
||||||
|
///////////////////////////
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
pub(crate) mod serde {
|
pub(crate) mod serde {
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
// Copyright (c) VexaHub and contributors.
|
||||||
|
|
||||||
|
use core::iter;
|
||||||
|
|
||||||
|
use digest::Output;
|
||||||
|
|
||||||
|
use crate::common::{Dst, Mode, STR_HASH_TO_GROUP, finalize_after_unblind};
|
||||||
|
use crate::{CipherSuite, Group};
|
||||||
|
|
||||||
|
pub(crate) fn prf<CS: CipherSuite>(
|
||||||
|
input: &[u8],
|
||||||
|
key: <CS::Group as Group>::Scalar,
|
||||||
|
mode: Mode,
|
||||||
|
) -> Output<CS::Hash> {
|
||||||
|
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, mode);
|
||||||
|
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
|
||||||
|
|
||||||
|
let res = point * &key;
|
||||||
|
|
||||||
|
finalize_after_unblind::<CS, _, _>(iter::once((input, res)))
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
// Copyright (c) VexaHub and contributors.
|
||||||
|
|
||||||
|
macro_rules! test_all_curves {
|
||||||
|
($($test_fn:ident),+ $(,)?) => {
|
||||||
|
#[test]
|
||||||
|
fn test_functionality() -> $crate::Result<()> {
|
||||||
|
#[cfg(feature = "ristretto255")]
|
||||||
|
{
|
||||||
|
$( $test_fn::<$crate::Ristretto255>(); )+
|
||||||
|
}
|
||||||
|
$( $test_fn::<::p256::NistP256>(); )+
|
||||||
|
$( $test_fn::<::p384::NistP384>(); )+
|
||||||
|
$( $test_fn::<::p521::NistP521>(); )+
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use test_all_curves;
|
||||||
@@ -3,6 +3,11 @@
|
|||||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
|
||||||
mod cfrg_vectors;
|
mod cfrg_vectors;
|
||||||
|
mod macros;
|
||||||
mod mock_rng;
|
mod mock_rng;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod test_cfrg_vectors;
|
mod test_cfrg_vectors;
|
||||||
|
|
||||||
|
pub(crate) mod helpers;
|
||||||
|
|
||||||
|
pub(crate) use macros::test_all_curves;
|
||||||
|
|||||||
+15
-102
@@ -9,15 +9,14 @@ use alloc::vec::Vec;
|
|||||||
use core::iter::{self, Map, Repeat, Zip};
|
use core::iter::{self, Map, Repeat, Zip};
|
||||||
|
|
||||||
use derive_where::derive_where;
|
use derive_where::derive_where;
|
||||||
use digest::{Digest, Output};
|
use digest::Output;
|
||||||
use hybrid_array::Array;
|
use hybrid_array::Array;
|
||||||
use hybrid_array::typenum::Unsigned;
|
|
||||||
use rand_core::{TryCryptoRng, TryRng};
|
use rand_core::{TryCryptoRng, TryRng};
|
||||||
|
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
BlindedElement, EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE,
|
BlindedElement, EvaluationElement, FinalizeAfterUnblindResult, Mode, PreparedEvaluationElement,
|
||||||
derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2,
|
Proof, derive_keypair, deterministic_blind_unchecked, finalize_after_unblind, generate_proof,
|
||||||
server_evaluate_hash_input, verify_proof,
|
hash_to_group, server_evaluate_hash_input, verify_proof,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use crate::serialization::serde::{Element, Scalar};
|
use crate::serialization::serde::{Element, Scalar};
|
||||||
@@ -504,37 +503,6 @@ where
|
|||||||
.map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
|
.map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
|
||||||
}
|
}
|
||||||
|
|
||||||
type FinalizeAfterUnblindResult<'a, C, I, IE> = Map<
|
|
||||||
IE,
|
|
||||||
fn((I, <<C as CipherSuite>::Group as Group>::Elem)) -> Result<Output<<C as CipherSuite>::Hash>>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
/// Returned values can only fail with [`Error::Input`].
|
|
||||||
fn finalize_after_unblind<
|
|
||||||
'a,
|
|
||||||
CS: CipherSuite,
|
|
||||||
I: AsRef<[u8]>,
|
|
||||||
IE: 'a + Iterator<Item = (I, <CS::Group as Group>::Elem)>,
|
|
||||||
>(
|
|
||||||
inputs_and_unblinded_elements: IE,
|
|
||||||
) -> FinalizeAfterUnblindResult<'a, CS, I, IE> {
|
|
||||||
inputs_and_unblinded_elements.map(|(input, unblinded_element)| {
|
|
||||||
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
|
|
||||||
|
|
||||||
// hashInput = I2OSP(len(input), 2) || input ||
|
|
||||||
// I2OSP(len(unblindedElement), 2) || unblindedElement ||
|
|
||||||
// "Finalize"
|
|
||||||
// return Hash(hashInput)
|
|
||||||
Ok(CS::Hash::new()
|
|
||||||
.chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?)
|
|
||||||
.chain_update(input.as_ref())
|
|
||||||
.chain_update(elem_len)
|
|
||||||
.chain_update(CS::Group::serialize_elem(unblinded_element))
|
|
||||||
.chain_update(STR_FINALIZE)
|
|
||||||
.finalize())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////
|
///////////
|
||||||
// Tests //
|
// Tests //
|
||||||
// ===== //
|
// ===== //
|
||||||
@@ -551,22 +519,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::Group;
|
use crate::Group;
|
||||||
use crate::common::{Dst, STR_HASH_TO_GROUP};
|
use crate::common::{Dst, STR_HASH_TO_GROUP};
|
||||||
|
use crate::tests::helpers::prf;
|
||||||
fn prf<CS: CipherSuite>(
|
|
||||||
input: &[u8],
|
|
||||||
key: <CS::Group as Group>::Scalar,
|
|
||||||
mode: Mode,
|
|
||||||
) -> Output<CS::Hash> {
|
|
||||||
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, mode);
|
|
||||||
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
|
|
||||||
|
|
||||||
let res = point * &key;
|
|
||||||
|
|
||||||
finalize_after_unblind::<CS, _, _>(iter::once((input, res)))
|
|
||||||
.next()
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verifiable_retrieval<CS: CipherSuite>() {
|
fn verifiable_retrieval<CS: CipherSuite>() {
|
||||||
let input = b"input";
|
let input = b"input";
|
||||||
@@ -713,7 +666,7 @@ mod tests {
|
|||||||
// inputs
|
// inputs
|
||||||
let wrong_input = b"wrong input";
|
let wrong_input = b"wrong input";
|
||||||
let server_evaluate = server.evaluate(wrong_input).unwrap();
|
let server_evaluate = server.evaluate(wrong_input).unwrap();
|
||||||
assert!(client_finalize != server_evaluate);
|
assert_ne!(client_finalize, server_evaluate);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zeroize_voprf_client<CS: CipherSuite>() {
|
fn zeroize_voprf_client<CS: CipherSuite>() {
|
||||||
@@ -750,53 +703,13 @@ mod tests {
|
|||||||
assert!(proof.serialize().iter().all(|&x| x == 0));
|
assert!(proof.serialize().iter().all(|&x| x == 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
crate::tests::test_all_curves!(
|
||||||
fn test_functionality() -> Result<()> {
|
verifiable_retrieval,
|
||||||
use p256::NistP256;
|
verifiable_batch_retrieval,
|
||||||
use p384::NistP384;
|
verifiable_bad_public_key,
|
||||||
use p521::NistP521;
|
verifiable_batch_bad_public_key,
|
||||||
|
verifiable_server_evaluate,
|
||||||
#[cfg(feature = "ristretto255")]
|
zeroize_voprf_client,
|
||||||
{
|
zeroize_voprf_server,
|
||||||
use crate::Ristretto255;
|
);
|
||||||
|
|
||||||
verifiable_retrieval::<Ristretto255>();
|
|
||||||
verifiable_batch_retrieval::<Ristretto255>();
|
|
||||||
verifiable_bad_public_key::<Ristretto255>();
|
|
||||||
verifiable_batch_bad_public_key::<Ristretto255>();
|
|
||||||
verifiable_server_evaluate::<Ristretto255>();
|
|
||||||
|
|
||||||
zeroize_voprf_client::<Ristretto255>();
|
|
||||||
zeroize_voprf_server::<Ristretto255>();
|
|
||||||
}
|
|
||||||
|
|
||||||
verifiable_retrieval::<NistP256>();
|
|
||||||
verifiable_batch_retrieval::<NistP256>();
|
|
||||||
verifiable_bad_public_key::<NistP256>();
|
|
||||||
verifiable_batch_bad_public_key::<NistP256>();
|
|
||||||
verifiable_server_evaluate::<NistP256>();
|
|
||||||
|
|
||||||
zeroize_voprf_client::<NistP256>();
|
|
||||||
zeroize_voprf_server::<NistP256>();
|
|
||||||
|
|
||||||
verifiable_retrieval::<NistP384>();
|
|
||||||
verifiable_batch_retrieval::<NistP384>();
|
|
||||||
verifiable_bad_public_key::<NistP384>();
|
|
||||||
verifiable_batch_bad_public_key::<NistP384>();
|
|
||||||
verifiable_server_evaluate::<NistP384>();
|
|
||||||
|
|
||||||
zeroize_voprf_client::<NistP384>();
|
|
||||||
zeroize_voprf_server::<NistP384>();
|
|
||||||
|
|
||||||
verifiable_retrieval::<NistP521>();
|
|
||||||
verifiable_batch_retrieval::<NistP521>();
|
|
||||||
verifiable_bad_public_key::<NistP521>();
|
|
||||||
verifiable_batch_bad_public_key::<NistP521>();
|
|
||||||
verifiable_server_evaluate::<NistP521>();
|
|
||||||
|
|
||||||
zeroize_voprf_client::<NistP521>();
|
|
||||||
zeroize_voprf_server::<NistP521>();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user