chore: bump to 0.6.0-rc.0, migrate to elliptic-curve 0.14, hash2curve 0.14, rand_core 0.10 (#1)
Rust CI / cargo fmt (push) Successful in 4s
Rust CI / cargo clippy (push) Successful in 27s
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 1m20s
Rust CI / test (1.87.0 / no backend / --features danger) (push) Successful in 1m26s
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 1m25s
Rust CI / test (stable / no backend / --features serde) (push) Successful in 1m18s
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 (1.87.0 / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m35s
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 1m36s
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 (wasm32-unknown-unknown / no backend) (push) Successful in 11s
Rust CI / no-std (thumbv6m-none-eabi / 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 15s

Reviewed-on: vexahub/voprf-vexahub#1
Co-authored-by: UneBaguette <[email protected]>
Co-committed-by: UneBaguette <[email protected]>
This commit was merged in pull request #1.
This commit is contained in:
2026-06-27 17:04:58 +02:00
committed by breakingbread
parent d42e7948a1
commit 3ce3ac8817
23 changed files with 598 additions and 499 deletions
+97
View File
@@ -0,0 +1,97 @@
name: Rust CI
on: [ push, pull_request ]
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
@@ -0,0 +1,26 @@
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 }}
+14
View File
@@ -1,5 +1,19 @@
# Changelog
## 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`
* 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
* 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
-76
View File
@@ -1,76 +0,0 @@
# Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all project spaces, and it also applies when
an individual is representing the project or its community in public spaces.
Examples of representing a project or community include using an official
project e-mail address, posting via an official social media account, or acting
as an appointed representative at an online or offline event. Representation of
a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at <opensource-conduct@fb.com>. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
-19
View File
@@ -2,29 +2,10 @@
We want to make contributing to this project as easy and transparent as
possible.
## Pull Requests
We actively welcome your pull requests.
1. Fork the repo and create your branch from `main`.
2. If you've added code that should be tested, add tests.
3. If you've changed APIs, update the documentation.
4. Ensure the test suite passes.
5. If you haven't already, complete the Contributor License Agreement ("CLA").
## Contributor License Agreement ("CLA")
In order to accept your pull request, we need you to submit a CLA. You only need
to do this once to work on any of Facebook's open source projects.
Complete your CLA here: <https://code.facebook.com/cla>
## Issues
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.
Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe
disclosure of security bugs. In those cases, please go through the process
outlined on that page and do not file a public issue.
## License
By contributing to voprf, you agree that your contributions will be
licensed under both the LICENSE-MIT and LICENSE-APACHE files in the root
+23 -25
View File
@@ -2,68 +2,66 @@
authors = ["Kevin Lewi <[email protected]>"]
categories = ["no-std", "algorithms", "cryptography"]
description = "An implementation of a verifiable oblivious pseudorandom function (VOPRF)"
edition = "2021"
edition = "2024"
keywords = ["oprf"]
license = "MIT"
name = "voprf"
name = "voprf-vexahub"
readme = "README.md"
repository = "https://github.com/facebook/voprf/"
rust-version = "1.85"
version = "0.6.0-pre.1"
rust-version = "1.87"
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"]
serde = ["curve25519-dalek?/serde", "generic-array/serde", "dep:serde"]
serde = ["curve25519-dalek?/serde", "hybrid-array/serde", "dep:serde"]
std = ["alloc"]
[dependencies]
curve25519-dalek = { version = "4", 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 = "0.10"
digest = "0.11"
displaydoc = { version = "0.2", default-features = false }
elliptic-curve = { version = "0.13", features = [
"hash2curve",
elliptic-curve = { version = "0.14", features = [
"sec1",
"voprf",
] }
generic-array = "1"
rand_core = { version = "0.9", default-features = false }
hash2curve = "0.14"
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.10", default-features = false, optional = true }
sha2 = { version = "0.11", default-features = false, optional = true }
p256 = { version = "0.14.0-rc", default-features = false, features = ["hash2curve", "oprf"], optional = true }
subtle = { version = "2.6", default-features = false }
zeroize = { version = "1.5", default-features = false }
[dev-dependencies]
generic-array = { version = "1" }
hex = "0.4"
p256 = { version = "0.13", default-features = false, features = [
p256 = { version = "0.14.0-rc", default-features = false, features = [
"hash2curve",
"voprf",
"oprf",
] }
p384 = { version = "0.13", default-features = false, features = [
p384 = { version = "0.14.0-rc", default-features = false, features = [
"hash2curve",
"voprf",
"oprf",
] }
p521 = { version = "0.13.3", default-features = false, features = [
p521 = { version = "0.14.0-rc", default-features = false, features = [
"hash2curve",
"voprf",
"oprf",
] }
proptest = "1"
rand = "0.9"
rand = "0.10"
regex = "1"
serde_json = "1"
sha2 = "0.10"
sha2 = "0.11"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
targets = []
features = ["doctest"]
+4 -4
View File
@@ -1,4 +1,4 @@
# voprf ![Build Status](https://github.com/novifinancial/voprf/workflows/Rust%20CI/badge.svg)
# voprf
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/) along with an example for usage.
The API can be found [here](https://docs.rs/voprf-vexahub/) along with an example for usage.
Installation
------------
@@ -16,12 +16,12 @@ Installation
Add the following line to the dependencies of your `Cargo.toml`:
```
voprf = "0.6.0-pre.1"
voprf = { package = "voprf-vexahub", version = "0.6.0-rc.0" }
```
### Minimum Supported Rust Version
Rust **1.85** or higher.
Rust **1.87** or higher.
Contributors
------------
+30
View File
@@ -0,0 +1,30 @@
{
"$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
}
-7
View File
@@ -1,8 +1 @@
format_code_in_doc_comments = true
format_strings = true
group_imports = "StdExternalCrate"
imports_granularity = "Module"
license_template_path = ".cargo/license.rs"
newline_style = "Unix"
unstable_features = true
wrap_comments = true
+13 -24
View File
@@ -8,23 +8,26 @@
//! Defines the CipherSuite trait to specify the underlying primitives for VOPRF
use digest::core_api::BlockSizeUser;
use digest::{FixedOutput, HashMarker, OutputSizeUser};
use elliptic_curve::VoprfParameters;
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
use generic_array::ArrayLength;
use crate::Group;
use core::ops::Mul;
use digest::block_api::BlockSizeUser;
use digest::typenum::{IsLess, IsLessOrEqual, U256};
use digest::{Digest, FixedOutput, HashMarker, OutputSizeUser};
use hybrid_array::ArraySize;
use hybrid_array::typenum::{IsGreaterOrEqual, Prod, True, U2};
/// Configures the underlying primitives used in VOPRF
pub trait CipherSuite
where
<Self::Hash as OutputSizeUser>::OutputSize:
ArrayLength + IsLess<U256> + IsLessOrEqual<<Self::Hash as BlockSizeUser>::BlockSize>,
<Self::Group as Group>::SecurityLevel: Mul<U2>,
<Self::Hash as OutputSizeUser>::OutputSize: ArraySize
+ IsLess<U256>
+ IsLessOrEqual<<Self::Hash as BlockSizeUser>::BlockSize, Output = True>
+ IsGreaterOrEqual<Prod<<Self::Group as Group>::SecurityLevel, U2>, Output = True>,
{
/// The ciphersuite identifier as dictated by
/// <https://www.rfc-editor.org/rfc/rfc9497>
const ID: &'static str;
const ID: &'static [u8];
/// A finite cyclic group along with a point representation that allows some
/// customization on how to hash an input to a curve point. See [`Group`].
@@ -32,19 +35,5 @@ where
/// The main hash function to use (for HKDF computations and hashing
/// transcripts).
type Hash: BlockSizeUser + Default + FixedOutput + HashMarker;
}
impl<T: VoprfParameters> CipherSuite for T
where
T: Group,
T::Hash: BlockSizeUser + Default + FixedOutput + HashMarker,
<T::Hash as OutputSizeUser>::OutputSize:
ArrayLength + IsLess<U256> + IsLessOrEqual<<T::Hash as BlockSizeUser>::BlockSize>,
{
const ID: &'static str = T::ID;
type Group = T;
type Hash = T::Hash;
type Hash: Digest + BlockSizeUser + Default + FixedOutput + HashMarker;
}
+26 -29
View File
@@ -13,10 +13,9 @@ use core::ops::Add;
use derive_where::derive_where;
use digest::{Digest, Output, OutputSizeUser};
use generic_array::sequence::Concat;
use generic_array::typenum::{IsLess, Unsigned, U2, U256, U9};
use generic_array::{ArrayLength, GenericArray};
use rand_core::{TryCryptoRng, TryRngCore};
use hybrid_array::typenum::{IsLess, U2, U9, U256, Unsigned};
use hybrid_array::{Array, ArrayN, ArraySize};
use rand_core::{TryCryptoRng, TryRng};
use subtle::ConstantTimeEq;
#[cfg(feature = "serde")]
@@ -29,14 +28,14 @@ use crate::{CipherSuite, Error, Group, InternalError, Result};
///////////////
pub(crate) const STR_FINALIZE: [u8; 8] = *b"Finalize";
pub(crate) const STR_SEED: [u8; 5] = *b"Seed-";
pub(crate) const STR_DERIVE_KEYPAIR: [u8; 13] = *b"DeriveKeyPair";
pub(crate) const STR_SEED: ArrayN<u8, 5> = Array(*b"Seed-");
pub(crate) const STR_DERIVE_KEYPAIR: ArrayN<u8, 13> = Array(*b"DeriveKeyPair");
pub(crate) const STR_COMPOSITE: [u8; 9] = *b"Composite";
pub(crate) const STR_CHALLENGE: [u8; 9] = *b"Challenge";
pub(crate) const STR_INFO: [u8; 4] = *b"Info";
pub(crate) const STR_OPRF: [u8; 7] = *b"OPRFV1-";
pub(crate) const STR_HASH_TO_SCALAR: [u8; 13] = *b"HashToScalar-";
pub(crate) const STR_HASH_TO_GROUP: [u8; 12] = *b"HashToGroup-";
pub(crate) const STR_HASH_TO_SCALAR: ArrayN<u8, 13> = Array(*b"HashToScalar-");
pub(crate) const STR_HASH_TO_GROUP: ArrayN<u8, 12> = Array(*b"HashToGroup-");
/// Determines the mode of operation (either base mode or verifiable mode). This
/// is only used for custom implementations for [`Group`].
@@ -128,7 +127,7 @@ pub struct Proof<CS: CipherSuite> {
/// Can only fail with [`Error::Batch`].
#[allow(clippy::many_single_char_names)]
pub(crate) fn generate_proof<CS: CipherSuite, R: TryRngCore + TryCryptoRng>(
pub(crate) fn generate_proof<CS: CipherSuite, R: TryRng + TryCryptoRng>(
rng: &mut R,
k: <CS::Group as Group>::Scalar,
a: <CS::Group as Group>::Elem,
@@ -178,7 +177,7 @@ pub(crate) fn generate_proof<CS: CipherSuite, R: TryRngCore + TryCryptoRng>(
&STR_CHALLENGE,
];
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, mode);
let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, mode);
// This can't fail, the size of the `input` is known.
let c_scalar = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
let s_scalar = r - &(c_scalar * &k);
@@ -234,7 +233,7 @@ pub(crate) fn verify_proof<CS: CipherSuite>(
&STR_CHALLENGE,
];
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, mode);
let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, mode);
// This can't fail, the size of the `input` is known.
let c = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
@@ -272,7 +271,7 @@ fn compute_composites<
let len = u16::try_from(c_slice.len()).map_err(|_| Error::Batch)?;
// seedDST = "Seed-" || contextString
let seed_dst = Dst::new::<CS, _, _>(STR_SEED, mode);
let seed_dst = Dst::new::<CS, _>(STR_SEED, mode);
// h1Input = I2OSP(len(Bm), 2) || Bm ||
// I2OSP(len(seedDST), 2) || seedDST
@@ -308,7 +307,7 @@ fn compute_composites<
&STR_COMPOSITE,
];
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, mode);
let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, mode);
// This can't fail, the size of the `input` is known.
let di = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
m = c * &di + &m;
@@ -337,7 +336,7 @@ pub(crate) fn derive_key_internal<CS: CipherSuite>(
info: &[u8],
mode: Mode,
) -> Result<<CS::Group as Group>::Scalar, Error> {
let dst = Dst::new::<CS, _, _>(STR_DERIVE_KEYPAIR, mode);
let dst = Dst::new::<CS, _>(STR_DERIVE_KEYPAIR, mode);
let info_len = i2osp_2(info.len()).map_err(|_| Error::DeriveKeyPair)?;
@@ -410,7 +409,7 @@ pub(crate) fn hash_to_group<CS: CipherSuite>(
input: &[u8],
mode: Mode,
) -> Result<<CS::Group as Group>::Elem> {
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, mode);
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, mode);
CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).map_err(|_| Error::Input)
}
@@ -419,7 +418,7 @@ pub(crate) fn hash_to_group<CS: CipherSuite>(
pub(crate) fn server_evaluate_hash_input<CS: CipherSuite>(
input: &[u8],
info: Option<&[u8]>,
issued_element: GenericArray<u8, <<CS as CipherSuite>::Group as Group>::ElemLen>,
issued_element: Array<u8, <<CS as CipherSuite>::Group as Group>::ElemLen>,
) -> Result<Output<CS::Hash>> {
// OPRF & VOPRF
// hashInput = I2OSP(len(input), 2) || input ||
@@ -448,24 +447,22 @@ pub(crate) fn server_evaluate_hash_input<CS: CipherSuite>(
.finalize())
}
pub(crate) struct Dst<L: ArrayLength> {
dst_1: GenericArray<u8, L>,
dst_2: &'static str,
pub(crate) struct Dst<L: ArraySize> {
dst_1: Array<u8, L>,
dst_2: &'static [u8],
}
impl<L: ArrayLength> Dst<L> {
pub(crate) fn new<CS, T, TL>(par_1: T, mode: Mode) -> Self
impl<L: ArraySize> Dst<L> {
pub(crate) fn new<CS, TL>(par_1: Array<u8, TL>, mode: Mode) -> Self
where
CS: CipherSuite,
T: Into<GenericArray<u8, TL>>,
TL: ArrayLength + Add<U9, Output = L>,
TL: ArraySize + Add<U9, Output = L>,
{
let par_1 = par_1.into();
// Generates the contextString parameter as defined in
// <https://www.rfc-editor.org/rfc/rfc9497#section-3.1>
let par_2 = GenericArray::from(STR_OPRF)
.concat([mode.to_u8()].into())
.concat([b'-'].into());
let par_2 = ArrayN::<u8, 7>::from(STR_OPRF)
.concat(ArrayN::<u8, 1>::from([mode.to_u8()]))
.concat(ArrayN::<u8, 1>::from([b'-']));
let dst_1 = par_1.concat(par_2);
let dst_2 = CS::ID;
@@ -480,7 +477,7 @@ impl<L: ArrayLength> Dst<L> {
}
pub(crate) fn as_dst(&self) -> [&[u8]; 2] {
[&self.dst_1, self.dst_2.as_bytes()]
[&self.dst_1, self.dst_2]
}
pub(crate) fn i2osp_2(&self) -> [u8; 2] {
@@ -518,6 +515,6 @@ pub(crate) fn i2osp_2(input: usize) -> Result<[u8; 2], InternalError> {
.map_err(|_| InternalError::I2osp)
}
pub(crate) fn i2osp_2_array<L: ArrayLength + IsLess<U256>>() -> GenericArray<u8, U2> {
pub(crate) fn i2osp_2_array<L: ArraySize + IsLess<U256>>() -> Array<u8, U2> {
L::U16.to_be_bytes().into()
}
+46 -71
View File
@@ -6,21 +6,20 @@
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
use core::num::NonZeroU32;
use core::ops::Add;
use digest::core_api::BlockSizeUser;
use core::ops::{Add, Mul};
use digest::block_api::BlockSizeUser;
use digest::typenum::{IsLess, IsLessOrEqual, U256};
use digest::{FixedOutput, HashMarker};
use elliptic_curve::group::cofactor::CofactorGroup;
use elliptic_curve::hash2curve::{ExpandMsgXmd, FromOkm, GroupDigest};
use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint};
use elliptic_curve::sec1::{FromSec1Point, ModulusSize, ToSec1Point};
use elliptic_curve::{
AffinePoint, Field, FieldBytes, FieldBytesSize, Group as _, ProjectivePoint, PublicKey, Scalar,
SecretKey,
};
use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256};
use generic_array::{ArrayLength, GenericArray};
use rand_core::{TryCryptoRng, TryRngCore};
use hash2curve::{ExpandMsgXmd, GroupDigest, MapToCurve, hash_to_scalar};
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};
@@ -30,19 +29,24 @@ type ScalarLen<C> = FieldBytesSize<C>;
impl<C> Group for C
where
C: GroupDigest,
ProjectivePoint<Self>: CofactorGroup + ToEncodedPoint<Self>,
C: GroupDigest + MapToCurve,
C::SecurityLevel: Mul<U2>,
C::SecurityLevel: ArraySize,
<C::SecurityLevel as Mul<U2>>::Output: ArraySize,
ProjectivePoint<Self>: CofactorGroup + ToSec1Point<Self>,
ScalarLen<Self>: ModulusSize,
ScalarLen<Self>: ArrayLength,
AffinePoint<Self>: FromEncodedPoint<Self> + ToEncodedPoint<Self>,
Scalar<Self>: FromOkm,
ScalarLen<Self>: ArraySize,
ScalarLen<Self>: hybrid_array::typenum::NonZero,
Scalar<Self>: elliptic_curve::ops::Reduce<Array<u8, ScalarLen<Self>>>,
Scalar<Self>: elliptic_curve::ops::Reduce<Array<u8, <C as MapToCurve>::Length>>,
AffinePoint<Self>: FromSec1Point<Self> + ToSec1Point<Self>,
// `VoprfClientLen`, `PoprfClientLen`, `VoprfServerLen`, `PoprfServerLen`
ScalarLen<Self>: Add<ElemLen<Self>>,
Sum<ScalarLen<Self>, ElemLen<Self>>: ArrayLength,
Sum<ScalarLen<Self>, ElemLen<Self>>: ArraySize,
// `ProofLen`
ScalarLen<Self>: Add<ScalarLen<Self>>,
Sum<ScalarLen<Self>, ScalarLen<Self>>: ArrayLength,
ElemLen<Self>: ArrayLength,
Sum<ScalarLen<Self>, ScalarLen<Self>>: ArraySize,
ElemLen<Self>: ArraySize,
{
type Elem = ProjectivePoint<Self>;
@@ -52,23 +56,25 @@ where
type ScalarLen = ScalarLen<Self>;
type SecurityLevel = C::SecurityLevel;
type OkmLen = <C as 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>
where
H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
{
Self::hash_from_bytes::<ExpandMsgXmd<H>>(input, dst).map_err(|_| InternalError::Input)
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError> {
Self::hash_from_bytes(input, dst).map_err(|_| InternalError::Input)
}
// Implements the `HashToScalar()` function
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>,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize, Output = True>,
C::SecurityLevel: Mul<U2>,
H::OutputSize: IsGreaterOrEqual<Prod<C::SecurityLevel, U2>, Output = True>,
{
<Self as GroupDigest>::hash_to_scalar::<ExpandMsgXmd<H>>(input, dst)
hash_to_scalar::<C, ExpandMsgXmd<H>, <C as MapToCurve>::Length>(input, dst)
.map_err(|_| InternalError::Input)
}
@@ -80,10 +86,10 @@ where
ProjectivePoint::<Self>::identity()
}
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> {
let bytes = elem.to_encoded_point(true);
fn serialize_elem(elem: Self::Elem) -> Array<u8, Self::ElemLen> {
let bytes = elem.to_sec1_point(true);
let bytes = bytes.as_bytes();
let mut result = GenericArray::default();
let mut result = Array::default();
result[..bytes.len()].copy_from_slice(bytes);
result
}
@@ -94,8 +100,16 @@ where
.map_err(|_| Error::Deserialization)
}
fn random_scalar<R: TryRngCore + TryCryptoRng>(rng: &mut R) -> Result<Self::Scalar> {
Ok(*SecretKey::<Self>::random(&mut CompatRng(rng)).to_nonzero_scalar())
fn random_scalar<R: TryCryptoRng>(rng: &mut R) -> Result<Self::Scalar> {
loop {
let mut bytes = FieldBytes::<Self>::default();
rng.try_fill_bytes(&mut bytes).map_err(|_| Error::Rng)?;
if let Ok(key) = SecretKey::<Self>::from_slice(&bytes) {
return Ok(*key.to_nonzero_scalar());
}
}
}
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar {
@@ -111,9 +125,9 @@ where
Scalar::<Self>::ZERO
}
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
fn serialize_scalar(scalar: Self::Scalar) -> Array<u8, Self::ScalarLen> {
let bytes: FieldBytes<Self> = scalar.into();
let mut result = GenericArray::<u8, Self::ScalarLen>::default();
let mut result = Array::<u8, Self::ScalarLen>::default();
result.as_mut_slice().copy_from_slice(bytes.as_ref());
result
}
@@ -124,42 +138,3 @@ where
.map_err(|_| Error::Deserialization)
}
}
/// Adapter allowing `rand_core 0.9` RNGs to satisfy the `elliptic_curve` 0.13
/// requirement for `rand_core 0.6` traits.
///
/// TODO #150: Remove this adapter when `elliptic_curve` migrates to `rand_core
/// 0.9`.
struct CompatRng<'a, R>(&'a mut R);
impl<'a, R> elliptic_curve::rand_core::RngCore for CompatRng<'a, R>
where
R: TryRngCore,
{
fn next_u32(&mut self) -> u32 {
self.0.try_next_u32().expect("RNG failure")
}
fn next_u64(&mut self) -> u64 {
self.0.try_next_u64().expect("RNG failure")
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.0
.try_fill_bytes(dest)
.expect("RNG failure while filling bytes");
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), elliptic_curve::rand_core::Error> {
self.0.try_fill_bytes(dest).map_err(|_| compat_error())?;
Ok(())
}
}
impl<'a, R> elliptic_curve::rand_core::CryptoRng for CompatRng<'a, R> where R: TryCryptoRng {}
fn compat_error() -> elliptic_curve::rand_core::Error {
let code = NonZeroU32::new(elliptic_curve::rand_core::Error::CUSTOM_START)
.expect("CUSTOM_START must be non-zero");
elliptic_curve::rand_core::Error::from(code)
}
+28 -13
View File
@@ -14,11 +14,11 @@ mod ristretto;
use core::ops::{Add, Mul, Sub};
use digest::core_api::BlockSizeUser;
use digest::block_api::BlockSizeUser;
use digest::{FixedOutput, HashMarker};
use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256};
use generic_array::{ArrayLength, GenericArray};
use rand_core::{TryCryptoRng, TryRngCore};
use hybrid_array::typenum::{IsGreaterOrEqual, IsLess, IsLessOrEqual, Prod, Sum, True, U2, U256};
use hybrid_array::{Array, ArraySize};
use rand_core::{TryCryptoRng, TryRng};
#[cfg(feature = "ristretto255")]
pub use ristretto::Ristretto255;
use subtle::{Choice, ConstantTimeEq};
@@ -32,10 +32,10 @@ pub trait Group
where
// `VoprfClientLen`, `PoprfClientLen`, `VoprfServerLen`, `PoprfServerLen`
Self::ScalarLen: Add<Self::ElemLen>,
Sum<Self::ScalarLen, Self::ElemLen>: ArrayLength,
Sum<Self::ScalarLen, Self::ElemLen>: ArraySize,
// `ProofLen`
Self::ScalarLen: Add<Self::ScalarLen>,
Sum<Self::ScalarLen, Self::ScalarLen>: ArrayLength,
Sum<Self::ScalarLen, Self::ScalarLen>: ArraySize,
{
/// The type of group elements
type Elem: ConstantTimeEq
@@ -45,7 +45,7 @@ where
+ for<'a> Mul<&'a Self::Scalar, Output = Self::Elem>;
/// The byte length necessary to represent group elements
type ElemLen: ArrayLength + 'static;
type ElemLen: ArraySize + 'static;
/// The type of base field scalars
type Scalar: ConstantTimeEq
@@ -56,7 +56,18 @@ where
+ for<'a> Sub<&'a Self::Scalar, Output = Self::Scalar>;
/// The byte length necessary to represent scalars
type ScalarLen: ArrayLength + 'static;
type ScalarLen: ArraySize + 'static;
/// Security parameter `k` in bytes (i.e. `k / 8`), as defined in
/// [RFC 9380 §8](https://www.rfc-editor.org/rfc/rfc9380#section-8).
///
/// 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;
/// 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
///
@@ -66,7 +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>;
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
///
@@ -76,7 +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>;
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;
@@ -90,7 +105,7 @@ where
}
/// Serializes the `self` group element
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen>;
fn serialize_elem(elem: Self::Elem) -> Array<u8, Self::ElemLen>;
/// Return an element from its fixed-length bytes representation. If the
/// element is the identity element, return an error.
@@ -104,7 +119,7 @@ where
///
/// # Errors
/// [`Error::Rng`](crate::Error::Rng) if the random number generator fails.
fn random_scalar<R: TryRngCore + TryCryptoRng>(rng: &mut R) -> Result<Self::Scalar>;
fn random_scalar<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self::Scalar>;
/// The multiplicative inverse of this scalar
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar;
@@ -117,7 +132,7 @@ where
fn zero_scalar() -> Self::Scalar;
/// Serializes a scalar to bytes
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen>;
fn serialize_scalar(scalar: Self::Scalar) -> Array<u8, Self::ScalarLen>;
/// Return a scalar from its fixed-length bytes representation. If the
/// scalar is zero or invalid, then return an error.
+45 -21
View File
@@ -6,16 +6,20 @@
// 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;
use curve25519_dalek::traits::Identity;
use digest::core_api::BlockSizeUser;
use digest::block_api::BlockSizeUser;
use digest::{FixedOutput, HashMarker};
use elliptic_curve::hash2curve::{ExpandMsg, ExpandMsgXmd, Expander};
use generic_array::typenum::{IsLess, IsLessOrEqual, U256, U32, U64};
use generic_array::GenericArray;
use rand_core::{TryCryptoRng, TryRngCore};
use hash2curve::{ExpandMsg, ExpandMsgXmd, Expander};
use hybrid_array::Array;
use hybrid_array::typenum::{
IsGreaterOrEqual, IsLess, IsLessOrEqual, Prod, True, U2, U16, U32, U64, U256,
};
use rand_core::{TryCryptoRng, TryRng};
use subtle::ConstantTimeEq;
use super::Group;
@@ -27,7 +31,7 @@ pub struct Ristretto255;
#[cfg(feature = "ristretto255-ciphersuite")]
impl crate::CipherSuite for Ristretto255 {
const ID: &'static str = "ristretto255-SHA512";
const ID: &'static [u8] = b"ristretto255-SHA512";
type Group = Ristretto255;
@@ -43,19 +47,31 @@ impl Group for Ristretto255 {
type ScalarLen = U32;
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>,
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 = GenericArray::<_, U64>::default();
ExpandMsgXmd::<H>::expand_message(input, dst, 64)
.map_err(|_| InternalError::Input)?
.fill_bytes(&mut uniform_bytes);
let mut uniform_bytes = [0u8; 64];
Ok(RistrettoPoint::from_uniform_bytes(&uniform_bytes.into()))
<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))
}
// Implements the `HashToScalar()` function from
@@ -63,14 +79,22 @@ 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>,
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 = GenericArray::<_, U64>::default();
ExpandMsgXmd::<H>::expand_message(input, dst, 64)
.map_err(|_| InternalError::Input)?
.fill_bytes(&mut uniform_bytes);
let mut uniform_bytes = [0u8; 64];
Ok(Scalar::from_bytes_mod_order_wide(&uniform_bytes.into()))
<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))
}
fn base_elem() -> Self::Elem {
@@ -82,7 +106,7 @@ impl Group for Ristretto255 {
}
// serialization of a group element
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> {
fn serialize_elem(elem: Self::Elem) -> Array<u8, Self::ElemLen> {
elem.compress().to_bytes().into()
}
@@ -94,7 +118,7 @@ impl Group for Ristretto255 {
.ok_or(Error::Deserialization)
}
fn random_scalar<R: TryRngCore + TryCryptoRng>(rng: &mut R) -> Result<Self::Scalar> {
fn random_scalar<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self::Scalar> {
loop {
let mut scalar_bytes = [0u8; 32];
rng.try_fill_bytes(&mut scalar_bytes)
@@ -119,7 +143,7 @@ impl Group for Ristretto255 {
Scalar::ZERO
}
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
fn serialize_scalar(scalar: Self::Scalar) -> Array<u8, Self::ScalarLen> {
scalar.to_bytes().into()
}
+105 -93
View File
@@ -20,7 +20,7 @@
//! We will use the following choice in this example:
//!
//! ```ignore
//! type CipherSuite = voprf::Ristretto255;
//! type CipherSuite = voprf_vexahub::Ristretto255;
//! ```
//!
//! ## Modes of Operation
@@ -51,14 +51,14 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vexahub::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! use rand::rngs::OsRng;
//! use rand::RngCore;
//! use voprf::OprfServer;
//! # type CipherSuite = voprf_vexahub::P256CipherSuite;
//! use rand::rngs::SysRng;
//! use rand::Rng;
//! use voprf_vexahub::OprfServer;
//!
//! let mut server_rng = OsRng;
//! let mut server_rng = SysRng;
//! let server = OprfServer::<CipherSuite>::new(&mut server_rng);
//! ```
//!
@@ -71,14 +71,14 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vexahub::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! use rand::rngs::OsRng;
//! use rand::RngCore;
//! use voprf::OprfClient;
//! # type CipherSuite = voprf_vexahub::P256CipherSuite;
//! use rand::rngs::SysRng;
//! use rand::Rng;
//! use voprf_vexahub::OprfClient;
//!
//! let mut client_rng = OsRng;
//! let mut client_rng = SysRng;
//! let client_blind_result = OprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
//! .expect("Unable to construct client");
//! ```
@@ -92,19 +92,19 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vexahub::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::OprfClient;
//! # use rand::{rngs::OsRng, RngCore};
//! # type CipherSuite = voprf_vexahub::P256CipherSuite;
//! # use voprf_vexahub::OprfClient;
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_rng = SysRng;
//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf::OprfServer;
//! # let mut server_rng = OsRng;
//! # use voprf_vexahub::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);
//! ```
@@ -117,19 +117,19 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vexahub::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::OprfClient;
//! # use rand::{rngs::OsRng, RngCore};
//! # type CipherSuite = voprf_vexahub::P256CipherSuite;
//! # use voprf_vexahub::OprfClient;
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_rng = SysRng;
//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf::OprfServer;
//! # let mut server_rng = OsRng;
//! # use voprf_vexahub::OprfServer;
//! # let mut server_rng = SysRng;
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let message = server.blind_evaluate(&client_blind_result.message);
//! let client_finalize_result = client_blind_result
@@ -150,19 +150,19 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vexahub::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::OprfClient;
//! # use rand::{rngs::OsRng, RngCore};
//! # type CipherSuite = voprf_vexahub::P256CipherSuite;
//! # use voprf_vexahub::OprfClient;
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_rng = SysRng;
//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf::OprfServer;
//! # let mut server_rng = OsRng;
//! # use voprf_vexahub::OprfServer;
//! # let mut server_rng = SysRng;
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let message = server.blind_evaluate(&client_blind_result.message);
//! let client_finalize_result = client_blind_result
@@ -197,14 +197,14 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vexahub::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! use rand::rngs::OsRng;
//! use rand::RngCore;
//! use voprf::VoprfServer;
//! # type CipherSuite = voprf_vexahub::P256CipherSuite;
//! use rand::rngs::SysRng;
//! use rand::Rng;
//! use voprf_vexahub::VoprfServer;
//!
//! let mut server_rng = OsRng;
//! let mut server_rng = SysRng;
//! let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//!
//! // To be sent to the client
@@ -224,14 +224,14 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vexahub::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! use rand::rngs::OsRng;
//! use rand::RngCore;
//! use voprf::VoprfClient;
//! # type CipherSuite = voprf_vexahub::P256CipherSuite;
//! use rand::rngs::SysRng;
//! use rand::Rng;
//! use voprf_vexahub::VoprfClient;
//!
//! let mut client_rng = OsRng;
//! let mut client_rng = SysRng;
//! let client_blind_result = VoprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
//! .expect("Unable to construct client");
//! ```
@@ -246,19 +246,19 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vexahub::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::{VoprfServerEvaluateResult, VoprfClient};
//! # use rand::{rngs::OsRng, RngCore};
//! # type CipherSuite = voprf_vexahub::P256CipherSuite;
//! # use voprf_vexahub::{VoprfServerEvaluateResult, VoprfClient};
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_rng = SysRng;
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf::VoprfServer;
//! # let mut server_rng = OsRng;
//! # use voprf_vexahub::VoprfServer;
//! # let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! let VoprfServerEvaluateResult { message, proof } =
//! server.blind_evaluate(&mut server_rng, &client_blind_result.message);
@@ -273,19 +273,19 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vexahub::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::VoprfClient;
//! # use rand::{rngs::OsRng, RngCore};
//! # type CipherSuite = voprf_vexahub::P256CipherSuite;
//! # use voprf_vexahub::VoprfClient;
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_rng = SysRng;
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf::VoprfServer;
//! # let mut server_rng = OsRng;
//! # use voprf_vexahub::VoprfServer;
//! # let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let server_evaluate_result = server.blind_evaluate(
//! # &mut server_rng,
@@ -314,19 +314,19 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vexahub::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::VoprfClient;
//! # use rand::{rngs::OsRng, RngCore};
//! # type CipherSuite = voprf_vexahub::P256CipherSuite;
//! # use voprf_vexahub::VoprfClient;
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_rng = SysRng;
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf::VoprfServer;
//! # let mut server_rng = OsRng;
//! # use voprf_vexahub::VoprfServer;
//! # let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let server_evaluate_result = server.blind_evaluate(
//! # &mut server_rng,
@@ -368,13 +368,13 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vexahub::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::VoprfClient;
//! # use rand::{rngs::OsRng, RngCore};
//! # type CipherSuite = voprf_vexahub::P256CipherSuite;
//! # use voprf_vexahub::VoprfClient;
//! # use rand::{rngs::SysRng, Rng};
//! #
//! let mut client_rng = OsRng;
//! let mut client_rng = SysRng;
//! let mut client_states = vec![];
//! let mut client_messages = vec![];
//! for _ in 0..10 {
@@ -392,13 +392,13 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vexahub::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::{VoprfServerBatchEvaluateFinishResult, VoprfClient};
//! # use rand::{rngs::OsRng, RngCore};
//! # type CipherSuite = voprf_vexahub::P256CipherSuite;
//! # use voprf_vexahub::{VoprfServerBatchEvaluateFinishResult, VoprfClient};
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_rng = SysRng;
//! # let mut client_states = vec![];
//! # let mut client_messages = vec![];
//! # for _ in 0..10 {
@@ -409,8 +409,8 @@
//! # client_states.push(client_blind_result.state);
//! # client_messages.push(client_blind_result.message);
//! # }
//! # use voprf::VoprfServer;
//! let mut server_rng = OsRng;
//! # use voprf_vexahub::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());
//! let prepared_elements: Vec<_> = prepared_evaluation_elements.collect();
@@ -426,13 +426,13 @@
//! ```
//! # #[cfg(feature = "alloc")] {
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vexahub::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::{VoprfServerBatchEvaluateResult, VoprfClient};
//! # use rand::{rngs::OsRng, RngCore};
//! # type CipherSuite = voprf_vexahub::P256CipherSuite;
//! # use voprf_vexahub::{VoprfServerBatchEvaluateResult, VoprfClient};
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_rng = SysRng;
//! # let mut client_states = vec![];
//! # let mut client_messages = vec![];
//! # for _ in 0..10 {
@@ -443,8 +443,8 @@
//! # client_states.push(client_blind_result.state);
//! # client_messages.push(client_blind_result.message);
//! # }
//! # use voprf::VoprfServer;
//! let mut server_rng = OsRng;
//! # use voprf_vexahub::VoprfServer;
//! let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! let VoprfServerBatchEvaluateResult { messages, proof } = server
//! .batch_blind_evaluate(&mut server_rng, &client_messages)
@@ -460,13 +460,13 @@
//! ```
//! # #[cfg(feature = "alloc")] {
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vexahub::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::{VoprfServerBatchEvaluateResult, VoprfClient};
//! # use rand::{rngs::OsRng, RngCore};
//! # type CipherSuite = voprf_vexahub::P256CipherSuite;
//! # use voprf_vexahub::{VoprfServerBatchEvaluateResult, VoprfClient};
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_rng = SysRng;
//! # let mut client_states = vec![];
//! # let mut client_messages = vec![];
//! # for _ in 0..10 {
@@ -477,8 +477,8 @@
//! # client_states.push(client_blind_result.state);
//! # client_messages.push(client_blind_result.message);
//! # }
//! # use voprf::VoprfServer;
//! # let mut server_rng = OsRng;
//! # use voprf_vexahub::VoprfServer;
//! # let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let VoprfServerBatchEvaluateResult { messages, proof } = server
//! # .batch_blind_evaluate(&mut server_rng, &client_messages)
@@ -566,6 +566,18 @@ mod voprf;
#[cfg(test)]
mod tests;
#[cfg(feature = "doctest")]
#[derive(Debug)]
#[doc(hidden)]
pub struct P256CipherSuite;
#[cfg(feature = "doctest")]
impl CipherSuite for P256CipherSuite {
const ID: &'static [u8] = b"P256-SHA256";
type Group = p256::NistP256;
type Hash = sha2::Sha256;
}
// Exports
pub use crate::ciphersuite::CipherSuite;
+18 -18
View File
@@ -12,13 +12,13 @@ use core::iter::{self, Map};
use derive_where::derive_where;
use digest::{Digest, Output};
use generic_array::typenum::Unsigned;
use generic_array::GenericArray;
use rand_core::{TryCryptoRng, TryRngCore};
use hybrid_array::Array;
use hybrid_array::typenum::Unsigned;
use rand_core::{TryCryptoRng, TryRng};
use crate::common::{
derive_key_internal, deterministic_blind_unchecked, hash_to_group, i2osp_2,
server_evaluate_hash_input, BlindedElement, EvaluationElement, Mode, STR_FINALIZE,
BlindedElement, EvaluationElement, Mode, STR_FINALIZE, derive_key_internal,
deterministic_blind_unchecked, hash_to_group, i2osp_2, server_evaluate_hash_input,
};
#[cfg(feature = "serde")]
use crate::serialization::serde::Scalar;
@@ -73,7 +73,7 @@ impl<CS: CipherSuite> OprfClient<CS> {
///
/// # Errors
/// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`].
pub fn blind<R: TryRngCore + TryCryptoRng>(
pub fn blind<R: TryRng + TryCryptoRng>(
input: &[u8],
blinding_factor_rng: &mut R,
) -> Result<OprfClientBlindResult<CS>> {
@@ -146,8 +146,8 @@ impl<CS: CipherSuite> OprfServer<CS> {
///
/// # Errors
/// [`Error::Protocol`] if the protocol fails and can't be completed.
pub fn new<R: TryRngCore + TryCryptoRng>(rng: &mut R) -> Result<Self> {
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
pub fn new<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self> {
let mut seed = Array::<_, <CS::Group as Group>::ScalarLen>::default();
rng.try_fill_bytes(&mut seed).map_err(|_| Error::Protocol)?;
Self::new_from_seed(&seed, &[])
}
@@ -267,12 +267,12 @@ fn finalize_after_unblind<
mod tests {
use core::ptr;
use rand::rngs::OsRng;
use rand::TryRngCore;
use rand::TryRng;
use rand::rngs::SysRng;
use super::*;
use crate::common::{Dst, STR_HASH_TO_GROUP};
use crate::Group;
use crate::common::{Dst, STR_HASH_TO_GROUP};
fn prf<CS: CipherSuite>(
input: &[u8],
@@ -280,7 +280,7 @@ mod tests {
info: &[u8],
mode: Mode,
) -> Output<CS::Hash> {
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, mode);
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;
@@ -293,7 +293,7 @@ mod tests {
fn base_retrieval<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = OprfServer::<CS>::new(&mut rng).unwrap();
let message = server.blind_evaluate(&client_blind_result.message);
@@ -303,7 +303,7 @@ mod tests {
}
fn base_inversion_unsalted<CS: CipherSuite>() {
let mut rng = OsRng;
let mut rng = SysRng;
let mut input = [0u8; 64];
rng.try_fill_bytes(&mut input).unwrap();
let client_blind_result = OprfClient::<CS>::blind(&input, &mut rng).unwrap();
@@ -312,7 +312,7 @@ mod tests {
.finalize(&input, &EvaluationElement(client_blind_result.message.0))
.unwrap();
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 res2 = finalize_after_unblind::<CS, _, _>(iter::once((input.as_ref(), point)), &[])
.next()
@@ -324,7 +324,7 @@ mod tests {
fn server_evaluate<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = OprfServer::<CS>::new(&mut rng).unwrap();
let server_result = server.blind_evaluate(&client_blind_result.message);
@@ -348,7 +348,7 @@ mod tests {
fn zeroize_oprf_client<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
let mut state = client_blind_result.state;
@@ -362,7 +362,7 @@ mod tests {
fn zeroize_oprf_server<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = OprfServer::<CS>::new(&mut rng).unwrap();
let mut message = server.blind_evaluate(&client_blind_result.message);
+26 -26
View File
@@ -14,14 +14,14 @@ use core::iter::{self, Map, Repeat, Zip};
use derive_where::derive_where;
use digest::{Digest, Output, OutputSizeUser};
use generic_array::typenum::Unsigned;
use generic_array::{ArrayLength, GenericArray};
use rand_core::{TryCryptoRng, TryRngCore};
use hybrid_array::typenum::Unsigned;
use hybrid_array::{Array, ArraySize};
use rand_core::{TryCryptoRng, TryRng};
use crate::common::{
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,
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,
};
#[cfg(feature = "serde")]
use crate::serialization::serde::{Element, Scalar};
@@ -75,7 +75,7 @@ impl<CS: CipherSuite> PoprfClient<CS> {
///
/// # Errors
/// [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`].
pub fn blind<R: TryRngCore + TryCryptoRng>(
pub fn blind<R: TryRng + TryCryptoRng>(
input: &[u8],
blinding_factor_rng: &mut R,
) -> Result<PoprfClientBlindResult<CS>> {
@@ -134,7 +134,7 @@ impl<CS: CipherSuite> PoprfClient<CS> {
info: Option<&[u8]>,
) -> Result<Output<CS::Hash>>
where
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArrayLength,
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArraySize,
{
let clients = core::array::from_ref(self);
let messages = core::array::from_ref(evaluation_element);
@@ -170,7 +170,7 @@ impl<CS: CipherSuite> PoprfClient<CS> {
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
<&'a IM as IntoIterator>::IntoIter: ExactSizeIterator,
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArrayLength,
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArraySize,
{
let unblinded_elements = poprf_unblind(clients, messages, pk, proof, info)?;
@@ -189,8 +189,8 @@ impl<CS: CipherSuite> PoprfServer<CS> {
///
/// # Errors
/// [`Error::Protocol`] if the protocol fails and can't be completed.
pub fn new<R: TryRngCore + TryCryptoRng>(rng: &mut R) -> Result<Self> {
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
pub fn new<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self> {
let mut seed = Array::<_, <CS::Group as Group>::ScalarLen>::default();
rng.try_fill_bytes(&mut seed).map_err(|_| Error::Protocol)?;
Self::new_from_seed(&seed, &[])
@@ -235,7 +235,7 @@ impl<CS: CipherSuite> PoprfServer<CS> {
/// # Errors
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
pub fn blind_evaluate<R: TryRngCore + TryCryptoRng>(
pub fn blind_evaluate<R: TryRng + TryCryptoRng>(
&self,
rng: &mut R,
blinded_element: &BlindedElement<CS>,
@@ -273,7 +273,7 @@ impl<CS: CipherSuite> PoprfServer<CS> {
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
#[cfg(feature = "alloc")]
pub fn batch_blind_evaluate<'a, R: TryRngCore + TryCryptoRng, IE>(
pub fn batch_blind_evaluate<'a, R: TryRng + TryCryptoRng, IE>(
&self,
rng: &mut R,
blinded_elements: &'a IE,
@@ -346,7 +346,7 @@ impl<CS: CipherSuite> PoprfServer<CS> {
pub fn batch_blind_evaluate_finish<
'a,
'b,
R: TryRngCore + TryCryptoRng,
R: TryRng + TryCryptoRng,
IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator,
IE,
>(
@@ -569,7 +569,7 @@ fn compute_tweaked_key<CS: CipherSuite>(
let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
let framed_info = [STR_INFO.as_slice(), &info_len, info];
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, Mode::Poprf);
let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, Mode::Poprf);
// This can't fail, the size of the `input` is known.
let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst.as_dst()).unwrap();
@@ -602,7 +602,7 @@ fn compute_tweak<CS: CipherSuite>(
let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
let framed_info = [STR_INFO.as_slice(), &info_len, info];
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, Mode::Poprf);
let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, Mode::Poprf);
// This can't fail, the size of the `input` is known.
let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst.as_dst()).unwrap();
@@ -692,7 +692,7 @@ fn finalize_after_unblind<
info: Option<&'a [u8]>,
) -> Result<FinalizeAfterUnblindResult<'a, CS, IE, II>>
where
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArrayLength,
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArraySize,
{
if unblinded_elements.len() != inputs.len() {
return Err(Error::Batch);
@@ -733,11 +733,11 @@ where
mod tests {
use core::ptr;
use rand::rngs::OsRng;
use rand::rngs::SysRng;
use super::*;
use crate::common::STR_HASH_TO_GROUP;
use crate::Group;
use crate::common::STR_HASH_TO_GROUP;
fn prf<CS: CipherSuite>(
input: &[u8],
@@ -747,7 +747,7 @@ mod tests {
) -> Output<CS::Hash> {
let t = compute_tweak::<CS>(key, Some(info)).unwrap();
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, mode);
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, mode);
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
// evaluatedElement = G.ScalarInverse(t) * blindedElement
@@ -763,7 +763,7 @@ mod tests {
fn verifiable_retrieval<CS: CipherSuite>() {
let input = b"input";
let info = b"info";
let mut rng = OsRng;
let mut rng = SysRng;
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server_result = server
@@ -786,14 +786,14 @@ mod tests {
fn verifiable_bad_public_key<CS: CipherSuite>() {
let input = b"input";
let info = b"info";
let mut rng = OsRng;
let mut rng = SysRng;
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server_result = server
.blind_evaluate(&mut rng, &client_blind_result.message, Some(info))
.unwrap();
let wrong_pk = {
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, Mode::Oprf);
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, Mode::Oprf);
// Choose a group element that is unlikely to be the right public key
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
};
@@ -810,7 +810,7 @@ mod tests {
fn verifiable_server_evaluate<CS: CipherSuite>() {
let input = b"input";
let info = Some(b"info".as_slice());
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
let server_result = server
@@ -842,7 +842,7 @@ mod tests {
fn zeroize_verifiable_client<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
let mut state = client_blind_result.state;
@@ -857,7 +857,7 @@ mod tests {
fn zeroize_verifiable_server<CS: CipherSuite>() {
let input = b"input";
let info = b"info";
let mut rng = OsRng;
let mut rng = SysRng;
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server_result = server
+14 -15
View File
@@ -9,9 +9,8 @@
//! Handles the serialization of each of the components used in the VOPRF
//! protocol
use generic_array::sequence::Concat;
use generic_array::typenum::{Sum, Unsigned};
use generic_array::GenericArray;
use hybrid_array::Array;
use hybrid_array::typenum::{Sum, Unsigned};
use crate::{
BlindedElement, CipherSuite, Error, EvaluationElement, Group, OprfClient, OprfServer,
@@ -28,7 +27,7 @@ pub type OprfClientLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
impl<CS: CipherSuite> OprfClient<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, OprfClientLen<CS>> {
pub fn serialize(&self) -> Array<u8, OprfClientLen<CS>> {
CS::Group::serialize_scalar(self.blind)
}
@@ -51,7 +50,7 @@ pub type VoprfClientLen<CS> = Sum<
impl<CS: CipherSuite> VoprfClient<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, VoprfClientLen<CS>> {
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))
}
@@ -79,7 +78,7 @@ pub type PoprfClientLen<CS> = Sum<
impl<CS: CipherSuite> PoprfClient<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, PoprfClientLen<CS>> {
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))
}
@@ -104,7 +103,7 @@ pub type OprfServerLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
impl<CS: CipherSuite> OprfServer<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, OprfServerLen<CS>> {
pub fn serialize(&self) -> Array<u8, OprfServerLen<CS>> {
CS::Group::serialize_scalar(self.sk)
}
@@ -127,7 +126,7 @@ pub type VoprfServerLen<CS> = Sum<
impl<CS: CipherSuite> VoprfServer<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, VoprfServerLen<CS>> {
pub fn serialize(&self) -> Array<u8, VoprfServerLen<CS>> {
CS::Group::serialize_scalar(self.sk).concat(CS::Group::serialize_elem(self.pk))
}
@@ -151,7 +150,7 @@ pub type PoprfServerLen<CS> = Sum<
impl<CS: CipherSuite> PoprfServer<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, PoprfServerLen<CS>> {
pub fn serialize(&self) -> Array<u8, PoprfServerLen<CS>> {
CS::Group::serialize_scalar(self.sk).concat(CS::Group::serialize_elem(self.pk))
}
@@ -175,7 +174,7 @@ pub type ProofLen<CS> = Sum<
impl<CS: CipherSuite> Proof<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, ProofLen<CS>> {
pub fn serialize(&self) -> Array<u8, ProofLen<CS>> {
CS::Group::serialize_scalar(self.c_scalar)
.concat(CS::Group::serialize_scalar(self.s_scalar))
}
@@ -197,7 +196,7 @@ pub type BlindedElementLen<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen;
impl<CS: CipherSuite> BlindedElement<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, BlindedElementLen<CS>> {
pub fn serialize(&self) -> Array<u8, BlindedElementLen<CS>> {
CS::Group::serialize_elem(self.0)
}
@@ -217,7 +216,7 @@ pub type EvaluationElementLen<CS> = <<CS as CipherSuite>::Group as Group>::ElemL
impl<CS: CipherSuite> EvaluationElement<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, EvaluationElementLen<CS>> {
pub fn serialize(&self) -> Array<u8, EvaluationElementLen<CS>> {
CS::Group::serialize_elem(self.0)
}
@@ -266,7 +265,7 @@ impl<T> SliceExt for [T] {
pub(crate) mod serde {
use core::marker::PhantomData;
use generic_array::GenericArray;
use hybrid_array::Array;
use serde::de::{Deserializer, Error};
use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
@@ -280,7 +279,7 @@ pub(crate) mod serde {
where
D: Deserializer<'de>,
{
GenericArray::<_, G::ElemLen>::deserialize(deserializer)
Array::<_, G::ElemLen>::deserialize(deserializer)
.and_then(|bytes| G::deserialize_elem(&bytes).map_err(D::Error::custom))
}
@@ -299,7 +298,7 @@ pub(crate) mod serde {
where
D: Deserializer<'de>,
{
GenericArray::<_, G::ScalarLen>::deserialize(deserializer)
Array::<_, G::ScalarLen>::deserialize(deserializer)
.and_then(|bytes| G::deserialize_scalar(&bytes).map_err(D::Error::custom))
}
+23 -10
View File
@@ -9,7 +9,8 @@
use alloc::vec::Vec;
use core::cmp::min;
use rand_core::{CryptoRng, RngCore};
use core::convert::Infallible;
use rand_core::{TryCryptoRng, TryRng};
/// A simple implementation of `RngCore` for testing purposes.
///
@@ -38,23 +39,35 @@ fn rotate_left<T>(data: &mut [T], steps: usize) {
data.reverse();
}
impl RngCore for CycleRng {
fn next_u32(&mut self) -> u32 {
unimplemented!()
impl TryRng for CycleRng {
type Error = Infallible;
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
let mut buf = [0u8; 4];
self.try_fill_bytes(&mut buf)?;
Ok(u32::from_le_bytes(buf))
}
#[inline]
fn next_u64(&mut self) -> u64 {
unimplemented!()
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
let mut buf = [0u8; 8];
self.try_fill_bytes(&mut buf)?;
Ok(u64::from_le_bytes(buf))
}
#[inline]
fn fill_bytes(&mut self, dest: &mut [u8]) {
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
let len = min(self.v.len(), dest.len());
dest[..len].copy_from_slice(&self.v[..len]);
rotate_left(&mut self.v, len);
Ok(())
}
}
// This is meant for testing only
impl CryptoRng for CycleRng {}
impl TryCryptoRng for CycleRng {}
+18
View File
@@ -10,3 +10,21 @@ 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;
}
+7 -12
View File
@@ -15,55 +15,50 @@ pub(crate) fn rfc_to_json(input: &str) -> String {
}
fn parse_ciphersuites(input: &str) -> String {
let re = regex::Regex::new(r"\nA\.\d\. (?P<ciphersuite>.+?)\n\n").unwrap();
let re = regex::Regex::new(r"\nA\.\d\. {2}(?P<ciphersuite>.+?)\n\n").unwrap();
let mut ciphersuites = vec![];
let chunks: Vec<&str> = re.split(input).collect();
let mut count = 1;
for caps in re.captures_iter(input) {
for (count, caps) in (1..).zip(re.captures_iter(input)) {
let ciphersuite = format!(
"\"{}\": {{ {} }}",
&caps["ciphersuite"],
parse_modes(chunks[count])
);
ciphersuites.push(ciphersuite);
count += 1;
}
ciphersuites.join(",\n")
}
fn parse_modes(input: &str) -> String {
let re = regex::Regex::new(r"A\.\d.\d\. (?P<mode>.*?) Mode").unwrap();
let re = regex::Regex::new(r"A\.\d.\d\. {2}(?P<mode>.*?) Mode").unwrap();
let mut modes = vec![];
let chunks: Vec<&str> = re.split(input).collect();
let mut count = 1;
for caps in re.captures_iter(input) {
for (count, caps) in (1..).zip(re.captures_iter(input)) {
let mode = format!(
"\"{}\": [\n {} \n]",
&caps["mode"],
parse_vectors(chunks[count])
);
modes.push(mode);
count += 1;
}
modes.join(",\n")
}
fn parse_vectors(input: &str) -> String {
let re = regex::Regex::new(r"A\.\d.\d\.\d\. Test Vector.*+\n").unwrap();
let re = regex::Regex::new(r"A\.\d.\d\.\d\. {2}Test Vector.*+\n").unwrap();
let mut vectors = vec![];
let chunks: Vec<&str> = re.split(input).collect();
let init_params = parse_params(chunks[0]);
let mut count = 1;
for _ in re.captures_iter(input) {
for (count, _) in (1..).zip(re.captures_iter(input)) {
let params = format!("{{\n{},\n{}\n}}", init_params, parse_params(chunks[count]));
vectors.push(params);
count += 1;
}
vectors.join(",\n")
+3 -4
View File
@@ -360,7 +360,7 @@ fn test_voprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters])
assert_eq!(&parameter, &message.serialize().as_slice());
}
assert_eq!(&parameters.proof, &proof.serialize().as_slice());
assert_eq!(&parameters.proof, &proof.serialize().to_vec());
}
Ok(())
}
@@ -386,8 +386,7 @@ fn test_poprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters])
blinded_elements.iter(),
&prepared_evaluation_elements,
&prepared_tweak,
)
.unwrap();
)?;
let messages: Vec<_> = messages.collect();
@@ -395,7 +394,7 @@ fn test_poprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters])
assert_eq!(&parameter, &message.serialize().as_slice());
}
assert_eq!(&parameters.proof, &proof.serialize().as_slice());
assert_eq!(&parameters.proof, &proof.serialize().to_vec());
}
Ok(())
}
+23 -23
View File
@@ -14,14 +14,14 @@ use core::iter::{self, Map, Repeat, Zip};
use derive_where::derive_where;
use digest::{Digest, Output};
use generic_array::typenum::Unsigned;
use generic_array::GenericArray;
use rand_core::{TryCryptoRng, TryRngCore};
use hybrid_array::Array;
use hybrid_array::typenum::Unsigned;
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, BlindedElement, EvaluationElement, Mode,
PreparedEvaluationElement, Proof, STR_FINALIZE,
server_evaluate_hash_input, verify_proof,
};
#[cfg(feature = "serde")]
use crate::serialization::serde::{Element, Scalar};
@@ -75,7 +75,7 @@ impl<CS: CipherSuite> VoprfClient<CS> {
///
/// # Errors
/// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`].
pub fn blind<R: TryRngCore + TryCryptoRng>(
pub fn blind<R: TryRng + TryCryptoRng>(
input: &[u8],
blinding_factor_rng: &mut R,
) -> Result<VoprfClientBlindResult<CS>> {
@@ -196,8 +196,8 @@ impl<CS: CipherSuite> VoprfServer<CS> {
///
/// # Errors
/// [`Error::Protocol`] if the protocol fails and can't be completed.
pub fn new<R: TryRngCore + TryCryptoRng>(rng: &mut R) -> Result<Self> {
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
pub fn new<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self> {
let mut seed = Array::<_, <CS::Group as Group>::ScalarLen>::default();
rng.try_fill_bytes(&mut seed).map_err(|_| Error::Protocol)?;
// This can't fail as the hash output is type constrained.
Self::new_from_seed(&seed, &[])
@@ -238,7 +238,7 @@ impl<CS: CipherSuite> VoprfServer<CS> {
/// Computes the second step for the multiplicative blinding version of
/// DH-OPRF. This message is sent from the server (who holds the OPRF key)
/// to the client.
pub fn blind_evaluate<R: TryRngCore + TryCryptoRng>(
pub fn blind_evaluate<R: TryRng + TryCryptoRng>(
&self,
rng: &mut R,
blinded_element: &BlindedElement<CS>,
@@ -271,7 +271,7 @@ impl<CS: CipherSuite> VoprfServer<CS> {
/// [`Error::Batch`] if the number of `blinded_elements` and
/// `evaluation_elements` don't match or is longer then [`u16::MAX`]
#[cfg(feature = "alloc")]
pub fn batch_blind_evaluate<'a, R: TryRngCore + TryCryptoRng, I>(
pub fn batch_blind_evaluate<'a, R: TryRng + TryCryptoRng, I>(
&self,
rng: &mut R,
blinded_elements: &'a I,
@@ -322,7 +322,7 @@ impl<CS: CipherSuite> VoprfServer<CS> {
pub fn batch_blind_evaluate_finish<
'a,
'b,
R: TryRngCore + TryCryptoRng,
R: TryRng + TryCryptoRng,
IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator,
IE,
>(
@@ -550,18 +550,18 @@ mod tests {
use ::alloc::vec;
use ::alloc::vec::Vec;
use rand::rngs::OsRng;
use rand::rngs::SysRng;
use super::*;
use crate::common::{Dst, STR_HASH_TO_GROUP};
use crate::Group;
use crate::common::{Dst, STR_HASH_TO_GROUP};
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 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;
@@ -574,7 +574,7 @@ mod tests {
fn verifiable_retrieval<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
@@ -592,7 +592,7 @@ mod tests {
}
fn verifiable_batch_retrieval<CS: CipherSuite>() {
let mut rng = OsRng;
let mut rng = SysRng;
let mut inputs = vec![];
let mut client_states = vec![];
let mut client_messages = vec![];
@@ -636,7 +636,7 @@ mod tests {
}
fn verifiable_batch_bad_public_key<CS: CipherSuite>() {
let mut rng = OsRng;
let mut rng = SysRng;
let mut inputs = vec![];
let mut client_states = vec![];
let mut client_messages = vec![];
@@ -662,7 +662,7 @@ mod tests {
.unwrap();
let messages: Vec<_> = messages.collect();
let wrong_pk = {
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, Mode::Oprf);
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, Mode::Oprf);
// Choose a group element that is unlikely to be the right public key
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
};
@@ -673,12 +673,12 @@ mod tests {
fn verifiable_bad_public_key<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
let wrong_pk = {
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, Mode::Oprf);
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, Mode::Oprf);
// Choose a group element that is unlikely to be the right public key
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
};
@@ -693,7 +693,7 @@ mod tests {
fn verifiable_server_evaluate<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
@@ -722,7 +722,7 @@ mod tests {
fn zeroize_voprf_client<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
let mut state = client_blind_result.state;
@@ -736,7 +736,7 @@ mod tests {
fn zeroize_voprf_server<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);