Compare commits

...
Author SHA1 Message Date
breakingbread 0bbf8fbfb5 chore: bump to v1.0.0-rc.1, reject trailing bytes, reject identity elemnts, more tests (#10)
Rust CI / cargo clippy (push) Successful in 26s
Rust CI / cargo fmt (push) Successful in 3s
Rust CI / test (stable / no backend / no frontend) (push) Successful in 1m20s
Rust CI / test (1.87.0 / no backend / no frontend) (push) Successful in 1m27s
Rust CI / test (1.87.0 / no backend / --features danger) (push) Successful in 1m30s
Rust CI / test (stable / no backend / --features danger) (push) Successful in 1m21s
Publish / publish (release) Successful in 19s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m34s
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 1m26s
Rust CI / test (stable / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m30s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features serde) (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 1m29s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m34s
Rust CI / cargo audit (push) Successful in 4s
Rust CI / no-std (thumbv6m-none-eabi / no backend) (push) Successful in 13s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (push) Successful in 13s
Rust CI / no-std (thumbv6m-none-eabi / --features ristretto255-ciphersuite) (push) Successful in 14s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (push) Successful in 14s
Reviewed-on: #10
Co-authored-by: UneBaguette <[email protected]>
Co-committed-by: UneBaguette <[email protected]>
2026-07-02 23:56:34 +02:00
breakingbread 1451f937ca chore: bump to v1.0.0-rc.0, implement zeroize for deps and license fix (#9)
Rust CI / cargo fmt (push) Successful in 3s
Rust CI / cargo clippy (push) Successful in 26s
Rust CI / test (1.87.0 / no backend / no frontend) (push) Successful in 1m23s
Rust CI / test (stable / no backend / --features serde) (push) Successful in 1m23s
Rust CI / test (stable / no backend / no frontend) (push) Successful in 1m17s
Rust CI / test (1.87.0 / no backend / --features danger) (push) Successful in 1m25s
Rust CI / test (stable / no backend / --features danger) (push) Successful in 1m19s
Rust CI / test (1.87.0 / no backend / --features serde) (push) Successful in 1m30s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m34s
Rust CI / test (stable / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m29s
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 1m30s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m34s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m27s
Rust CI / cargo audit (push) Successful in 4s
Rust CI / no-std (thumbv6m-none-eabi / no backend) (push) Successful in 12s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (push) Successful in 13s
Rust CI / no-std (thumbv6m-none-eabi / --features ristretto255-ciphersuite) (push) Successful in 11s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (push) Successful in 13s
Publish / publish (release) Successful in 17s
Reviewed-on: #9
Co-authored-by: UneBaguette <[email protected]>
Co-committed-by: UneBaguette <[email protected]>
2026-07-02 15:16:25 +02:00
breakingbread eb00b86000 chore: bump to v1.0.0-pre.1 and mostly cleanup stuff (#8)
Rust CI / cargo fmt (push) Successful in 3s
Rust CI / cargo clippy (push) Successful in 29s
Rust CI / test (1.87.0 / no backend / --features serde) (push) Successful in 1m25s
Rust CI / test (1.87.0 / no backend / no frontend) (push) Successful in 1m24s
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 1m25s
Rust CI / test (stable / no backend / --features danger) (push) Successful in 1m20s
Rust CI / test (stable / no backend / --features serde) (push) Successful in 1m20s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m34s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m35s
Rust CI / test (stable / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m29s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m28s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m34s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m29s
Rust CI / cargo audit (push) Successful in 5s
Rust CI / no-std (thumbv6m-none-eabi / --features ristretto255-ciphersuite) (push) Successful in 14s
Rust CI / no-std (thumbv6m-none-eabi / no backend) (push) Successful in 14s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (push) Successful in 14s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (push) Successful in 14s
Publish / publish (release) Successful in 18s
Reviewed-on: #8
Co-authored-by: UneBaguette <[email protected]>
Co-committed-by: UneBaguette <[email protected]>
2026-07-01 15:10:11 +02:00
20 changed files with 256 additions and 137 deletions
+17
View File
@@ -1,5 +1,22 @@
# Changelog # Changelog
## 1.0.0-rc.1 (July 3, 2026)
* Reject trailing bytes in all `deserialize` methods
* Reject identity element in `deterministic_blind_unchecked` to prevent blinding bypass
* Added roundtrip, trailing bytes, truncated, and empty input tests for serialization
## 1.0.0-rc.0 (July 2, 2026)
* Added missing license in Cargo manifest
* Implement `zeroize` feature for `digest`, `hybrid-array` and `sha2`
* Replaced license appendix in files while keeping original copyright
## 1.0.0-pre.1 (July 2, 2026)
* Simplified ciphersuite trait
* Moved multiplication operator to SecurityLevel type in Group trait
## 1.0.0-pre.0 (June 29, 2026) ## 1.0.0-pre.0 (June 29, 2026)
Forked from [facebook/voprf](https://github.com/facebook/voprf/) at `0.6.0-pre.1`. Forked from [facebook/voprf](https://github.com/facebook/voprf/) at `0.6.0-pre.1`.
+11 -11
View File
@@ -4,12 +4,12 @@ categories = ["no-std", "algorithms", "cryptography"]
description = "An implementation of a verifiable oblivious pseudorandom function (VOPRF)" description = "An implementation of a verifiable oblivious pseudorandom function (VOPRF)"
edition = "2024" edition = "2024"
keywords = ["oprf", "voprf", "cryptography", "oblivious-prf"] keywords = ["oprf", "voprf", "cryptography", "oblivious-prf"]
license = "MIT" license = "MIT OR Apache-2.0"
name = "voprf-vx" 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-pre.0" version = "1.0.0-rc.1"
[features] [features]
alloc = [] alloc = []
@@ -22,35 +22,35 @@ serde = ["curve25519-dalek?/serde", "hybrid-array/serde", "dep:serde"]
std = ["alloc"] std = ["alloc"]
[dependencies] [dependencies]
curve25519-dalek = { version = "5.0.0-rc", default-features = false, features = ["rand_core", "zeroize"], optional = true } curve25519-dalek = { version = "5.0.0-rc.1", 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 = "0.11" digest = { version = "0.11", features = ["zeroize"] }
displaydoc = { version = "0.2", default-features = false } displaydoc = { version = "0.2", default-features = false }
elliptic-curve = { version = "0.14", features = [ elliptic-curve = { version = "0.14", features = [
"sec1", "sec1",
] } ] }
hash2curve = "0.14" hash2curve = "0.14"
hybrid-array = "0.4" hybrid-array = { version = "0.4", features = ["zeroize"] }
rand_core = { version = "0.10", default-features = false, features = [] } rand_core = { version = "0.10", default-features = false }
serde = { version = "1", default-features = false, features = [ serde = { version = "1", default-features = false, features = [
"derive", "derive",
], optional = true } ], optional = true }
sha2 = { version = "0.11", default-features = false, optional = true } sha2 = { version = "0.11", default-features = false, features = ["zeroize"], optional = true }
p256 = { version = "0.14.0-rc", default-features = false, features = ["hash2curve", "oprf"], optional = true } p256 = { version = "0.14.0-rc.15", 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", default-features = false, features = [ p256 = { version = "0.14.0-rc.15", default-features = false, features = [
"hash2curve", "hash2curve",
"oprf", "oprf",
] } ] }
p384 = { version = "0.14.0-rc", default-features = false, features = [ p384 = { version = "0.14.0-rc.15", default-features = false, features = [
"hash2curve", "hash2curve",
"oprf", "oprf",
] } ] }
p521 = { version = "0.14.0-rc", default-features = false, features = [ p521 = { version = "0.14.0-rc.15", default-features = false, features = [
"hash2curve", "hash2curve",
"oprf", "oprf",
] } ] }
+1 -1
View File
@@ -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-pre.0" } voprf = { package = "voprf-vx", version = "1.0.0-rc.1" }
``` ```
### Minimum Supported Rust Version ### Minimum Supported Rust Version
+2 -9
View File
@@ -1,15 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates. // Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! Defines the CipherSuite trait to specify the underlying primitives for VOPRF //! Defines the CipherSuite trait to specify the underlying primitives for VOPRF
use crate::Group; use crate::Group;
use core::ops::Mul;
use digest::block_api::BlockSizeUser; use digest::block_api::BlockSizeUser;
use digest::typenum::{IsLess, IsLessOrEqual, U256}; use digest::typenum::{IsLess, IsLessOrEqual, U256};
use digest::{FixedOutput, HashMarker, OutputSizeUser}; use digest::{FixedOutput, HashMarker, OutputSizeUser};
@@ -20,7 +15,6 @@ use hybrid_array::typenum::{IsGreaterOrEqual, Prod, True, U2};
/// Configures the underlying primitives used in VOPRF /// Configures the underlying primitives used in VOPRF
pub trait CipherSuite pub trait CipherSuite
where where
<Self::Group as Group>::SecurityLevel: Mul<U2>,
<Self::Hash as OutputSizeUser>::OutputSize: ArraySize <Self::Hash as OutputSizeUser>::OutputSize: ArraySize
+ IsLess<U256> + IsLess<U256>
+ IsLessOrEqual<<Self::Hash as BlockSizeUser>::BlockSize, Output = True> + IsLessOrEqual<<Self::Hash as BlockSizeUser>::BlockSize, Output = True>
@@ -46,7 +40,6 @@ type OprfHash<T> =
impl<T: OprfParameters> CipherSuite for T impl<T: OprfParameters> CipherSuite for T
where where
T: Group, T: Group,
<T as Group>::SecurityLevel: Mul<U2>,
OprfHash<T>: BlockSizeUser + Default + FixedOutput + HashMarker, OprfHash<T>: BlockSizeUser + Default + FixedOutput + HashMarker,
<OprfHash<T> as OutputSizeUser>::OutputSize: ArraySize <OprfHash<T> as OutputSizeUser>::OutputSize: ArraySize
+ IsLess<U256> + IsLess<U256>
+8 -6
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates. // Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! Common functionality between multiple OPRF modes. //! Common functionality between multiple OPRF modes.
@@ -401,6 +397,12 @@ pub(crate) fn deterministic_blind_unchecked<CS: CipherSuite>(
mode: Mode, mode: Mode,
) -> Result<<CS::Group as Group>::Elem> { ) -> Result<<CS::Group as Group>::Elem> {
let hashed_point = hash_to_group::<CS>(input, mode)?; let hashed_point = hash_to_group::<CS>(input, mode)?;
// Identity element would nullify blinding, revealing the input.
if CS::Group::is_identity_elem(hashed_point).into() {
return Err(Error::Input);
}
Ok(hashed_point * blind) Ok(hashed_point * blind)
} }
+2 -6
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates. // Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! Errors which are produced during an execution of the protocol //! Errors which are produced during an execution of the protocol
+5 -9
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates. // Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
use core::ops::{Add, Mul}; use core::ops::{Add, Mul};
use digest::block_api::BlockSizeUser; use digest::block_api::BlockSizeUser;
@@ -67,9 +63,9 @@ where
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError> fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
where where
H: BlockSizeUser + Default + FixedOutput + HashMarker, H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize, Output = True>, H::OutputSize: IsLess<U256>
C::SecurityLevel: Mul<U2>, + IsLessOrEqual<H::BlockSize, Output = True>
H::OutputSize: IsGreaterOrEqual<Prod<C::SecurityLevel, U2>, Output = True>, + IsGreaterOrEqual<Prod<C::SecurityLevel, U2>, Output = True>,
{ {
hash_to_scalar::<C, ExpandMsgXmd<H>, <C as MapToCurve>::Length>(input, dst) hash_to_scalar::<C, ExpandMsgXmd<H>, <C as MapToCurve>::Length>(input, dst)
.map_err(|_| InternalError::Input) .map_err(|_| InternalError::Input)
+9 -13
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates. // Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! Defines the Group trait to specify the underlying prime order group //! Defines the Group trait to specify the underlying prime order group
@@ -64,7 +60,7 @@ where
/// Used to enforce `H::OutputSize >= 2 * SecurityLevel` in /// Used to enforce `H::OutputSize >= 2 * SecurityLevel` in
/// `hash_to_curve` and `hash_to_scalar`, which corresponds to the /// `hash_to_curve` and `hash_to_scalar`, which corresponds to the
/// `expand_message` requirement `len_in_bytes = 2 * k / 8`. /// `expand_message` requirement `len_in_bytes = 2 * k / 8`.
type SecurityLevel: ArraySize; type SecurityLevel: ArraySize + Mul<U2>;
/// Transforms a password and domain separation tag (DST) into a curve point /// Transforms a password and domain separation tag (DST) into a curve point
/// ///
@@ -74,9 +70,9 @@ where
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError> fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError>
where where
H: BlockSizeUser + Default + FixedOutput + HashMarker, H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize, Output = True>, H::OutputSize: IsLess<U256>
Self::SecurityLevel: Mul<U2>, + IsLessOrEqual<H::BlockSize, Output = True>
H::OutputSize: IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>; + IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>;
/// Hashes a slice of pseudo-random bytes to a scalar /// Hashes a slice of pseudo-random bytes to a scalar
/// ///
@@ -86,9 +82,9 @@ where
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError> fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
where where
H: BlockSizeUser + Default + FixedOutput + HashMarker, H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize, Output = True>, H::OutputSize: IsLess<U256>
Self::SecurityLevel: Mul<U2>, + IsLessOrEqual<H::BlockSize, Output = True>
H::OutputSize: IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>; + IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>;
/// Get the base point for the group /// Get the base point for the group
fn base_elem() -> Self::Elem; fn base_elem() -> Self::Elem;
+9 -13
View File
@@ -1,13 +1,9 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates. // Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
use core::num::NonZeroU16; use core::num::NonZeroU16;
use core::ops::Mul;
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT; use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
use curve25519_dalek::scalar::Scalar; use curve25519_dalek::scalar::Scalar;
@@ -54,9 +50,9 @@ impl Group for Ristretto255 {
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError> fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError>
where where
H: BlockSizeUser + Default + FixedOutput + HashMarker, H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize, Output = True>, H::OutputSize: IsLess<U256>
Self::SecurityLevel: Mul<U2>, + IsLessOrEqual<H::BlockSize, Output = True>
H::OutputSize: IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>, + IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>,
{ {
let mut uniform_bytes = [0u8; 64]; let mut uniform_bytes = [0u8; 64];
@@ -77,9 +73,9 @@ impl Group for Ristretto255 {
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError> fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
where where
H: BlockSizeUser + Default + FixedOutput + HashMarker, H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize, Output = True>, H::OutputSize: IsLess<U256>
Self::SecurityLevel: Mul<U2>, + IsLessOrEqual<H::BlockSize, Output = True>
H::OutputSize: IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>, + IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>,
{ {
let mut uniform_bytes = [0u8; 64]; let mut uniform_bytes = [0u8; 64];
+2 -6
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates. // Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! Includes a series of tests for the group implementations //! Includes a series of tests for the group implementations
+2 -6
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates. // Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! An implementation of a verifiable oblivious pseudorandom function (VOPRF) //! An implementation of a verifiable oblivious pseudorandom function (VOPRF)
//! //!
+2 -6
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates. // Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! Contains the main OPRF API //! Contains the main OPRF API
+2 -6
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates. // Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! Contains the main POPRF API //! Contains the main POPRF API
+172 -9
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates. // Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! Handles the serialization of each of the components used in the VOPRF //! Handles the serialization of each of the components used in the VOPRF
//! protocol //! protocol
@@ -38,6 +34,10 @@ impl<CS: CipherSuite> OprfClient<CS> {
pub fn deserialize(mut input: &[u8]) -> Result<Self> { pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let blind = deserialize_scalar::<CS::Group>(&mut input)?; let blind = deserialize_scalar::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self { blind }) Ok(Self { blind })
} }
} }
@@ -63,6 +63,10 @@ impl<CS: CipherSuite> VoprfClient<CS> {
let blind = deserialize_scalar::<CS::Group>(&mut input)?; let blind = deserialize_scalar::<CS::Group>(&mut input)?;
let blinded_element = deserialize_elem::<CS::Group>(&mut input)?; let blinded_element = deserialize_elem::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self { Ok(Self {
blind, blind,
blinded_element, blinded_element,
@@ -91,6 +95,10 @@ impl<CS: CipherSuite> PoprfClient<CS> {
let blind = deserialize_scalar::<CS::Group>(&mut input)?; let blind = deserialize_scalar::<CS::Group>(&mut input)?;
let blinded_element = deserialize_elem::<CS::Group>(&mut input)?; let blinded_element = deserialize_elem::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self { Ok(Self {
blind, blind,
blinded_element, blinded_element,
@@ -114,6 +122,10 @@ impl<CS: CipherSuite> OprfServer<CS> {
pub fn deserialize(mut input: &[u8]) -> Result<Self> { pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let sk = deserialize_scalar::<CS::Group>(&mut input)?; let sk = deserialize_scalar::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self { sk }) Ok(Self { sk })
} }
} }
@@ -138,6 +150,10 @@ impl<CS: CipherSuite> VoprfServer<CS> {
let sk = deserialize_scalar::<CS::Group>(&mut input)?; let sk = deserialize_scalar::<CS::Group>(&mut input)?;
let pk = deserialize_elem::<CS::Group>(&mut input)?; let pk = deserialize_elem::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self { sk, pk }) Ok(Self { sk, pk })
} }
} }
@@ -162,6 +178,10 @@ impl<CS: CipherSuite> PoprfServer<CS> {
let sk = deserialize_scalar::<CS::Group>(&mut input)?; let sk = deserialize_scalar::<CS::Group>(&mut input)?;
let pk = deserialize_elem::<CS::Group>(&mut input)?; let pk = deserialize_elem::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self { sk, pk }) Ok(Self { sk, pk })
} }
} }
@@ -187,6 +207,10 @@ impl<CS: CipherSuite> Proof<CS> {
let c_scalar = deserialize_scalar::<CS::Group>(&mut input)?; let c_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
let s_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 }) Ok(Proof { c_scalar, s_scalar })
} }
} }
@@ -207,6 +231,10 @@ impl<CS: CipherSuite> BlindedElement<CS> {
pub fn deserialize(mut input: &[u8]) -> Result<Self> { pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let value = deserialize_elem::<CS::Group>(&mut input)?; let value = deserialize_elem::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self(value)) Ok(Self(value))
} }
} }
@@ -227,6 +255,10 @@ impl<CS: CipherSuite> EvaluationElement<CS> {
pub fn deserialize(mut input: &[u8]) -> Result<Self> { pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let value = deserialize_elem::<CS::Group>(&mut input)?; let value = deserialize_elem::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self(value)) Ok(Self(value))
} }
} }
@@ -321,6 +353,7 @@ mod test {
VoprfClient, VoprfServer, VoprfClient, VoprfServer,
}; };
// Fuzz: no panics on arbitrary input
macro_rules! test_deserialize { macro_rules! test_deserialize {
($item:ident, $bytes:ident) => { ($item:ident, $bytes:ident) => {
#[cfg(feature = "ristretto255")] #[cfg(feature = "ristretto255")]
@@ -328,12 +361,43 @@ mod test {
let _ = $item::<crate::Ristretto255>::deserialize(&$bytes[..]); let _ = $item::<crate::Ristretto255>::deserialize(&$bytes[..]);
} }
let _ = $item::<p256::NistP256>::deserialize(&$bytes[..]); let _ = $item::<::p256::NistP256>::deserialize(&$bytes[..]);
let _ = $item::<p384::NistP384>::deserialize(&$bytes[..]); let _ = $item::<::p384::NistP384>::deserialize(&$bytes[..]);
let _ = $item::<p521::NistP521>::deserialize(&$bytes[..]); let _ = $item::<::p521::NistP521>::deserialize(&$bytes[..]);
}; };
} }
// Roundtrip: serialize to deserialize == original
macro_rules! test_roundtrip {
($item:ident, $cs:ty, $constructor:expr) => {{
let original = $constructor;
let bytes = original.serialize();
let recovered = $item::<$cs>::deserialize(&bytes).expect("roundtrip deserialize");
assert_eq!(original.serialize(), recovered.serialize());
}};
}
// Trailing bytes: valid serialization + extra byte must fail
macro_rules! test_trailing {
($item:ident, $cs:ty, $constructor:expr) => {{
let original = $constructor;
let bytes = original.serialize();
let mut extended = bytes.to_vec();
extended.push(0x00);
assert!($item::<$cs>::deserialize(&extended).is_err());
}};
}
// Truncated: valid serialization minus one byte must fail
macro_rules! test_truncated {
($item:ident, $cs:ty, $constructor:expr) => {{
let original = $constructor;
let bytes = original.serialize();
let truncated = &bytes[..bytes.len() - 1];
assert!($item::<$cs>::deserialize(truncated).is_err());
}};
}
proptest! { proptest! {
#[test] #[test]
fn test_nocrash_oprf_client(bytes in vec(any::<u8>(), 0..200)) { fn test_nocrash_oprf_client(bytes in vec(any::<u8>(), 0..200)) {
@@ -381,4 +445,103 @@ mod test {
test_deserialize!(Proof, bytes); test_deserialize!(Proof, bytes);
} }
} }
macro_rules! structured_tests {
($cs:ty, $mod:ident) => {
mod $mod {
use super::*;
use rand::rngs::SysRng;
#[test]
fn roundtrip_oprf_client() {
let client = OprfClient::<$cs>::blind(b"input", &mut SysRng)
.expect("blind")
.state;
test_roundtrip!(OprfClient, $cs, client);
}
#[test]
fn roundtrip_oprf_server() {
let server = OprfServer::<$cs>::new(&mut SysRng).expect("new");
test_roundtrip!(OprfServer, $cs, server);
}
#[test]
fn roundtrip_voprf_client() {
let client = VoprfClient::<$cs>::blind(b"input", &mut SysRng)
.expect("blind")
.state;
test_roundtrip!(VoprfClient, $cs, client);
}
#[test]
fn roundtrip_voprf_server() {
let server = VoprfServer::<$cs>::new(&mut SysRng).expect("new");
test_roundtrip!(VoprfServer, $cs, server);
}
#[test]
fn roundtrip_poprf_client() {
let client = PoprfClient::<$cs>::blind(b"input", &mut SysRng)
.expect("blind")
.state;
test_roundtrip!(PoprfClient, $cs, client);
}
#[test]
fn roundtrip_poprf_server() {
let server = PoprfServer::<$cs>::new(&mut SysRng).expect("new");
test_roundtrip!(PoprfServer, $cs, server);
}
#[test]
fn trailing_oprf_client() {
let client = OprfClient::<$cs>::blind(b"input", &mut SysRng)
.expect("blind")
.state;
test_trailing!(OprfClient, $cs, client);
}
#[test]
fn trailing_oprf_server() {
let server = OprfServer::<$cs>::new(&mut SysRng).expect("new");
test_trailing!(OprfServer, $cs, server);
}
#[test]
fn truncated_oprf_client() {
let client = OprfClient::<$cs>::blind(b"input", &mut SysRng)
.expect("blind")
.state;
test_truncated!(OprfClient, $cs, client);
}
#[test]
fn truncated_oprf_server() {
let server = OprfServer::<$cs>::new(&mut SysRng).expect("new");
test_truncated!(OprfServer, $cs, server);
}
#[test]
fn empty_input_fails() {
assert!(OprfClient::<$cs>::deserialize(&[]).is_err());
assert!(OprfServer::<$cs>::deserialize(&[]).is_err());
assert!(VoprfClient::<$cs>::deserialize(&[]).is_err());
assert!(VoprfServer::<$cs>::deserialize(&[]).is_err());
assert!(PoprfClient::<$cs>::deserialize(&[]).is_err());
assert!(PoprfServer::<$cs>::deserialize(&[]).is_err());
assert!(BlindedElement::<$cs>::deserialize(&[]).is_err());
assert!(EvaluationElement::<$cs>::deserialize(&[]).is_err());
assert!(Proof::<$cs>::deserialize(&[]).is_err());
}
}
};
}
#[cfg(feature = "ristretto255")]
structured_tests!(crate::Ristretto255, ristretto255);
structured_tests!(::p256::NistP256, p256);
structured_tests!(::p384::NistP384, p384);
structured_tests!(::p521::NistP521, p521);
} }
+2 -6
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates. // Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! The VOPRF test vectors taken from: //! The VOPRF test vectors taken from:
//! https://www.rfc-editor.org/rfc/rfc9497#appendix-A //! https://www.rfc-editor.org/rfc/rfc9497#appendix-A
+2 -6
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates. // Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
use alloc::vec::Vec; use alloc::vec::Vec;
use core::cmp::min; use core::cmp::min;
+2 -6
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates. // Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
mod cfrg_vectors; mod cfrg_vectors;
mod mock_rng; mod mock_rng;
+2 -6
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates. // Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
use alloc::string::{String, ToString}; use alloc::string::{String, ToString};
use alloc::vec::Vec; use alloc::vec::Vec;
+2 -6
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates. // Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
use alloc::string::String; use alloc::string::String;
use alloc::vec; use alloc::vec;
+2 -6
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates. // Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! Contains the main VOPRF API //! Contains the main VOPRF API