Compare commits

..
1 Commits
24 changed files with 256 additions and 541 deletions
-102
View File
@@ -1,102 +0,0 @@
name: Rust CI
on:
push:
branches:
- master
pull_request:
types: [opened, reopened, synchronize]
concurrency:
group: ci-${{ gitea.ref }}
cancel-in-progress: true
jobs:
fmt:
name: cargo fmt
runs-on: linux_amd64
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Run cargo fmt
run: cargo fmt --all -- --check
clippy:
name: cargo clippy
runs-on: linux_amd64
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Run cargo clippy
run: cargo clippy --all-features --all-targets -- -D warnings
- name: Run cargo doc
run: cargo doc --no-deps --document-private-items --features danger,std
env:
RUSTDOCFLAGS: -D warnings
test:
name: test (${{ matrix.toolchain }} / ${{ matrix.backend_feature || 'no backend' }} / ${{ matrix.frontend_feature || 'no frontend' }})
runs-on: linux_amd64
strategy:
fail-fast: false
matrix:
backend_feature:
- --features ristretto255-ciphersuite
- ""
frontend_feature:
- ""
- --features danger
- --features serde
toolchain:
- stable
- "1.87.0"
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: dtolnay/rust-toolchain@${{ matrix.toolchain }}
- name: Run cargo test
run: cargo test --no-default-features --lib --tests ${{ matrix.backend_feature }}
- name: Run cargo test with alloc
run: cargo test --no-default-features --lib --tests ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }} --features alloc
- name: Run cargo test with std
run: cargo test --no-default-features --lib --tests ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }} --features std
- name: Run cargo test with all features
run: cargo test --all-features
build-no-std:
name: no-std (${{ matrix.target }} / ${{ matrix.backend_feature || 'no backend' }})
runs-on: linux_amd64
strategy:
fail-fast: false
matrix:
target:
- wasm32-unknown-unknown
- thumbv6m-none-eabi
backend_feature:
- ""
- --features ristretto255-ciphersuite
frontend_feature:
- ""
- --features danger
- --features serde
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Build no-std
run: cargo build --verbose --target=${{ matrix.target }} --no-default-features ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }}
audit:
name: cargo audit
runs-on: linux_amd64
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: dtolnay/rust-toolchain@stable
- name: Install cargo-audit
run: cargo install cargo-audit
- name: Run cargo audit
run: cargo audit -D warnings
-26
View File
@@ -1,26 +0,0 @@
name: Publish
on:
release:
types: [ published ]
jobs:
publish:
runs-on: linux_amd64
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: dtolnay/rust-toolchain@stable
- name: Login to crates.io
run: cargo login $CRATES_IO_TOKEN
env:
CRATES_IO_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
- name: Dry run publish
run: cargo publish --dry-run --manifest-path Cargo.toml
- name: Publish
run: cargo publish --manifest-path Cargo.toml
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
+10 -41
View File
@@ -1,41 +1,20 @@
# 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)
Forked from [facebook/voprf](https://github.com/facebook/voprf/) at `0.6.0-pre.1`.
## 0.6.0-rc.0 (June 27, 2026)
* MSRV bumped to 1.87
* Migrated from `elliptic-curve 0.13` to `0.14`
* Replaced `generic-array` with `hybrid-array 0.4`
* Updated `digest` to 0.11, `rand_core` to 0.10, `rand` to 0.10, `sha2` to 0.11
* Updated `p256`, `p384`, `p521` to `0.14`
* Replaced `generic-array` with `hybrid-array`
* Updated `digest` dependency to 0.11
* Updated `rand_core` dependency to 0.10
* Updated `rand` dependency to 0.10
* Updated `sha2` dependency to 0.11
* Updated `p256`, `p384`, `p521` dependencies to 0.14.0-rc
* Replaced `elliptic-curve/hash2curve` feature with standalone `hash2curve 0.14` crate
* Removed `VoprfParameters` dependency to be replaced with `OprfParameters` + `GroupDigest`
* Added `SecurityLevel` associated type to `Group` trait for generic hash bounds
* Added `OkmLen` associated type to `Group` trait (`MapToCurve::Length`)
* Updated `hash_to_scalar` to use `MapToCurve::Length` as OKM length per RFC 9380
* Updated `random_scalar` for deterministic byte consumption with `rand_core 0.10`
* Auto-impl `CipherSuite` for any `OprfParameters + Group` type via `OprfHash<T>`
* Updated `hash_to_scalar` to use `MapToCurve::Length` as OKM length per RFC 9380, replacing the removed `GroupDigest::hash_to_scalar` method
* Updated `random_scalar` to consume exactly `ScalarLen` bytes per attempt, adapting to the new `rand_core 0.10` API
* Added `OkmLen` associated type to `Group` trait
## 0.6.0-pre.1 (April 6, 2026)
* MSRV bumped to 1.85
* Updated rand_core dependency to 0.9
* Updated rand dependency to 0.9
@@ -43,50 +22,40 @@ Forked from [facebook/voprf](https://github.com/facebook/voprf/) at `0.6.0-pre.1
* Fixed docs issue
## 0.6.0-pre.0 (November 8, 2025)
* MSRV bumped to 1.83
* Updated Ristretto255 random scalar generation
* Updated generic-array to v1
## 0.5.0 (March 6, 2024)
* Just a version bump from v0.5.0-pre.7
## 0.5.0-pre.7 (January 11, 2024)
* Updated to be in sync with RFC 9497
## 0.5.0-pre.6 (July 24, 2023)
* Updated curve25519-dalek dependency to 4
## 0.5.0-pre.5 (June 27, 2023)
* Updated curve25519-dalek dependency to 4.0.0-rc.3
## 0.5.0-pre.4 (May 20, 2023)
* Updated curve25519-dalek dependency to 4.0.0-rc.2
## 0.5.0-pre.3 (March 4, 2023)
* Updated to be in sync with draft-irtf-cfrg-voprf-19
* Increased MSRV to 1.65
* Updated p256 dependency to v0.13
* Added p384 tests
## 0.5.0-pre.2 (February 3, 2023)
* Increased MSRV to 1.60
* Updated p256 dependency to v0.12
* Updated curve25519-dalek dependency to 4.0.0-rc.1
## 0.5.0-pre.1 (December 19, 2022)
* Updated curve25519-dalek dependency to 4.0.0-pre.5
## 0.4.0 (September 15, 2022)
* Updated to be in sync with draft-irtf-cfrg-voprf-11, with
the addition of the POPRF mode
* Adds the evaluate() function to the servers to calculate the output of the OPRF
+1 -1
View File
@@ -7,6 +7,6 @@ We use GitHub issues to track public bugs. Please ensure your description is
clear and has sufficient instructions to be able to reproduce the issue.
## License
By contributing to voprf-vx, you agree that your contributions will be
By contributing to voprf, you agree that your contributions will be
licensed under both the LICENSE-MIT and LICENSE-APACHE files in the root
directory of this source tree.
+15 -18
View File
@@ -1,20 +1,19 @@
[package]
authors = ["VexaHub Developers", "Kevin Lewi <[email protected]>"]
authors = ["Kevin Lewi <[email protected]>"]
categories = ["no-std", "algorithms", "cryptography"]
description = "An implementation of a verifiable oblivious pseudorandom function (VOPRF)"
edition = "2024"
keywords = ["oprf", "voprf", "cryptography", "oblivious-prf"]
license = "MIT OR Apache-2.0"
name = "voprf-vx"
edition = "2021"
keywords = ["oprf"]
license = "MIT"
name = "voprf"
readme = "README.md"
repository = "https://github.com/vexahub/voprf-vx/"
repository = "https://github.com/facebook/voprf/"
rust-version = "1.87"
version = "1.0.0-rc.1"
version = "0.6.0-rc.0"
[features]
alloc = []
danger = []
doctest = ["dep:p256", "dep:sha2"]
default = ["ristretto255-ciphersuite", "dep:serde"]
ristretto255 = ["dep:curve25519-dalek"]
ristretto255-ciphersuite = ["ristretto255", "dep:sha2"]
@@ -22,35 +21,34 @@ serde = ["curve25519-dalek?/serde", "hybrid-array/serde", "dep:serde"]
std = ["alloc"]
[dependencies]
curve25519-dalek = { version = "5.0.0-rc.1", default-features = false, features = ["rand_core", "zeroize"], optional = true }
curve25519-dalek = { version = "4", default-features = false, features = ["rand_core", "zeroize"], optional = true }
derive-where = { version = "1", features = ["zeroize-on-drop"] }
digest = { version = "0.11", features = ["zeroize"] }
digest = "0.11"
displaydoc = { version = "0.2", default-features = false }
elliptic-curve = { version = "0.14", features = [
"sec1",
] }
hash2curve = "0.14"
hybrid-array = { version = "0.4", features = ["zeroize"] }
rand_core = { version = "0.10", default-features = false }
hybrid-array = "0.4"
rand_core = { version = "0.10", default-features = false, features = [] }
serde = { version = "1", default-features = false, features = [
"derive",
], 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 }
sha2 = { version = "0.11", default-features = false, optional = true }
subtle = { version = "2.6", default-features = false }
zeroize = { version = "1.5", default-features = false }
[dev-dependencies]
hex = "0.4"
p256 = { version = "0.14.0-rc.15", default-features = false, features = [
p256 = { version = "0.14.0-rc", default-features = false, features = [
"hash2curve",
"oprf",
] }
p384 = { version = "0.14.0-rc.15", default-features = false, features = [
p384 = { version = "0.14.0-rc", default-features = false, features = [
"hash2curve",
"oprf",
] }
p521 = { version = "0.14.0-rc.15", default-features = false, features = [
p521 = { version = "0.14.0-rc", default-features = false, features = [
"hash2curve",
"oprf",
] }
@@ -64,4 +62,3 @@ sha2 = "0.11"
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
targets = []
features = ["doctest"]
+4 -6
View File
@@ -1,4 +1,4 @@
# voprf
# voprf ![Build Status](https://github.com/novifinancial/voprf/workflows/Rust%20CI/badge.svg)
An implementation of a (verifiable) oblivious pseudorandom function (VOPRF)
A VOPRF is a verifiable oblivious pseudorandom function, a protocol between a client and a server. The regular (non-verifiable) OPRF is also supported in this implementation.
@@ -8,7 +8,7 @@ This implementation is based on [RFC 9497](https://www.rfc-editor.org/rfc/rfc949
Documentation
-------------
The API can be found [here](https://docs.rs/voprf-vx/) along with an example for usage.
The API can be found [here](https://docs.rs/voprf/) along with an example for usage.
Installation
------------
@@ -16,7 +16,7 @@ Installation
Add the following line to the dependencies of your `Cargo.toml`:
```
voprf = { package = "voprf-vx", version = "1.0.0-rc.1" }
voprf = { package = "voprf-vexahub", version = "0.6.0-rc.0" }
```
### Minimum Supported Rust Version
@@ -26,9 +26,7 @@ Rust **1.87** or higher.
Contributors
------------
This is a fork of [facebook/voprf](https://github.com/facebook/voprf/) maintained by [VexaHub](https://github.com/vexahub).
The original author is Kevin Lewi ([@kevinlewi](https://github.com/kevinlewi)).
The author of this code is Kevin Lewi ([@kevinlewi](https://github.com/kevinlewi)).
To learn more about contributing to this project, [see this document](./CONTRIBUTING.md).
License
-30
View File
@@ -1,30 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"dependencyDashboard": true,
"osvVulnerabilityAlerts": true,
"rangeStrategy": "auto",
"packageRules": [
{
"matchManagers": [
"cargo"
],
"groupName": "rust deps"
},
{
"matchManagers": [
"cargo"
],
"matchUpdateTypes": [
"major"
],
"automerge": false
}
],
"lockFileMaintenance": {
"enabled": true
},
"configMigration": true
}
+11 -24
View File
@@ -1,20 +1,25 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// 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
use crate::Group;
use core::ops::Mul;
use digest::block_api::BlockSizeUser;
use digest::typenum::{IsLess, IsLessOrEqual, U256};
use digest::{FixedOutput, HashMarker, OutputSizeUser};
use hash2curve::{ExpandMsg, GroupDigest, MapToCurve, OprfParameters};
use hybrid_array::ArraySize;
use digest::{Digest, FixedOutput, HashMarker, OutputSizeUser};
use hybrid_array::typenum::{IsGreaterOrEqual, Prod, True, U2};
use hybrid_array::ArraySize;
/// Configures the underlying primitives used in VOPRF
pub trait CipherSuite
where
<Self::Group as Group>::SecurityLevel: Mul<U2>,
<Self::Hash as OutputSizeUser>::OutputSize: ArraySize
+ IsLess<U256>
+ IsLessOrEqual<<Self::Hash as BlockSizeUser>::BlockSize, Output = True>
@@ -30,23 +35,5 @@ where
/// The main hash function to use (for HKDF computations and hashing
/// transcripts).
type Hash: BlockSizeUser + Default + FixedOutput + HashMarker;
}
/// The hash function associated with a curve's OPRF `expand_message` implementation.
type OprfHash<T> =
<<T as GroupDigest>::ExpandMsg as ExpandMsg<<T as MapToCurve>::SecurityLevel>>::Hash;
impl<T: OprfParameters> CipherSuite for T
where
T: Group,
OprfHash<T>: BlockSizeUser + Default + FixedOutput + HashMarker,
<OprfHash<T> as OutputSizeUser>::OutputSize: ArraySize
+ IsLess<U256>
+ IsLessOrEqual<<OprfHash<T> as BlockSizeUser>::BlockSize, Output = True>
+ IsGreaterOrEqual<Prod<<T as Group>::SecurityLevel, U2>, Output = True>,
{
const ID: &'static [u8] = T::ID;
type Group = T;
type Hash = OprfHash<T>;
type Hash: Digest + BlockSizeUser + Default + FixedOutput + HashMarker;
}
+7 -9
View File
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// 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.
@@ -9,7 +13,7 @@ use core::ops::Add;
use derive_where::derive_where;
use digest::{Digest, Output, OutputSizeUser};
use hybrid_array::typenum::{IsLess, U2, U9, U256, Unsigned};
use hybrid_array::typenum::{IsLess, Unsigned, U2, U256, U9};
use hybrid_array::{Array, ArrayN, ArraySize};
use rand_core::{TryCryptoRng, TryRng};
use subtle::ConstantTimeEq;
@@ -397,12 +401,6 @@ pub(crate) fn deterministic_blind_unchecked<CS: CipherSuite>(
mode: Mode,
) -> Result<<CS::Group as Group>::Elem> {
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)
}
+6 -2
View File
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// 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
+17 -10
View File
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// 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 digest::block_api::BlockSizeUser;
@@ -12,20 +16,20 @@ use elliptic_curve::{
AffinePoint, Field, FieldBytes, FieldBytesSize, Group as _, ProjectivePoint, PublicKey, Scalar,
SecretKey,
};
use hash2curve::{ExpandMsgXmd, GroupDigest, MapToCurve, hash_to_scalar};
use hash2curve::{hash_to_scalar, ExpandMsgXmd, GroupDigest};
use hybrid_array::typenum::{IsGreaterOrEqual, Prod, Sum, True, U2};
use hybrid_array::{Array, ArraySize};
use rand_core::TryCryptoRng;
use super::Group;
use crate::{Error, InternalError, Result};
use crate::{CipherSuite, Error, InternalError, Result};
type ElemLen<C> = <ScalarLen<C> as ModulusSize>::CompressedPointSize;
type ScalarLen<C> = FieldBytesSize<C>;
impl<C> Group for C
where
C: GroupDigest,
C: GroupDigest + CipherSuite + hash2curve::MapToCurve,
C::SecurityLevel: Mul<U2>,
C::SecurityLevel: ArraySize,
<C::SecurityLevel as Mul<U2>>::Output: ArraySize,
@@ -33,7 +37,8 @@ where
ScalarLen<Self>: ModulusSize,
ScalarLen<Self>: ArraySize,
ScalarLen<Self>: hybrid_array::typenum::NonZero,
Scalar<Self>: elliptic_curve::ops::Reduce<Array<u8, <C as MapToCurve>::Length>>,
Scalar<Self>: elliptic_curve::ops::Reduce<Array<u8, ScalarLen<Self>>>,
Scalar<Self>: elliptic_curve::ops::Reduce<Array<u8, <C as hash2curve::MapToCurve>::Length>>,
AffinePoint<Self>: FromSec1Point<Self> + ToSec1Point<Self>,
// `VoprfClientLen`, `PoprfClientLen`, `VoprfServerLen`, `PoprfServerLen`
ScalarLen<Self>: Add<ElemLen<Self>>,
@@ -53,6 +58,8 @@ where
type SecurityLevel = C::SecurityLevel;
type OkmLen = <C as hash2curve::MapToCurve>::Length;
// Implements the `hash_to_curve()` function from
// https://www.rfc-editor.org/rfc/rfc9380.html#section-3
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError> {
@@ -63,11 +70,11 @@ where
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
where
H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess<U256>
+ IsLessOrEqual<H::BlockSize, Output = True>
+ IsGreaterOrEqual<Prod<C::SecurityLevel, U2>, Output = True>,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize, Output = True>,
C::SecurityLevel: Mul<U2>,
H::OutputSize: 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 hash2curve::MapToCurve>::Length>(input, dst)
.map_err(|_| InternalError::Input)
}
+16 -9
View File
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// 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
@@ -60,7 +64,10 @@ where
/// Used to enforce `H::OutputSize >= 2 * SecurityLevel` in
/// `hash_to_curve` and `hash_to_scalar`, which corresponds to the
/// `expand_message` requirement `len_in_bytes = 2 * k / 8`.
type SecurityLevel: ArraySize + Mul<U2>;
type SecurityLevel: ArraySize;
/// The OKM length for hash_to_scalar (>= ScalarLen, used by hash_to_field).
type OkmLen: ArraySize + hybrid_array::typenum::NonZero;
/// Transforms a password and domain separation tag (DST) into a curve point
///
@@ -70,9 +77,9 @@ where
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError>
where
H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess<U256>
+ IsLessOrEqual<H::BlockSize, Output = True>
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>;
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize, Output = True>,
Self::SecurityLevel: Mul<U2>,
H::OutputSize: IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>;
/// Hashes a slice of pseudo-random bytes to a scalar
///
@@ -82,9 +89,9 @@ where
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
where
H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess<U256>
+ IsLessOrEqual<H::BlockSize, Output = True>
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>;
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize, Output = True>,
Self::SecurityLevel: Mul<U2>,
H::OutputSize: IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>;
/// Get the base point for the group
fn base_elem() -> Self::Elem;
+17 -11
View File
@@ -1,9 +1,13 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// 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::ops::Mul;
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
use curve25519_dalek::scalar::Scalar;
@@ -11,10 +15,10 @@ use curve25519_dalek::traits::Identity;
use digest::block_api::BlockSizeUser;
use digest::{FixedOutput, HashMarker};
use hash2curve::{ExpandMsg, ExpandMsgXmd, Expander};
use hybrid_array::Array;
use hybrid_array::typenum::{
IsGreaterOrEqual, IsLess, IsLessOrEqual, Prod, True, U2, U16, U32, U256,
IsGreaterOrEqual, IsLess, IsLessOrEqual, Prod, True, U16, U2, U256, U32, U64,
};
use hybrid_array::Array;
use rand_core::{TryCryptoRng, TryRng};
use subtle::ConstantTimeEq;
@@ -45,14 +49,16 @@ impl Group for Ristretto255 {
type SecurityLevel = U16;
type OkmLen = U64;
// Implements the `hash_to_ristretto255()` function from
// https://www.rfc-editor.org/rfc/rfc9380.html#appendix-B
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError>
where
H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess<U256>
+ IsLessOrEqual<H::BlockSize, Output = True>
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize, Output = True>,
Self::SecurityLevel: Mul<U2>,
H::OutputSize: IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>,
{
let mut uniform_bytes = [0u8; 64];
@@ -73,9 +79,9 @@ impl Group for Ristretto255 {
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
where
H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess<U256>
+ IsLessOrEqual<H::BlockSize, Output = True>
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize, Output = True>,
Self::SecurityLevel: Mul<U2>,
H::OutputSize: IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>,
{
let mut uniform_bytes = [0u8; 64];
+6 -2
View File
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// 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
+44 -40
View File
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// 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)
//!
@@ -16,7 +20,7 @@
//! We will use the following choice in this example:
//!
//! ```ignore
//! type CipherSuite = voprf_vx::Ristretto255;
//! type CipherSuite = voprf::Ristretto255;
//! ```
//!
//! ## Modes of Operation
@@ -47,12 +51,12 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # type CipherSuite = voprf::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! use rand::rngs::SysRng;
//! use rand::Rng;
//! use voprf_vx::OprfServer;
//! use voprf::OprfServer;
//!
//! let mut server_rng = SysRng;
//! let server = OprfServer::<CipherSuite>::new(&mut server_rng);
@@ -67,12 +71,12 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # type CipherSuite = voprf::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! use rand::rngs::SysRng;
//! use rand::Rng;
//! use voprf_vx::OprfClient;
//! use voprf::OprfClient;
//!
//! let mut client_rng = SysRng;
//! let client_blind_result = OprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
@@ -88,10 +92,10 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # type CipherSuite = voprf::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf_vx::OprfClient;
//! # use voprf::OprfClient;
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = SysRng;
@@ -99,7 +103,7 @@
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf_vx::OprfServer;
//! # use voprf::OprfServer;
//! # let mut server_rng = SysRng;
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! let server_evaluate_result = server.blind_evaluate(&client_blind_result.message);
@@ -113,10 +117,10 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # type CipherSuite = voprf::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf_vx::OprfClient;
//! # use voprf::OprfClient;
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = SysRng;
@@ -124,7 +128,7 @@
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf_vx::OprfServer;
//! # use voprf::OprfServer;
//! # let mut server_rng = SysRng;
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let message = server.blind_evaluate(&client_blind_result.message);
@@ -146,10 +150,10 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # type CipherSuite = voprf::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf_vx::OprfClient;
//! # use voprf::OprfClient;
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = SysRng;
@@ -157,7 +161,7 @@
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf_vx::OprfServer;
//! # use voprf::OprfServer;
//! # let mut server_rng = SysRng;
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let message = server.blind_evaluate(&client_blind_result.message);
@@ -193,12 +197,12 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # type CipherSuite = voprf::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! use rand::rngs::SysRng;
//! use rand::Rng;
//! use voprf_vx::VoprfServer;
//! use voprf::VoprfServer;
//!
//! let mut server_rng = SysRng;
//! let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
@@ -220,12 +224,12 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # type CipherSuite = voprf::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! use rand::rngs::SysRng;
//! use rand::Rng;
//! use voprf_vx::VoprfClient;
//! use voprf::VoprfClient;
//!
//! let mut client_rng = SysRng;
//! let client_blind_result = VoprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
@@ -242,10 +246,10 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # type CipherSuite = voprf::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf_vx::{VoprfServerEvaluateResult, VoprfClient};
//! # use voprf::{VoprfServerEvaluateResult, VoprfClient};
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = SysRng;
@@ -253,7 +257,7 @@
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf_vx::VoprfServer;
//! # use voprf::VoprfServer;
//! # let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! let VoprfServerEvaluateResult { message, proof } =
@@ -269,10 +273,10 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # type CipherSuite = voprf::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf_vx::VoprfClient;
//! # use voprf::VoprfClient;
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = SysRng;
@@ -280,7 +284,7 @@
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf_vx::VoprfServer;
//! # use voprf::VoprfServer;
//! # let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let server_evaluate_result = server.blind_evaluate(
@@ -310,10 +314,10 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # type CipherSuite = voprf::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf_vx::VoprfClient;
//! # use voprf::VoprfClient;
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = SysRng;
@@ -321,7 +325,7 @@
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf_vx::VoprfServer;
//! # use voprf::VoprfServer;
//! # let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let server_evaluate_result = server.blind_evaluate(
@@ -364,10 +368,10 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # type CipherSuite = voprf::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf_vx::VoprfClient;
//! # use voprf::VoprfClient;
//! # use rand::{rngs::SysRng, Rng};
//! #
//! let mut client_rng = SysRng;
@@ -388,10 +392,10 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # type CipherSuite = voprf::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf_vx::{VoprfServerBatchEvaluateFinishResult, VoprfClient};
//! # use voprf::{VoprfServerBatchEvaluateFinishResult, VoprfClient};
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = SysRng;
@@ -405,7 +409,7 @@
//! # client_states.push(client_blind_result.state);
//! # client_messages.push(client_blind_result.message);
//! # }
//! # use voprf_vx::VoprfServer;
//! # use voprf::VoprfServer;
//! let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! let prepared_evaluation_elements = server.batch_blind_evaluate_prepare(client_messages.iter());
@@ -422,10 +426,10 @@
//! ```
//! # #[cfg(feature = "alloc")] {
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # type CipherSuite = voprf::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf_vx::{VoprfServerBatchEvaluateResult, VoprfClient};
//! # use voprf::{VoprfServerBatchEvaluateResult, VoprfClient};
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = SysRng;
@@ -439,7 +443,7 @@
//! # client_states.push(client_blind_result.state);
//! # client_messages.push(client_blind_result.message);
//! # }
//! # use voprf_vx::VoprfServer;
//! # use voprf::VoprfServer;
//! let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! let VoprfServerBatchEvaluateResult { messages, proof } = server
@@ -456,10 +460,10 @@
//! ```
//! # #[cfg(feature = "alloc")] {
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # type CipherSuite = voprf::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf_vx::{VoprfServerBatchEvaluateResult, VoprfClient};
//! # use voprf::{VoprfServerBatchEvaluateResult, VoprfClient};
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = SysRng;
@@ -473,7 +477,7 @@
//! # client_states.push(client_blind_result.state);
//! # client_messages.push(client_blind_result.message);
//! # }
//! # use voprf_vx::VoprfServer;
//! # use voprf::VoprfServer;
//! # let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let VoprfServerBatchEvaluateResult { messages, proof } = server
+11 -7
View File
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// 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
@@ -8,13 +12,13 @@ use core::iter::{self, Map};
use derive_where::derive_where;
use digest::{Digest, Output};
use hybrid_array::Array;
use hybrid_array::typenum::Unsigned;
use hybrid_array::Array;
use rand_core::{TryCryptoRng, TryRng};
use crate::common::{
BlindedElement, EvaluationElement, Mode, STR_FINALIZE, derive_key_internal,
deterministic_blind_unchecked, hash_to_group, i2osp_2, server_evaluate_hash_input,
derive_key_internal, deterministic_blind_unchecked, hash_to_group, i2osp_2,
server_evaluate_hash_input, BlindedElement, EvaluationElement, Mode, STR_FINALIZE,
};
#[cfg(feature = "serde")]
use crate::serialization::serde::Scalar;
@@ -263,12 +267,12 @@ fn finalize_after_unblind<
mod tests {
use core::ptr;
use rand::TryRng;
use rand::rngs::SysRng;
use rand::TryRng;
use super::*;
use crate::Group;
use crate::common::{Dst, STR_HASH_TO_GROUP};
use crate::Group;
fn prf<CS: CipherSuite>(
input: &[u8],
+12 -8
View File
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// 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
@@ -15,9 +19,9 @@ use hybrid_array::{Array, ArraySize};
use rand_core::{TryCryptoRng, TryRng};
use crate::common::{
BlindedElement, Dst, EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE,
STR_HASH_TO_SCALAR, STR_INFO, derive_keypair, deterministic_blind_unchecked, generate_proof,
hash_to_group, i2osp_2, server_evaluate_hash_input, verify_proof,
derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2,
server_evaluate_hash_input, verify_proof, BlindedElement, Dst, EvaluationElement, Mode,
PreparedEvaluationElement, Proof, STR_FINALIZE, STR_HASH_TO_SCALAR, STR_INFO,
};
#[cfg(feature = "serde")]
use crate::serialization::serde::{Element, Scalar};
@@ -367,7 +371,7 @@ impl<CS: CipherSuite> PoprfServer<CS> {
tweaked_key,
prepared_evaluation_elements
.into_iter()
.map(|element| element.0.0),
.map(|element| element.0 .0),
blinded_elements.map(|element| element.0),
Mode::Poprf,
)?;
@@ -375,7 +379,7 @@ impl<CS: CipherSuite> PoprfServer<CS> {
let messages = prepared_evaluation_elements.into_iter().map(<fn(
&PreparedEvaluationElement<CS>,
) -> _>::from(
|element| EvaluationElement(element.0.0),
|element| EvaluationElement(element.0 .0),
));
Ok(PoprfServerBatchEvaluateFinishResult { messages, proof })
@@ -732,8 +736,8 @@ mod tests {
use rand::rngs::SysRng;
use super::*;
use crate::Group;
use crate::common::STR_HASH_TO_GROUP;
use crate::Group;
fn prf<CS: CipherSuite>(
input: &[u8],
+10 -173
View File
@@ -1,12 +1,16 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// 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
//! protocol
use hybrid_array::Array;
use hybrid_array::typenum::{Sum, Unsigned};
use hybrid_array::Array;
use crate::{
BlindedElement, CipherSuite, Error, EvaluationElement, Group, OprfClient, OprfServer,
@@ -34,10 +38,6 @@ impl<CS: CipherSuite> OprfClient<CS> {
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 })
}
}
@@ -63,10 +63,6 @@ impl<CS: CipherSuite> VoprfClient<CS> {
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,
@@ -95,10 +91,6 @@ impl<CS: CipherSuite> PoprfClient<CS> {
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,
@@ -122,10 +114,6 @@ impl<CS: CipherSuite> OprfServer<CS> {
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 })
}
}
@@ -150,10 +138,6 @@ impl<CS: CipherSuite> VoprfServer<CS> {
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 })
}
}
@@ -178,10 +162,6 @@ impl<CS: CipherSuite> PoprfServer<CS> {
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 })
}
}
@@ -207,10 +187,6 @@ impl<CS: CipherSuite> Proof<CS> {
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 })
}
}
@@ -231,10 +207,6 @@ impl<CS: CipherSuite> BlindedElement<CS> {
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))
}
}
@@ -255,10 +227,6 @@ impl<CS: CipherSuite> EvaluationElement<CS> {
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))
}
}
@@ -353,7 +321,6 @@ mod test {
VoprfClient, VoprfServer,
};
// Fuzz: no panics on arbitrary input
macro_rules! test_deserialize {
($item:ident, $bytes:ident) => {
#[cfg(feature = "ristretto255")]
@@ -361,43 +328,12 @@ mod test {
let _ = $item::<crate::Ristretto255>::deserialize(&$bytes[..]);
}
let _ = $item::<::p256::NistP256>::deserialize(&$bytes[..]);
let _ = $item::<::p384::NistP384>::deserialize(&$bytes[..]);
let _ = $item::<::p521::NistP521>::deserialize(&$bytes[..]);
let _ = $item::<p256::NistP256>::deserialize(&$bytes[..]);
let _ = $item::<p384::NistP384>::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! {
#[test]
fn test_nocrash_oprf_client(bytes in vec(any::<u8>(), 0..200)) {
@@ -445,103 +381,4 @@ mod test {
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);
}
+6 -2
View File
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// 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:
//! https://www.rfc-editor.org/rfc/rfc9497#appendix-A
+6 -2
View File
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// 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 core::cmp::min;
+24 -2
View File
@@ -1,8 +1,30 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// 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 mock_rng;
mod parser;
mod test_cfrg_vectors;
impl crate::CipherSuite for p256::NistP256 {
const ID: &'static [u8] = <p256::NistP256 as hash2curve::OprfParameters>::ID;
type Group = p256::NistP256;
type Hash = sha2::Sha256;
}
impl crate::CipherSuite for p384::NistP384 {
const ID: &'static [u8] = <p384::NistP384 as hash2curve::OprfParameters>::ID;
type Group = p384::NistP384;
type Hash = sha2::Sha384;
}
impl crate::CipherSuite for p521::NistP521 {
const ID: &'static [u8] = <p521::NistP521 as hash2curve::OprfParameters>::ID;
type Group = p521::NistP521;
type Hash = sha2::Sha512;
}
+15 -6
View File
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// 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::vec::Vec;
@@ -15,14 +19,15 @@ fn parse_ciphersuites(input: &str) -> String {
let mut ciphersuites = vec![];
let chunks: Vec<&str> = re.split(input).collect();
for (count, caps) in (1..).zip(re.captures_iter(input)) {
let mut count = 1;
for caps in re.captures_iter(input) {
let ciphersuite = format!(
"\"{}\": {{ {} }}",
&caps["ciphersuite"],
parse_modes(chunks[count])
);
ciphersuites.push(ciphersuite);
count += 1;
}
ciphersuites.join(",\n")
@@ -33,13 +38,15 @@ fn parse_modes(input: &str) -> String {
let mut modes = vec![];
let chunks: Vec<&str> = re.split(input).collect();
for (count, caps) in (1..).zip(re.captures_iter(input)) {
let mut count = 1;
for caps in re.captures_iter(input) {
let mode = format!(
"\"{}\": [\n {} \n]",
&caps["mode"],
parse_vectors(chunks[count])
);
modes.push(mode);
count += 1;
}
modes.join(",\n")
@@ -52,9 +59,11 @@ fn parse_vectors(input: &str) -> String {
let chunks: Vec<&str> = re.split(input).collect();
let init_params = parse_params(chunks[0]);
for (count, _) in (1..).zip(re.captures_iter(input)) {
let mut count = 1;
for _ in re.captures_iter(input) {
let params = format!("{{\n{},\n{}\n}}", init_params, parse_params(chunks[count]));
vectors.push(params);
count += 1;
}
vectors.join(",\n")
+6 -2
View File
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// 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::vec;
+12 -8
View File
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// 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
@@ -10,14 +14,14 @@ use core::iter::{self, Map, Repeat, Zip};
use derive_where::derive_where;
use digest::{Digest, Output};
use hybrid_array::Array;
use hybrid_array::typenum::Unsigned;
use hybrid_array::Array;
use rand_core::{TryCryptoRng, TryRng};
use crate::common::{
BlindedElement, EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE,
derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2,
server_evaluate_hash_input, verify_proof,
server_evaluate_hash_input, verify_proof, BlindedElement, EvaluationElement, Mode,
PreparedEvaluationElement, Proof, STR_FINALIZE,
};
#[cfg(feature = "serde")]
use crate::serialization::serde::{Element, Scalar};
@@ -339,14 +343,14 @@ impl<CS: CipherSuite> VoprfServer<CS> {
g,
self.pk,
blinded_elements.map(|element| element.0),
evaluation_elements.into_iter().map(|element| element.0.0),
evaluation_elements.into_iter().map(|element| element.0 .0),
Mode::Voprf,
)?;
let messages = evaluation_elements.into_iter().map(<fn(
&PreparedEvaluationElement<CS>,
) -> EvaluationElement<CS>>::from(
|element| EvaluationElement(element.0.0),
|element| EvaluationElement(element.0 .0),
));
Ok(VoprfServerBatchEvaluateFinishResult { messages, proof })
@@ -549,8 +553,8 @@ mod tests {
use rand::rngs::SysRng;
use super::*;
use crate::Group;
use crate::common::{Dst, STR_HASH_TO_GROUP};
use crate::Group;
fn prf<CS: CipherSuite>(
input: &[u8],