Compare commits
84
Commits
v0.3.0
...
e4c8dd3419
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4c8dd3419
|
||
|
|
8d462be1f7
|
||
|
|
201fb57d9f
|
||
|
|
ef59638de0
|
||
|
|
313f1b66bb
|
||
|
|
0bbf8fbfb5
|
||
|
|
1451f937ca
|
||
|
|
eb00b86000
|
||
|
|
385ee9b1cc
|
||
|
|
51157080fc
|
||
|
|
617fc0241c
|
||
|
|
9f9dc23aa1
|
||
|
|
405c1901ef
|
||
|
|
3ce3ac8817
|
||
|
|
d42e7948a1
|
||
|
|
0a7dc184ca | ||
|
|
f5b7e689e7 | ||
|
|
a22d46fd96 | ||
|
|
f23cdfab2d | ||
|
|
256ab7bc52 | ||
|
|
eb55e9f5b5 | ||
|
|
e944f9db3b | ||
|
|
f3f4fef0e9 | ||
|
|
23aa7813e7 | ||
|
|
0473d9db68 | ||
|
|
f0531f0812 | ||
|
|
c93884aca3 | ||
|
|
40769f7eca | ||
|
|
1b67086028 | ||
|
|
68cc7d3709 | ||
|
|
59e3fedb21 | ||
|
|
8da56845b8 | ||
|
|
c0162ec8d9 | ||
|
|
ee91c9776c | ||
|
|
0fdfdfdaee | ||
|
|
eafa134c94 | ||
|
|
209b957ae4 | ||
|
|
f79ebf9844 | ||
|
|
20a35da7ba | ||
|
|
4bd2cf466e | ||
|
|
829c6add0f | ||
|
|
8b895cc631 | ||
|
|
83eb78b232 | ||
|
|
5badeff8d2 | ||
|
|
40d81294db | ||
|
|
8363d26f6f | ||
|
|
5bce3e3206 | ||
|
|
2787151e1d | ||
|
|
0409db6f40 | ||
|
|
74eaebe446 | ||
|
|
c8de51672b | ||
|
|
daa8dc048f | ||
|
|
2a351ceb4d | ||
|
|
8f60a10b8d | ||
|
|
1691125b09 | ||
|
|
6913b5deaa | ||
|
|
2dc6a8b2c2 | ||
|
|
f670733165 | ||
|
|
6e16a99a87 | ||
|
|
4646fe4ad0 | ||
|
|
f26f5d2c57 | ||
|
|
dfa88efeca | ||
|
|
0b3544fc79 | ||
|
|
1a61401272 | ||
|
|
a366a14125 | ||
|
|
8277383fc3 | ||
|
|
1af4f470c3 | ||
|
|
f8e0600b22 | ||
|
|
9eee936140 | ||
|
|
b59b359aa3 | ||
|
|
b01b8ed409 | ||
|
|
16e072dcd4 | ||
|
|
652fd1d1d0 | ||
|
|
e7675437e6 | ||
|
|
d316ce4c8d | ||
|
|
1c5e965446 | ||
|
|
55ef981a3f | ||
|
|
b2f6d5eac8 | ||
|
|
5228474f05 | ||
|
|
6669a0c4e6 | ||
|
|
140f9e063d | ||
|
|
4700d4b725 | ||
|
|
b1b315f23c | ||
|
|
7613610859 |
@@ -0,0 +1,7 @@
|
||||
// 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.
|
||||
@@ -0,0 +1,102 @@
|
||||
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
|
||||
@@ -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 }}
|
||||
@@ -1,125 +0,0 @@
|
||||
name: Rust CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, repoened, synchronize]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
backend_feature:
|
||||
- ristretto255_u64
|
||||
- ristretto255_u32
|
||||
- p256,ristretto255_u64
|
||||
frontend_feature:
|
||||
- serde
|
||||
- danger
|
||||
toolchain:
|
||||
- stable
|
||||
- 1.51.0
|
||||
name: test
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install ${{ matrix.toolchain }} toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
override: true
|
||||
|
||||
- name: Run cargo test
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --no-default-features --features ${{ matrix.backend_feature }}
|
||||
|
||||
- name: Run cargo test with std
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --no-default-features --features ${{ matrix.frontend_feature }},std --features ${{ matrix.backend_feature }}
|
||||
|
||||
build-no-std:
|
||||
name: Build with no-std on ${{ matrix.target }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target:
|
||||
# for wasm
|
||||
- wasm32-unknown-unknown
|
||||
# for any no_std target
|
||||
- thumbv6m-none-eabi
|
||||
backend_feature:
|
||||
-
|
||||
- --features ristretto255_u64
|
||||
- --features ristretto255_u32
|
||||
- --features p256
|
||||
frontend_feature:
|
||||
-
|
||||
- --features serde
|
||||
- --features danger
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: hecrj/setup-rust-action@v1
|
||||
- run: rustup target add ${{ matrix.target }}
|
||||
- run: cargo build --verbose --target=${{ matrix.target }} --no-default-features ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }}
|
||||
|
||||
|
||||
clippy:
|
||||
name: cargo clippy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: clippy
|
||||
|
||||
- name: Run cargo clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --all-targets -- -D warnings
|
||||
|
||||
- name: Run cargo doc
|
||||
uses: actions-rs/cargo@v1
|
||||
env:
|
||||
RUSTDOCFLAGS: -D warnings
|
||||
with:
|
||||
command: doc
|
||||
args: --no-deps --document-private-items --features std,p256
|
||||
|
||||
|
||||
format:
|
||||
name: cargo fmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt
|
||||
|
||||
- name: Run cargo fmt
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
@@ -1,28 +0,0 @@
|
||||
name: Publish
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- uses: hecrj/setup-rust-action@v1
|
||||
with:
|
||||
rust-version: ${{ matrix.rust }}
|
||||
- uses: actions/checkout@master
|
||||
- name: Login to crates.io
|
||||
run: cargo login $CRATES_IO_TOKEN
|
||||
env:
|
||||
CRATES_IO_TOKEN: ${{ secrets.crates_io_token }}
|
||||
- name: Dry run publish voprf
|
||||
run: cargo publish --dry-run --manifest-path Cargo.toml
|
||||
- name: Publish voprf
|
||||
run: cargo publish --manifest-path Cargo.toml
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.crates_io_token }}
|
||||
+7
-5
@@ -1,10 +1,12 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
debug/
|
||||
target/
|
||||
pkg/
|
||||
demo/
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
+108
@@ -1,5 +1,113 @@
|
||||
# Changelog
|
||||
|
||||
## 1.0.0 (July 8, 2026)
|
||||
|
||||
* Deduplicated serialization with `impl_serde_scalar`, `impl_serde_elem`, and `impl_serde_scalar_elem` macros
|
||||
* Moved `finalize_after_unblind` to `common.rs`, shared by OPRF and VOPRF
|
||||
* Added shared test helpers (`test_all_curves` macro, `prf` function)
|
||||
* Updated dependencies to stable releases
|
||||
|
||||
## 1.0.0-rc.1 (July 3, 2026)
|
||||
|
||||
* 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`.
|
||||
|
||||
* 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 `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>`
|
||||
|
||||
## 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
|
||||
* Updated subtle dependency to 2.6
|
||||
* 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
|
||||
directly
|
||||
* Renames the former evaluate() function to blind_evaluate to match the spec
|
||||
* Fixes the order of parameters for PoprfClient::blind to align it with the
|
||||
other clients
|
||||
* Exposes the derive_key function under the "danger" feature
|
||||
* Added support for running the API without performing allocations
|
||||
* Revamped the way the Group trait was used, so as to be more easily
|
||||
extendable to other groups
|
||||
* Added common traits for each public-facing struct, including serde
|
||||
support
|
||||
|
||||
## 0.3.0 (October 25, 2021)
|
||||
|
||||
* Updated to be in sync with draft-irtf-cfrg-voprf-08
|
||||
|
||||
@@ -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
|
||||
+3
-21
@@ -2,29 +2,11 @@
|
||||
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 the LICENSE file in the root directory of this source tree.
|
||||
By contributing to voprf-vx, 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.
|
||||
|
||||
Generated
+855
@@ -0,0 +1,855 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||
|
||||
[[package]]
|
||||
name = "base16ct"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6"
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "chacha20"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cmov"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
|
||||
|
||||
[[package]]
|
||||
name = "cpubits"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-bigint"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a52aa3fcda4e6302a9f48734f234d35d4721b96f8fe07d073f07ce9df4f0271"
|
||||
dependencies = [
|
||||
"cpubits",
|
||||
"ctutils",
|
||||
"hybrid-array",
|
||||
"num-traits",
|
||||
"rand_core 0.10.1",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctutils"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
|
||||
dependencies = [
|
||||
"cmov",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "5.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"curve25519-dalek-derive",
|
||||
"digest",
|
||||
"fiat-crypto",
|
||||
"rand_core 0.10.1",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek-derive"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a69dedd701da44b0536442edf09c81a64b0ab97a7a4a5e3d1971f00027cbc63d"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive-where"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"const-oid",
|
||||
"crypto-common",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d65aa39b3a5c1c9c1b745c9a019234bb7a21b77abcb4f4d266d706e2d577d65"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"crypto-bigint",
|
||||
"crypto-common",
|
||||
"digest",
|
||||
"ff",
|
||||
"group",
|
||||
"hybrid-array",
|
||||
"rand_core 0.10.1",
|
||||
"sec1",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1f686ab92a9fb0eaf188f6c6c87b89490baa6fdb0db4544ba4dc47f7942489f"
|
||||
dependencies = [
|
||||
"rand_core 0.10.1",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fiat-crypto"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi 5.3.0",
|
||||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi 6.0.0",
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fd1a1c7a5206c5b7a3f5a0d7ccd3ff85d0c8f5133d62a02680255b0004af5f4"
|
||||
dependencies = [
|
||||
"ff",
|
||||
"rand_core 0.10.1",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash2curve"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1eaf40612d7d854743e7189228a6d528f0f6e8502cf6a0cb831d28a218b7f3f6"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"elliptic-curve",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hybrid-array"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"subtle",
|
||||
"typenum",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.186"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "p256"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2c9239b2dbc807adbbe147e8cf72ea7450c3a0aabe62cb8e75ff4ec22e1f72a"
|
||||
dependencies = [
|
||||
"elliptic-curve",
|
||||
"hash2curve",
|
||||
"primefield",
|
||||
"primeorder",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "p384"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d17b851e6b3e378ab4ecb07fa2ed23f4d15f075735f8fec9fa1e7bdce5f8301f"
|
||||
dependencies = [
|
||||
"elliptic-curve",
|
||||
"fiat-crypto",
|
||||
"hash2curve",
|
||||
"primefield",
|
||||
"primeorder",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "p521"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ad64cc32c2dc466317c12ee5853e61f159f9eab1fe7efade0395dc2e7b43449"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"elliptic-curve",
|
||||
"hash2curve",
|
||||
"primefield",
|
||||
"primeorder",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "primefield"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c555a6e4eb7d4e158fcb028c835c3b8642206ddc279b5c6b202ef9a8bdb592f4"
|
||||
dependencies = [
|
||||
"crypto-bigint",
|
||||
"crypto-common",
|
||||
"ff",
|
||||
"rand_core 0.10.1",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "primeorder"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c9f42978c78a00e3d68f69fc03e57a234debae69da4020a4fb588fcdcd07b06"
|
||||
dependencies = [
|
||||
"elliptic-curve",
|
||||
"primefield",
|
||||
"wnaf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proptest"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"bit-vec",
|
||||
"bitflags",
|
||||
"num-traits",
|
||||
"rand 0.9.4",
|
||||
"rand_chacha",
|
||||
"rand_xorshift",
|
||||
"regex-syntax",
|
||||
"rusty-fork",
|
||||
"tempfile",
|
||||
"unarray",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
|
||||
dependencies = [
|
||||
"chacha20",
|
||||
"getrandom 0.4.3",
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
|
||||
dependencies = [
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusty-fork"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"quick-error",
|
||||
"tempfile",
|
||||
"wait-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sec1"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4d"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"ctutils",
|
||||
"der",
|
||||
"hybrid-array",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.151"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "3.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.4.3",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
|
||||
|
||||
[[package]]
|
||||
name = "unarray"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "voprf-vx"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"curve25519-dalek",
|
||||
"derive-where",
|
||||
"digest",
|
||||
"displaydoc",
|
||||
"elliptic-curve",
|
||||
"hash2curve",
|
||||
"hex",
|
||||
"hybrid-array",
|
||||
"p256",
|
||||
"p384",
|
||||
"p521",
|
||||
"proptest",
|
||||
"rand 0.10.2",
|
||||
"rand_core 0.10.1",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wait-timeout"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.4+wasi-0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.57.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
|
||||
|
||||
[[package]]
|
||||
name = "wnaf"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab12e7090f27e2ffd9322651492942d50c2926094af30601e1964337db39daf1"
|
||||
dependencies = [
|
||||
"ff",
|
||||
"group",
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
+50
-38
@@ -1,55 +1,67 @@
|
||||
[package]
|
||||
name = "voprf"
|
||||
version = "0.3.0"
|
||||
description = "An implementation of a verifiable oblivious pseudorandom function (VOPRF)"
|
||||
authors = ["Kevin Lewi <[email protected]>"]
|
||||
repository = "https://github.com/novifinancial/voprf/"
|
||||
authors = ["VexaHub Developers", "Kevin Lewi <[email protected]>"]
|
||||
categories = ["no-std", "algorithms", "cryptography"]
|
||||
keywords = ["oprf"]
|
||||
license = "MIT"
|
||||
edition = "2018"
|
||||
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"
|
||||
readme = "README.md"
|
||||
resolver = "2"
|
||||
repository = "https://github.com/vexahub/voprf-vx/"
|
||||
rust-version = "1.87"
|
||||
version = "1.0.0"
|
||||
|
||||
[features]
|
||||
default = ["ristretto255_u64", "serde"]
|
||||
alloc = []
|
||||
danger = []
|
||||
ristretto255_u64 = ["curve25519-dalek/u64_backend"]
|
||||
ristretto255_u32 = ["curve25519-dalek/u32_backend"]
|
||||
ristretto255_fiat_u64 = ["curve25519-dalek/fiat_u64_backend"]
|
||||
ristretto255_fiat_u32 = ["curve25519-dalek/fiat_u32_backend"]
|
||||
ristretto255_simd = ["curve25519-dalek/simd_backend"]
|
||||
p256 = ["num-bigint", "num-integer", "num-traits", "once_cell", "p256_"]
|
||||
std = []
|
||||
serde = ["serde_", "base64"]
|
||||
doctest = ["dep:p256", "dep:sha2"]
|
||||
default = ["ristretto255-ciphersuite", "dep:serde"]
|
||||
ristretto255 = ["dep:curve25519-dalek"]
|
||||
ristretto255-ciphersuite = ["ristretto255", "dep:sha2"]
|
||||
serde = ["curve25519-dalek?/serde", "hybrid-array/serde", "dep:serde"]
|
||||
std = ["alloc"]
|
||||
|
||||
[dependencies]
|
||||
base64 = { version = "0.13", default-features = false, features = ["alloc"], optional = true }
|
||||
curve25519-dalek = { version = "3", default-features = false, optional = true }
|
||||
digest = "0.9"
|
||||
curve25519-dalek = { version = "5", default-features = false, features = ["rand_core", "zeroize"], optional = true }
|
||||
derive-where = { version = "1", features = ["zeroize-on-drop"] }
|
||||
digest = { version = "0.11", features = ["zeroize"] }
|
||||
displaydoc = { version = "0.2", default-features = false }
|
||||
generic-array = "0.14"
|
||||
num-bigint = { version = "0.4", default-features = false, optional = true }
|
||||
num-integer = { version = "0.1", default-features = false, optional = true }
|
||||
num-traits = { version = "0.2", default-features = false, optional = true }
|
||||
once_cell = { version = "1", default-features = false, optional = true }
|
||||
p256_ = { package = "p256", version = "0.9", default-features = false, features = ["arithmetic", "zeroize"], optional = true }
|
||||
rand_core = { version = "0.6", default-features = false }
|
||||
serde_ = { version = "1", package = "serde", default-features = false, optional = true }
|
||||
subtle = { version = "2.3", default-features = false }
|
||||
zeroize = { version = "1", 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 }
|
||||
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", 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 = "0.14", features = ["more_lengths"] }
|
||||
hex = "0.4"
|
||||
json = "0.12"
|
||||
p256 = { version = "0.14", default-features = false, features = [
|
||||
"hash2curve",
|
||||
"oprf",
|
||||
] }
|
||||
p384 = { version = "0.14", default-features = false, features = [
|
||||
"hash2curve",
|
||||
"oprf",
|
||||
] }
|
||||
p521 = { version = "0.14", default-features = false, features = [
|
||||
"hash2curve",
|
||||
"oprf",
|
||||
] }
|
||||
proptest = "1"
|
||||
rand = "0.8"
|
||||
rand = "0.10"
|
||||
regex = "1"
|
||||
sha2 = "0.9"
|
||||
voprf = { path = "", default-features = false, features = ["std", "danger"] }
|
||||
serde_json = "1"
|
||||
sha2 = "0.11"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["danger", "p256", "std"]
|
||||
targets = []
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
targets = []
|
||||
features = ["doctest"]
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall
|
||||
be dual licensed as above, without any additional terms or conditions.
|
||||
@@ -1,14 +1,14 @@
|
||||
# voprf 
|
||||
# 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.
|
||||
|
||||
This implementation is based on the [Internet Draft for VOPRF](https://github.com/cfrg/draft-irtf-cfrg-voprf).
|
||||
This implementation is based on [RFC 9497](https://www.rfc-editor.org/rfc/rfc9497).
|
||||
|
||||
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-vx/) along with an example for usage.
|
||||
|
||||
Installation
|
||||
------------
|
||||
@@ -16,20 +16,24 @@ Installation
|
||||
Add the following line to the dependencies of your `Cargo.toml`:
|
||||
|
||||
```
|
||||
voprf = "0.3"
|
||||
voprf-vx = "1.0.0"
|
||||
```
|
||||
|
||||
### Minimum Supported Rust Version
|
||||
|
||||
Rust **1.51** or higher.
|
||||
Rust **1.87** or higher.
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
The author of this code is Kevin Lewi ([@kevinlewi](https://github.com/kevinlewi)).
|
||||
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)).
|
||||
To learn more about contributing to this project, [see this document](./CONTRIBUTING.md).
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
This project is [licensed](./LICENSE) under either Apache 2.0 or MIT, at your option.
|
||||
This project is dual-licensed under either the [MIT license](./LICENSE-MIT)
|
||||
or the [Apache License, Version 2.0](./LICENSE-APACHE).
|
||||
You may select, at your option, one of the above-listed licenses.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
newline_style = "Unix"
|
||||
@@ -0,0 +1,52 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
// Copyright (c) VexaHub and contributors.
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
//! Defines the CipherSuite trait to specify the underlying primitives for VOPRF
|
||||
|
||||
use crate::Group;
|
||||
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 hybrid_array::typenum::{IsGreaterOrEqual, Prod, True, U2};
|
||||
|
||||
/// Configures the underlying primitives used in VOPRF
|
||||
pub trait CipherSuite
|
||||
where
|
||||
<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 [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`].
|
||||
type Group: Group;
|
||||
|
||||
/// 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>;
|
||||
}
|
||||
+550
@@ -0,0 +1,550 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
// Copyright (c) VexaHub and contributors.
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
//! Common functionality between multiple OPRF modes.
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use core::iter::Map;
|
||||
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::{Array, ArrayN, ArraySize};
|
||||
use rand_core::{TryCryptoRng, TryRng};
|
||||
use subtle::ConstantTimeEq;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::serialization::serde::{Element, Scalar};
|
||||
use crate::{CipherSuite, Error, Group, InternalError, Result};
|
||||
|
||||
///////////////
|
||||
// Constants //
|
||||
// ========= //
|
||||
///////////////
|
||||
|
||||
pub(crate) const STR_FINALIZE: [u8; 8] = *b"Finalize";
|
||||
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: 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`].
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Mode {
|
||||
/// Non-verifiable mode.
|
||||
Oprf,
|
||||
/// Verifiable mode.
|
||||
Voprf,
|
||||
/// Partially-oblivious mode.
|
||||
Poprf,
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
/// Mode as it is represented in a context string.
|
||||
pub fn to_u8(self) -> u8 {
|
||||
match self {
|
||||
Mode::Oprf => 0,
|
||||
Mode::Voprf => 1,
|
||||
Mode::Poprf => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
// High-level API Structs //
|
||||
// ====================== //
|
||||
////////////////////////////
|
||||
|
||||
/// The first client message sent from a client (either verifiable or not) to a
|
||||
/// server (either verifiable or not).
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Elem)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct BlindedElement<CS: CipherSuite>(
|
||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||
pub(crate) <CS::Group as Group>::Elem,
|
||||
);
|
||||
|
||||
/// The server's response to the [BlindedElement] message from a client (either
|
||||
/// verifiable or not) to a server (either verifiable or not).
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Elem)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct EvaluationElement<CS: CipherSuite>(
|
||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||
pub(crate) <CS::Group as Group>::Elem,
|
||||
);
|
||||
|
||||
/// Contains prepared [`EvaluationElement`]s by a server batch evaluate
|
||||
/// preparation.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Elem)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct PreparedEvaluationElement<CS: CipherSuite>(pub(crate) EvaluationElement<CS>);
|
||||
|
||||
/// A proof produced by a server that the OPRF output matches against a server
|
||||
/// public key.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct Proof<CS: CipherSuite> {
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) c_scalar: <CS::Group as Group>::Scalar,
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) s_scalar: <CS::Group as Group>::Scalar,
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// Proof Functions //
|
||||
// =============== //
|
||||
/////////////////////
|
||||
|
||||
/// Can only fail with [`Error::Batch`].
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
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,
|
||||
b: <CS::Group as Group>::Elem,
|
||||
cs: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
|
||||
ds: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
|
||||
mode: Mode,
|
||||
) -> Result<Proof<CS>> {
|
||||
// https://www.rfc-editor.org/rfc/rfc9497#section-2.2.1
|
||||
|
||||
let (m, z) = compute_composites::<CS, _, _>(Some(k), b, cs, ds, mode)?;
|
||||
|
||||
let r = CS::Group::random_scalar(rng)?;
|
||||
let t2 = a * &r;
|
||||
let t3 = m * &r;
|
||||
|
||||
// Bm = GG.SerializeElement(B)
|
||||
let bm = CS::Group::serialize_elem(b);
|
||||
// a0 = GG.SerializeElement(M)
|
||||
let a0 = CS::Group::serialize_elem(m);
|
||||
// a1 = GG.SerializeElement(Z)
|
||||
let a1 = CS::Group::serialize_elem(z);
|
||||
// a2 = GG.SerializeElement(t2)
|
||||
let a2 = CS::Group::serialize_elem(t2);
|
||||
// a3 = GG.SerializeElement(t3)
|
||||
let a3 = CS::Group::serialize_elem(t3);
|
||||
|
||||
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
|
||||
|
||||
// h2Input = I2OSP(len(Bm), 2) || Bm ||
|
||||
// I2OSP(len(a0), 2) || a0 ||
|
||||
// I2OSP(len(a1), 2) || a1 ||
|
||||
// I2OSP(len(a2), 2) || a2 ||
|
||||
// I2OSP(len(a3), 2) || a3 ||
|
||||
// "Challenge"
|
||||
let h2_input = [
|
||||
&elem_len,
|
||||
bm.as_slice(),
|
||||
&elem_len,
|
||||
&a0,
|
||||
&elem_len,
|
||||
&a1,
|
||||
&elem_len,
|
||||
&a2,
|
||||
&elem_len,
|
||||
&a3,
|
||||
&STR_CHALLENGE,
|
||||
];
|
||||
|
||||
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);
|
||||
|
||||
Ok(Proof { c_scalar, s_scalar })
|
||||
}
|
||||
|
||||
/// Can only fail with [`Error::ProofVerification`] or [`Error::Batch`].
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
pub(crate) fn verify_proof<CS: CipherSuite>(
|
||||
a: <CS::Group as Group>::Elem,
|
||||
b: <CS::Group as Group>::Elem,
|
||||
cs: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
|
||||
ds: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
|
||||
proof: &Proof<CS>,
|
||||
mode: Mode,
|
||||
) -> Result<()> {
|
||||
// https://www.rfc-editor.org/rfc/rfc9497#section-2.2.2
|
||||
let (m, z) = compute_composites::<CS, _, _>(None, b, cs, ds, mode)?;
|
||||
let t2 = (a * &proof.s_scalar) + &(b * &proof.c_scalar);
|
||||
let t3 = (m * &proof.s_scalar) + &(z * &proof.c_scalar);
|
||||
|
||||
// Bm = GG.SerializeElement(B)
|
||||
let bm = CS::Group::serialize_elem(b);
|
||||
// a0 = GG.SerializeElement(M)
|
||||
let a0 = CS::Group::serialize_elem(m);
|
||||
// a1 = GG.SerializeElement(Z)
|
||||
let a1 = CS::Group::serialize_elem(z);
|
||||
// a2 = GG.SerializeElement(t2)
|
||||
let a2 = CS::Group::serialize_elem(t2);
|
||||
// a3 = GG.SerializeElement(t3)
|
||||
let a3 = CS::Group::serialize_elem(t3);
|
||||
|
||||
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
|
||||
|
||||
// h2Input = I2OSP(len(Bm), 2) || Bm ||
|
||||
// I2OSP(len(a0), 2) || a0 ||
|
||||
// I2OSP(len(a1), 2) || a1 ||
|
||||
// I2OSP(len(a2), 2) || a2 ||
|
||||
// I2OSP(len(a3), 2) || a3 ||
|
||||
// "Challenge"
|
||||
let h2_input = [
|
||||
&elem_len,
|
||||
bm.as_slice(),
|
||||
&elem_len,
|
||||
&a0,
|
||||
&elem_len,
|
||||
&a1,
|
||||
&elem_len,
|
||||
&a2,
|
||||
&elem_len,
|
||||
&a3,
|
||||
&STR_CHALLENGE,
|
||||
];
|
||||
|
||||
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();
|
||||
|
||||
match c.ct_eq(&proof.c_scalar).into() {
|
||||
true => Ok(()),
|
||||
false => Err(Error::ProofVerification),
|
||||
}
|
||||
}
|
||||
|
||||
type ComputeCompositesResult<CS> = (
|
||||
<<CS as CipherSuite>::Group as Group>::Elem,
|
||||
<<CS as CipherSuite>::Group as Group>::Elem,
|
||||
);
|
||||
|
||||
/// Can only fail with [`Error::Batch`].
|
||||
fn compute_composites<
|
||||
CS: CipherSuite,
|
||||
IC: Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
||||
ID: Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
||||
>(
|
||||
k_option: Option<<CS::Group as Group>::Scalar>,
|
||||
b: <CS::Group as Group>::Elem,
|
||||
c_slice: IC,
|
||||
d_slice: ID,
|
||||
mode: Mode,
|
||||
) -> Result<ComputeCompositesResult<CS>> {
|
||||
// https://www.rfc-editor.org/rfc/rfc9497#section-2.2.1
|
||||
|
||||
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
|
||||
|
||||
if c_slice.len() != d_slice.len() {
|
||||
return Err(Error::Batch);
|
||||
}
|
||||
|
||||
let len = u16::try_from(c_slice.len()).map_err(|_| Error::Batch)?;
|
||||
|
||||
// seedDST = "Seed-" || contextString
|
||||
let seed_dst = Dst::new::<CS, _>(STR_SEED, mode);
|
||||
|
||||
// h1Input = I2OSP(len(Bm), 2) || Bm ||
|
||||
// I2OSP(len(seedDST), 2) || seedDST
|
||||
// seed = Hash(h1Input)
|
||||
let seed = CS::Hash::new()
|
||||
.chain_update(elem_len)
|
||||
.chain_update(CS::Group::serialize_elem(b))
|
||||
.chain_update(seed_dst.i2osp_2())
|
||||
.chain_update_multi(&seed_dst.as_dst())
|
||||
.finalize();
|
||||
let seed_len = i2osp_2_array::<<CS::Hash as OutputSizeUser>::OutputSize>();
|
||||
|
||||
let mut m = CS::Group::identity_elem();
|
||||
let mut z = CS::Group::identity_elem();
|
||||
|
||||
for (i, (c, d)) in (0..len).zip(c_slice.zip(d_slice)) {
|
||||
// Ci = GG.SerializeElement(Cs[i])
|
||||
let ci = CS::Group::serialize_elem(c);
|
||||
// Di = GG.SerializeElement(Ds[i])
|
||||
let di = CS::Group::serialize_elem(d);
|
||||
// h2Input = I2OSP(len(seed), 2) || seed || I2OSP(i, 2) ||
|
||||
// I2OSP(len(Ci), 2) || Ci ||
|
||||
// I2OSP(len(Di), 2) || Di ||
|
||||
// "Composite"
|
||||
let h2_input = [
|
||||
seed_len.as_slice(),
|
||||
&seed,
|
||||
&i.to_be_bytes(),
|
||||
&elem_len,
|
||||
&ci,
|
||||
&elem_len,
|
||||
&di,
|
||||
&STR_COMPOSITE,
|
||||
];
|
||||
|
||||
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;
|
||||
z = match k_option {
|
||||
Some(_) => z,
|
||||
None => d * &di + &z,
|
||||
};
|
||||
}
|
||||
|
||||
z = match k_option {
|
||||
Some(k) => m * &k,
|
||||
None => z,
|
||||
};
|
||||
|
||||
Ok((m, z))
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// Inner Functions //
|
||||
// =============== //
|
||||
/////////////////////
|
||||
|
||||
/// Can only fail with [`Error::DeriveKeyPair`] and [`Error::Protocol`].
|
||||
pub(crate) fn derive_key_internal<CS: CipherSuite>(
|
||||
seed: &[u8],
|
||||
info: &[u8],
|
||||
mode: Mode,
|
||||
) -> Result<<CS::Group as Group>::Scalar, Error> {
|
||||
let dst = Dst::new::<CS, _>(STR_DERIVE_KEYPAIR, mode);
|
||||
|
||||
let info_len = i2osp_2(info.len()).map_err(|_| Error::DeriveKeyPair)?;
|
||||
|
||||
for counter in 0_u8..=u8::MAX {
|
||||
// deriveInput = seed || I2OSP(len(info), 2) || info
|
||||
// skS = G.HashToScalar(deriveInput || I2OSP(counter, 1), DST = "DeriveKeyPair"
|
||||
// || contextString)
|
||||
let sk_s = CS::Group::hash_to_scalar::<CS::Hash>(
|
||||
&[seed, &info_len, info, &counter.to_be_bytes()],
|
||||
&dst.as_dst(),
|
||||
)
|
||||
.map_err(|_| Error::DeriveKeyPair)?;
|
||||
|
||||
if !bool::from(CS::Group::is_zero_scalar(sk_s)) {
|
||||
return Ok(sk_s);
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::Protocol)
|
||||
}
|
||||
|
||||
/// Corresponds to DeriveKeyPair() function from the VOPRF specification.
|
||||
///
|
||||
/// # Errors
|
||||
/// - [`Error::DeriveKeyPair`] if the `input` and `seed` together are longer
|
||||
/// then `u16::MAX - 3`.
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
#[cfg(feature = "danger")]
|
||||
pub fn derive_key<CS: CipherSuite>(
|
||||
seed: &[u8],
|
||||
info: &[u8],
|
||||
mode: Mode,
|
||||
) -> Result<<CS::Group as Group>::Scalar, Error> {
|
||||
derive_key_internal::<CS>(seed, info, mode)
|
||||
}
|
||||
|
||||
type DeriveKeypairResult<CS> = (
|
||||
<<CS as CipherSuite>::Group as Group>::Scalar,
|
||||
<<CS as CipherSuite>::Group as Group>::Elem,
|
||||
);
|
||||
|
||||
/// Can only fail with [`Error::DeriveKeyPair`] and [`Error::Protocol`].
|
||||
pub(crate) fn derive_keypair<CS: CipherSuite>(
|
||||
seed: &[u8],
|
||||
info: &[u8],
|
||||
mode: Mode,
|
||||
) -> Result<DeriveKeypairResult<CS>, Error> {
|
||||
let sk_s = derive_key_internal::<CS>(seed, info, mode)?;
|
||||
let pk_s = CS::Group::base_elem() * &sk_s;
|
||||
|
||||
Ok((sk_s, pk_s))
|
||||
}
|
||||
|
||||
/// Inner function for blind that assumes that the blinding factor has already
|
||||
/// been chosen, and therefore takes it as input. Does not check if the blinding
|
||||
/// factor is non-zero.
|
||||
///
|
||||
/// Can only fail with [`Error::Input`].
|
||||
pub(crate) fn deterministic_blind_unchecked<CS: CipherSuite>(
|
||||
input: &[u8],
|
||||
blind: &<CS::Group as Group>::Scalar,
|
||||
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)
|
||||
}
|
||||
|
||||
/// Hashes `input` to a point on the curve
|
||||
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);
|
||||
CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).map_err(|_| Error::Input)
|
||||
}
|
||||
|
||||
/// Internal function that finalizes the hash input for OPRF, VOPRF & POPRF.
|
||||
/// Returned values can only fail with [`Error::Input`].
|
||||
pub(crate) fn server_evaluate_hash_input<CS: CipherSuite>(
|
||||
input: &[u8],
|
||||
info: Option<&[u8]>,
|
||||
issued_element: Array<u8, <<CS as CipherSuite>::Group as Group>::ElemLen>,
|
||||
) -> Result<Output<CS::Hash>> {
|
||||
// OPRF & VOPRF
|
||||
// hashInput = I2OSP(len(input), 2) || input ||
|
||||
// I2OSP(len(issuedElement), 2) || issuedElement ||
|
||||
// "Finalize"
|
||||
// return Hash(hashInput)
|
||||
//
|
||||
// POPRF
|
||||
// hashInput = I2OSP(len(input), 2) || input ||
|
||||
// I2OSP(len(info), 2) || info ||
|
||||
// I2OSP(len(issuedElement), 2) || issuedElement ||
|
||||
// "Finalize"
|
||||
|
||||
let mut hash = CS::Hash::new()
|
||||
.chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?)
|
||||
.chain_update(input.as_ref());
|
||||
if let Some(info) = info {
|
||||
hash = hash
|
||||
.chain_update(i2osp_2(info.as_ref().len()).map_err(|_| Error::Input)?)
|
||||
.chain_update(info.as_ref());
|
||||
}
|
||||
Ok(hash
|
||||
.chain_update(i2osp_2(issued_element.as_slice().len()).map_err(|_| Error::Input)?)
|
||||
.chain_update(issued_element)
|
||||
.chain_update(STR_FINALIZE)
|
||||
.finalize())
|
||||
}
|
||||
|
||||
pub(crate) type FinalizeAfterUnblindResult<'a, C, I, IE> = Map<
|
||||
IE,
|
||||
fn((I, <<C as CipherSuite>::Group as Group>::Elem)) -> Result<Output<<C as CipherSuite>::Hash>>,
|
||||
>;
|
||||
|
||||
/// Returned values can only fail with [`Error::Input`].
|
||||
pub(crate) fn finalize_after_unblind<
|
||||
'a,
|
||||
CS: CipherSuite,
|
||||
I: AsRef<[u8]>,
|
||||
IE: 'a + Iterator<Item = (I, <CS::Group as Group>::Elem)>,
|
||||
>(
|
||||
inputs_and_unblinded_elements: IE,
|
||||
) -> FinalizeAfterUnblindResult<'a, CS, I, IE> {
|
||||
inputs_and_unblinded_elements.map(|(input, unblinded_element)| {
|
||||
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
|
||||
|
||||
Ok(CS::Hash::new()
|
||||
.chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?)
|
||||
.chain_update(input.as_ref())
|
||||
.chain_update(elem_len)
|
||||
.chain_update(CS::Group::serialize_elem(unblinded_element))
|
||||
.chain_update(STR_FINALIZE)
|
||||
.finalize())
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) struct Dst<L: ArraySize> {
|
||||
dst_1: Array<u8, L>,
|
||||
dst_2: &'static [u8],
|
||||
}
|
||||
|
||||
impl<L: ArraySize> Dst<L> {
|
||||
pub(crate) fn new<CS, TL>(par_1: Array<u8, TL>, mode: Mode) -> Self
|
||||
where
|
||||
CS: CipherSuite,
|
||||
TL: ArraySize + Add<U9, Output = L>,
|
||||
{
|
||||
// Generates the contextString parameter as defined in
|
||||
// <https://www.rfc-editor.org/rfc/rfc9497#section-3.1>
|
||||
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;
|
||||
|
||||
assert!(
|
||||
L::USIZE + dst_2.len() <= u16::MAX.into(),
|
||||
"constructed DST longer then {}",
|
||||
u16::MAX
|
||||
);
|
||||
|
||||
Self { dst_1, dst_2 }
|
||||
}
|
||||
|
||||
pub(crate) fn as_dst(&self) -> [&[u8]; 2] {
|
||||
[&self.dst_1, self.dst_2]
|
||||
}
|
||||
|
||||
pub(crate) fn i2osp_2(&self) -> [u8; 2] {
|
||||
u16::try_from(L::USIZE + self.dst_2.len())
|
||||
.unwrap()
|
||||
.to_be_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
trait DigestExt {
|
||||
fn chain_update_multi(self, data: &[&[u8]]) -> Self;
|
||||
}
|
||||
|
||||
impl<T> DigestExt for T
|
||||
where
|
||||
T: Digest,
|
||||
{
|
||||
fn chain_update_multi(mut self, datas: &[&[u8]]) -> Self {
|
||||
for data in datas {
|
||||
self.update(data)
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// Utility Functions //
|
||||
// ================= //
|
||||
///////////////////////
|
||||
|
||||
pub(crate) fn i2osp_2(input: usize) -> Result<[u8; 2], InternalError> {
|
||||
u16::try_from(input)
|
||||
.map(|input| input.to_be_bytes())
|
||||
.map_err(|_| InternalError::I2osp)
|
||||
}
|
||||
|
||||
pub(crate) fn i2osp_2_array<L: ArraySize + IsLess<U256>>() -> Array<u8, U2> {
|
||||
L::U16.to_be_bytes().into()
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
// Copyright (c) VexaHub and contributors.
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
//! Errors which are produced during an execution of the protocol
|
||||
|
||||
/// [`Result`](core::result::Result) shorthand that uses [`Error`].
|
||||
pub type Result<T, E = Error> = core::result::Result<T, E>;
|
||||
|
||||
/// Represents an error in the manipulation of internal cryptographic data
|
||||
#[derive(Clone, Copy, Debug, displaydoc::Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub enum Error {
|
||||
/// Size of info is longer then [`u16::MAX`].
|
||||
Info,
|
||||
/// Size of input is empty or longer then [`u16::MAX`].
|
||||
Input,
|
||||
/// Size of info and seed together are longer then `u16::MAX - 3`.
|
||||
DeriveKeyPair,
|
||||
/// Failure to deserialize bytes
|
||||
Deserialization,
|
||||
/// Batched items are more than [`u16::MAX`] or length don't match.
|
||||
Batch,
|
||||
/// In verifiable mode, occurs when the proof failed to verify
|
||||
ProofVerification,
|
||||
/// The protocol has failed and can't be completed.
|
||||
Protocol,
|
||||
/// Random number generator failure.
|
||||
Rng,
|
||||
}
|
||||
|
||||
/// Only used to implement [`Group`](crate::Group).
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub enum InternalError {
|
||||
/// Size of input is empty or longer then [`u16::MAX`].
|
||||
Input,
|
||||
/// `input` is longer then [`u16::MAX`].
|
||||
I2osp,
|
||||
}
|
||||
|
||||
impl core::error::Error for Error {}
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
//! A list of error types which are produced during an execution of the protocol
|
||||
#[cfg(feature = "std")]
|
||||
use std::error::Error;
|
||||
|
||||
use displaydoc::Display;
|
||||
|
||||
/// Represents an error in the manipulation of internal cryptographic data
|
||||
#[derive(Clone, Debug, Display, Eq, Hash, PartialEq)]
|
||||
pub enum InternalError {
|
||||
/// Could not parse byte sequence for key
|
||||
InvalidByteSequence,
|
||||
/// Could not deserialize element, or deserialized to the identity element
|
||||
PointError,
|
||||
/// Computing the hash-to-curve function failed
|
||||
HashToCurveError,
|
||||
/// Failure to serialize or deserialize bytes
|
||||
SerializationError,
|
||||
/// Use of incompatible modes (base vs. verifiable)
|
||||
IncompatibleModeError,
|
||||
/**
|
||||
* Internal error thrown when different-lengthed slices are supplied
|
||||
* to the compute_composites() function.
|
||||
*/
|
||||
MismatchedLengthsForCompositeInputs,
|
||||
/// In verifiable mode, occurs when the proof failed to verify
|
||||
ProofVerificationError,
|
||||
/// Encountered insufficient bytes when attempting to deserialize
|
||||
SizeError,
|
||||
/// Encountered a zero scalar
|
||||
ZeroScalarError,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Error for InternalError {}
|
||||
@@ -0,0 +1,133 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
// Copyright (c) VexaHub and contributors.
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
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::sec1::{FromSec1Point, ModulusSize, ToSec1Point};
|
||||
use elliptic_curve::{
|
||||
AffinePoint, Field, FieldBytes, FieldBytesSize, Group as _, ProjectivePoint, PublicKey, Scalar,
|
||||
SecretKey,
|
||||
};
|
||||
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};
|
||||
|
||||
type ElemLen<C> = <ScalarLen<C> as ModulusSize>::CompressedPointSize;
|
||||
type ScalarLen<C> = FieldBytesSize<C>;
|
||||
|
||||
impl<C> Group for C
|
||||
where
|
||||
C: GroupDigest,
|
||||
C::SecurityLevel: Mul<U2>,
|
||||
C::SecurityLevel: ArraySize,
|
||||
<C::SecurityLevel as Mul<U2>>::Output: ArraySize,
|
||||
ProjectivePoint<Self>: CofactorGroup + ToSec1Point<Self>,
|
||||
ScalarLen<Self>: ModulusSize,
|
||||
ScalarLen<Self>: ArraySize,
|
||||
ScalarLen<Self>: hybrid_array::typenum::NonZero,
|
||||
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>>: ArraySize,
|
||||
// `ProofLen`
|
||||
ScalarLen<Self>: Add<ScalarLen<Self>>,
|
||||
Sum<ScalarLen<Self>, ScalarLen<Self>>: ArraySize,
|
||||
ElemLen<Self>: ArraySize,
|
||||
{
|
||||
type Elem = ProjectivePoint<Self>;
|
||||
|
||||
type ElemLen = ElemLen<Self>;
|
||||
|
||||
type Scalar = Scalar<Self>;
|
||||
|
||||
type ScalarLen = ScalarLen<Self>;
|
||||
|
||||
type SecurityLevel = C::SecurityLevel;
|
||||
|
||||
// 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> {
|
||||
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, Output = True>
|
||||
+ IsGreaterOrEqual<Prod<C::SecurityLevel, U2>, Output = True>,
|
||||
{
|
||||
hash_to_scalar::<C, ExpandMsgXmd<H>, <C as MapToCurve>::Length>(input, dst)
|
||||
.map_err(|_| InternalError::Input)
|
||||
}
|
||||
|
||||
fn base_elem() -> Self::Elem {
|
||||
ProjectivePoint::<Self>::generator()
|
||||
}
|
||||
|
||||
fn identity_elem() -> Self::Elem {
|
||||
ProjectivePoint::<Self>::identity()
|
||||
}
|
||||
|
||||
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 = Array::default();
|
||||
result[..bytes.len()].copy_from_slice(bytes);
|
||||
result
|
||||
}
|
||||
|
||||
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem> {
|
||||
PublicKey::<Self>::from_sec1_bytes(element_bits)
|
||||
.map(|public_key| public_key.to_projective())
|
||||
.map_err(|_| Error::Deserialization)
|
||||
}
|
||||
|
||||
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 {
|
||||
Option::from(scalar.invert()).unwrap()
|
||||
}
|
||||
|
||||
fn is_zero_scalar(scalar: Self::Scalar) -> subtle::Choice {
|
||||
scalar.is_zero()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn zero_scalar() -> Self::Scalar {
|
||||
Scalar::<Self>::ZERO
|
||||
}
|
||||
|
||||
fn serialize_scalar(scalar: Self::Scalar) -> Array<u8, Self::ScalarLen> {
|
||||
let bytes: FieldBytes<Self> = scalar.into();
|
||||
let mut result = Array::<u8, Self::ScalarLen>::default();
|
||||
result.as_mut_slice().copy_from_slice(bytes.as_ref());
|
||||
result
|
||||
}
|
||||
|
||||
fn deserialize_scalar(scalar_bits: &[u8]) -> Result<Self::Scalar> {
|
||||
SecretKey::<Self>::from_slice(scalar_bits)
|
||||
.map(|secret_key| *secret_key.to_nonzero_scalar())
|
||||
.map_err(|_| Error::Deserialization)
|
||||
}
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
use crate::errors::InternalError;
|
||||
use crate::util::i2osp;
|
||||
use core::ops::Add;
|
||||
use digest::{BlockInput, Digest};
|
||||
use generic_array::{
|
||||
sequence::Concat,
|
||||
typenum::{Unsigned, U1, U2},
|
||||
ArrayLength, GenericArray,
|
||||
};
|
||||
|
||||
// Computes ceil(x / y)
|
||||
fn div_ceil(x: usize, y: usize) -> usize {
|
||||
let additive = (x % y != 0) as usize;
|
||||
x / y + additive
|
||||
}
|
||||
|
||||
fn xor<L: ArrayLength<u8>>(x: GenericArray<u8, L>, y: GenericArray<u8, L>) -> GenericArray<u8, L> {
|
||||
x.into_iter().zip(y).map(|(x1, x2)| x1 ^ x2).collect()
|
||||
}
|
||||
|
||||
/// Corresponds to the expand_message_xmd() function defined in
|
||||
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt>
|
||||
pub fn expand_message_xmd<
|
||||
'a,
|
||||
H: BlockInput + Digest,
|
||||
L: ArrayLength<u8>,
|
||||
M: IntoIterator<Item = &'a [u8]>,
|
||||
D: ArrayLength<u8> + Add<U1>,
|
||||
>(
|
||||
msg: M,
|
||||
dst: GenericArray<u8, D>,
|
||||
) -> Result<GenericArray<u8, L>, InternalError>
|
||||
where
|
||||
<D as Add<U1>>::Output: ArrayLength<u8>,
|
||||
{
|
||||
let digest_len = <H as Digest>::OutputSize::USIZE;
|
||||
let ell = div_ceil(L::USIZE, digest_len);
|
||||
if ell > 255 {
|
||||
return Err(InternalError::HashToCurveError);
|
||||
}
|
||||
let dst_prime = dst.concat(i2osp::<U1>(D::USIZE)?);
|
||||
let z_pad = i2osp::<<H as BlockInput>::BlockSize>(0)?;
|
||||
let l_i_b_str = i2osp::<U2>(L::USIZE)?;
|
||||
|
||||
let mut h = H::new();
|
||||
|
||||
// msg_prime = Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime
|
||||
h.update(z_pad);
|
||||
for bytes in msg {
|
||||
h.update(bytes)
|
||||
}
|
||||
h.update(l_i_b_str);
|
||||
h.update(i2osp::<U1>(0)?);
|
||||
h.update(&dst_prime);
|
||||
|
||||
// b[0]
|
||||
let b_0 = h.finalize_reset();
|
||||
let mut b_i = GenericArray::default();
|
||||
|
||||
let mut uniform_bytes = GenericArray::default();
|
||||
|
||||
for (i, chunk) in (1..(ell + 1)).zip(uniform_bytes.chunks_mut(digest_len)) {
|
||||
h.update(xor(b_0.clone(), b_i.clone()));
|
||||
h.update(i2osp::<U1>(i)?);
|
||||
h.update(&dst_prime);
|
||||
b_i = h.finalize_reset();
|
||||
chunk.copy_from_slice(&b_i[..digest_len.min(chunk.len())]);
|
||||
}
|
||||
|
||||
Ok(uniform_bytes)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use generic_array::{
|
||||
typenum::{U128, U32},
|
||||
GenericArray,
|
||||
};
|
||||
|
||||
struct Params {
|
||||
msg: &'static str,
|
||||
len_in_bytes: usize,
|
||||
uniform_bytes: &'static str,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expand_message_xmd() {
|
||||
// Test vectors taken from Section K.1 of https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt
|
||||
let test_vectors: alloc::vec::Vec<Params> = alloc::vec![
|
||||
Params {
|
||||
msg: "",
|
||||
len_in_bytes: 0x20,
|
||||
uniform_bytes: "f659819a6473c1835b25ea59e3d38914c98b374f0970b7e4c\
|
||||
92181df928fca88",
|
||||
},
|
||||
Params {
|
||||
msg: "abc",
|
||||
len_in_bytes: 0x20,
|
||||
uniform_bytes: "1c38f7c211ef233367b2420d04798fa4698080a8901021a79\
|
||||
5a1151775fe4da7",
|
||||
},
|
||||
Params {
|
||||
msg: "abcdef0123456789",
|
||||
len_in_bytes: 0x20,
|
||||
uniform_bytes: "8f7e7b66791f0da0dbb5ec7c22ec637f79758c0a48170bfb7c4611bd304ece89",
|
||||
},
|
||||
Params {
|
||||
msg: "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
|
||||
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
|
||||
qqqqqqqqqqqqqqqqqqqqqqqqq",
|
||||
len_in_bytes: 0x20,
|
||||
uniform_bytes: "72d5aa5ec810370d1f0013c0df2f1d65699494ee2a39f72e\
|
||||
1716b1b964e1c642",
|
||||
},
|
||||
Params {
|
||||
msg: "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
len_in_bytes: 0x20,
|
||||
uniform_bytes: "3b8e704fc48336aca4c2a12195b720882f2162a4b7b13a9c\
|
||||
350db46f429b771b",
|
||||
},
|
||||
Params {
|
||||
msg: "",
|
||||
len_in_bytes: 0x80,
|
||||
uniform_bytes: "8bcffd1a3cae24cf9cd7ab85628fd111bb17e3739d3b53f8\
|
||||
9580d217aa79526f1708354a76a402d3569d6a9d19ef3de4d0b991\
|
||||
e4f54b9f20dcde9b95a66824cbdf6c1a963a1913d43fd7ac443a02\
|
||||
fc5d9d8d77e2071b86ab114a9f34150954a7531da568a1ea8c7608\
|
||||
61c0cde2005afc2c114042ee7b5848f5303f0611cf297f",
|
||||
},
|
||||
Params {
|
||||
msg: "abc",
|
||||
len_in_bytes: 0x80,
|
||||
uniform_bytes: "fe994ec51bdaa821598047b3121c149b364b178606d5e72b\
|
||||
fbb713933acc29c186f316baecf7ea22212f2496ef3f785a27e84a\
|
||||
40d8b299cec56032763eceeff4c61bd1fe65ed81decafff4a31d01\
|
||||
98619c0aa0c6c51fca15520789925e813dcfd318b542f879944127\
|
||||
1f4db9ee3b8092a7a2e8d5b75b73e28fb1ab6b4573c192",
|
||||
},
|
||||
Params {
|
||||
msg: "abcdef0123456789",
|
||||
len_in_bytes: 0x80,
|
||||
uniform_bytes: "c9ec7941811b1e19ce98e21db28d22259354d4d0643e3011\
|
||||
75e2f474e030d32694e9dd5520dde93f3600d8edad94e5c3649030\
|
||||
88a7228cc9eff685d7eaac50d5a5a8229d083b51de4ccc3733917f\
|
||||
4b9535a819b445814890b7029b5de805bf62b33a4dc7e24acdf2c9\
|
||||
24e9fe50d55a6b832c8c84c7f82474b34e48c6d43867be",
|
||||
},
|
||||
Params {
|
||||
msg: "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
|
||||
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
|
||||
qqqqqqqqqqqqqqqqqqqqqqqqq",
|
||||
len_in_bytes: 0x80,
|
||||
uniform_bytes: "48e256ddba722053ba462b2b93351fc966026e6d6db49318\
|
||||
9798181c5f3feea377b5a6f1d8368d7453faef715f9aecb078cd40\
|
||||
2cbd548c0e179c4ed1e4c7e5b048e0a39d31817b5b24f50db58bb3\
|
||||
720fe96ba53db947842120a068816ac05c159bb5266c63658b4f00\
|
||||
0cbf87b1209a225def8ef1dca917bcda79a1e42acd8069",
|
||||
},
|
||||
Params {
|
||||
msg: "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
len_in_bytes: 0x80,
|
||||
uniform_bytes: "396962db47f749ec3b5042ce2452b619607f27fd3939ece2\
|
||||
746a7614fb83a1d097f554df3927b084e55de92c7871430d6b95c2\
|
||||
a13896d8a33bc48587b1f66d21b128a1a8240d5b0c26dfe795a1a8\
|
||||
42a0807bb148b77c2ef82ed4b6c9f7fcb732e7f94466c8b51e52bf\
|
||||
378fba044a31f5cb44583a892f5969dcd73b3fa128816e",
|
||||
},
|
||||
];
|
||||
let dst = GenericArray::from(*b"QUUX-V01-CS02-with-expander");
|
||||
|
||||
for tv in test_vectors {
|
||||
let uniform_bytes = match tv.len_in_bytes {
|
||||
32 => super::expand_message_xmd::<sha2::Sha256, U32, _, _>(
|
||||
Some(tv.msg.as_bytes()),
|
||||
dst,
|
||||
)
|
||||
.map(|bytes| bytes.to_vec()),
|
||||
128 => super::expand_message_xmd::<sha2::Sha256, U128, _, _>(
|
||||
Some(tv.msg.as_bytes()),
|
||||
dst,
|
||||
)
|
||||
.map(|bytes| bytes.to_vec()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
.unwrap();
|
||||
assert_eq!(tv.uniform_bytes, hex::encode(uniform_bytes));
|
||||
}
|
||||
}
|
||||
}
|
||||
+110
-126
@@ -1,155 +1,139 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
// Copyright (c) VexaHub and contributors.
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
//! Defines the Group trait to specify the underlying prime order group
|
||||
|
||||
#[cfg(any(
|
||||
feature = "ristretto255_u64",
|
||||
feature = "ristretto255_u32",
|
||||
feature = "ristretto255_fiat_u64",
|
||||
feature = "ristretto255_fiat_u32",
|
||||
feature = "ristretto255_simd",
|
||||
feature = "p256",
|
||||
))]
|
||||
mod expand;
|
||||
#[cfg(feature = "p256")]
|
||||
mod p256;
|
||||
#[cfg(any(
|
||||
feature = "ristretto255_u64",
|
||||
feature = "ristretto255_u32",
|
||||
feature = "ristretto255_fiat_u64",
|
||||
feature = "ristretto255_fiat_u32",
|
||||
feature = "ristretto255_simd",
|
||||
))]
|
||||
mod elliptic_curve;
|
||||
#[cfg(feature = "ristretto255")]
|
||||
mod ristretto;
|
||||
|
||||
use crate::errors::InternalError;
|
||||
use core::ops::{Add, Mul, Sub};
|
||||
use digest::{BlockInput, Digest};
|
||||
use generic_array::{typenum::U1, ArrayLength, GenericArray};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use subtle::ConstantTimeEq;
|
||||
|
||||
use digest::block_api::BlockSizeUser;
|
||||
use digest::{FixedOutput, HashMarker};
|
||||
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};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::{InternalError, Result};
|
||||
|
||||
/// A prime-order subgroup of a base field (EC, prime-order field ...). This
|
||||
/// subgroup is noted additively — as in the draft RFC — in this trait.
|
||||
pub trait Group:
|
||||
Copy
|
||||
+ Sized
|
||||
+ ConstantTimeEq
|
||||
+ for<'a> Mul<&'a <Self as Group>::Scalar, Output = Self>
|
||||
+ for<'a> Add<&'a Self, Output = Self>
|
||||
/// subgroup is noted additively — as in the RFC — in this trait.
|
||||
pub trait Group
|
||||
where
|
||||
// `VoprfClientLen`, `PoprfClientLen`, `VoprfServerLen`, `PoprfServerLen`
|
||||
Self::ScalarLen: Add<Self::ElemLen>,
|
||||
Sum<Self::ScalarLen, Self::ElemLen>: ArraySize,
|
||||
// `ProofLen`
|
||||
Self::ScalarLen: Add<Self::ScalarLen>,
|
||||
Sum<Self::ScalarLen, Self::ScalarLen>: ArraySize,
|
||||
{
|
||||
/// The ciphersuite identifier as dictated by
|
||||
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-05.txt>
|
||||
const SUITE_ID: usize;
|
||||
|
||||
/// transforms a password and domain separation tag (DST) into a curve point
|
||||
fn hash_to_curve<H: BlockInput + Digest, D: ArrayLength<u8> + Add<U1>>(
|
||||
msg: &[u8],
|
||||
dst: GenericArray<u8, D>,
|
||||
) -> Result<Self, InternalError>
|
||||
where
|
||||
<D as Add<U1>>::Output: ArrayLength<u8>;
|
||||
|
||||
/// Hashes a slice of pseudo-random bytes to a scalar
|
||||
fn hash_to_scalar<
|
||||
'a,
|
||||
H: BlockInput + Digest,
|
||||
D: ArrayLength<u8> + Add<U1>,
|
||||
I: IntoIterator<Item = &'a [u8]>,
|
||||
>(
|
||||
input: I,
|
||||
dst: GenericArray<u8, D>,
|
||||
) -> Result<Self::Scalar, InternalError>
|
||||
where
|
||||
<D as Add<U1>>::Output: ArrayLength<u8>;
|
||||
|
||||
/// The type of base field scalars
|
||||
type Scalar: Zeroize
|
||||
/// The type of group elements
|
||||
type Elem: ConstantTimeEq
|
||||
+ Copy
|
||||
+ ConstantTimeEq
|
||||
+ for<'a> Add<&'a Self::Scalar, Output = Self::Scalar>
|
||||
+ for<'a> Sub<&'a Self::Scalar, Output = Self::Scalar>
|
||||
+ for<'a> Mul<&'a Self::Scalar, Output = Self::Scalar>;
|
||||
/// The byte length necessary to represent scalars
|
||||
type ScalarLen: ArrayLength<u8> + 'static;
|
||||
|
||||
/// Return a scalar from its fixed-length bytes representation, without
|
||||
/// checking if the scalar is zero.
|
||||
fn from_scalar_slice_unchecked(
|
||||
scalar_bits: &GenericArray<u8, Self::ScalarLen>,
|
||||
) -> Result<Self::Scalar, InternalError>;
|
||||
|
||||
/// Return a scalar from its fixed-length bytes representation. If the scalar
|
||||
/// is zero, then return an error.
|
||||
fn from_scalar_slice<'a>(
|
||||
scalar_bits: impl Into<&'a GenericArray<u8, Self::ScalarLen>>,
|
||||
) -> Result<Self::Scalar, InternalError> {
|
||||
let scalar = Self::from_scalar_slice_unchecked(scalar_bits.into())?;
|
||||
if scalar.ct_eq(&Self::scalar_zero()).into() {
|
||||
return Err(InternalError::ZeroScalarError);
|
||||
}
|
||||
Ok(scalar)
|
||||
}
|
||||
|
||||
/// picks a scalar at random
|
||||
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar;
|
||||
/// Serializes a scalar to bytes
|
||||
fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen>;
|
||||
/// The multiplicative inverse of this scalar
|
||||
fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar;
|
||||
+ Zeroize
|
||||
+ for<'a> Add<&'a Self::Elem, Output = Self::Elem>
|
||||
+ for<'a> Mul<&'a Self::Scalar, Output = Self::Elem>;
|
||||
|
||||
/// The byte length necessary to represent group elements
|
||||
type ElemLen: ArrayLength<u8> + 'static;
|
||||
type ElemLen: ArraySize + 'static;
|
||||
|
||||
/// Return an element from its fixed-length bytes representation. This is
|
||||
/// the unchecked version, which does not check for deserializing the identity
|
||||
/// element
|
||||
fn from_element_slice_unchecked(
|
||||
element_bits: &GenericArray<u8, Self::ElemLen>,
|
||||
) -> Result<Self, InternalError>;
|
||||
/// The type of base field scalars
|
||||
type Scalar: ConstantTimeEq
|
||||
+ Copy
|
||||
+ Zeroize
|
||||
+ for<'a> Add<&'a Self::Scalar, Output = Self::Scalar>
|
||||
+ for<'a> Mul<&'a Self::Scalar, Output = Self::Scalar>
|
||||
+ for<'a> Sub<&'a Self::Scalar, Output = Self::Scalar>;
|
||||
|
||||
/// Return an element from its fixed-length bytes representation. If the element
|
||||
/// is the identity element, return an error.
|
||||
fn from_element_slice<'a>(
|
||||
element_bits: impl Into<&'a GenericArray<u8, Self::ElemLen>>,
|
||||
) -> Result<Self, InternalError> {
|
||||
let elem = Self::from_element_slice_unchecked(element_bits.into())?;
|
||||
/// The byte length necessary to represent scalars
|
||||
type ScalarLen: ArraySize + 'static;
|
||||
|
||||
if Self::ct_eq(&elem, &<Self as Group>::identity()).into() {
|
||||
// found the identity element
|
||||
return Err(InternalError::PointError);
|
||||
}
|
||||
/// 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 + Mul<U2>;
|
||||
|
||||
Ok(elem)
|
||||
/// Transforms a password and domain separation tag (DST) into a curve point
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer
|
||||
/// then [`u16::MAX`].
|
||||
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>;
|
||||
|
||||
/// Hashes a slice of pseudo-random bytes to a scalar
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer
|
||||
/// then [`u16::MAX`].
|
||||
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>;
|
||||
|
||||
/// Get the base point for the group
|
||||
fn base_elem() -> Self::Elem;
|
||||
|
||||
/// Returns the identity group element
|
||||
fn identity_elem() -> Self::Elem;
|
||||
|
||||
/// Returns `true` if the element is equal to the identity element
|
||||
fn is_identity_elem(elem: Self::Elem) -> Choice {
|
||||
Self::identity_elem().ct_eq(&elem)
|
||||
}
|
||||
|
||||
/// Serializes the `self` group element
|
||||
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen>;
|
||||
fn serialize_elem(elem: Self::Elem) -> Array<u8, Self::ElemLen>;
|
||||
|
||||
/// Get the base point for the group
|
||||
fn base_point() -> Self;
|
||||
/// Return an element from its fixed-length bytes representation. If the
|
||||
/// element is the identity element, return an error.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Deserialization`](crate::Error::Deserialization) if the element
|
||||
/// is not a valid point on the group or the identity element.
|
||||
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem>;
|
||||
|
||||
/// Returns if the group element is equal to the identity (1)
|
||||
fn is_identity(&self) -> bool {
|
||||
self.ct_eq(&<Self as Group>::identity()).into()
|
||||
}
|
||||
/// Picks a scalar at random.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Rng`](crate::Error::Rng) if the random number generator fails.
|
||||
fn random_scalar<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self::Scalar>;
|
||||
|
||||
/// Returns the identity group element
|
||||
fn identity() -> Self;
|
||||
/// The multiplicative inverse of this scalar
|
||||
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar;
|
||||
|
||||
/// Returns `true` if the scalar is zero.
|
||||
fn is_zero_scalar(scalar: Self::Scalar) -> Choice;
|
||||
|
||||
/// Returns the scalar representing zero
|
||||
fn scalar_zero() -> Self::Scalar;
|
||||
#[cfg(test)]
|
||||
fn zero_scalar() -> Self::Scalar;
|
||||
|
||||
/// Set the contents of self to the identity value
|
||||
fn zeroize(&mut self) {
|
||||
*self = <Self as Group>::identity();
|
||||
}
|
||||
/// Serializes a scalar to bytes
|
||||
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.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Deserialization`](crate::Error::Deserialization) if the scalar
|
||||
/// is not a valid point on the group or zero.
|
||||
fn deserialize_scalar(scalar_bits: &[u8]) -> Result<Self::Scalar>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,580 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
// Note: This group implementation of p256 is experimental for now,
|
||||
// until hash-to-curve or crypto-bigint are fully supported.
|
||||
|
||||
#![allow(
|
||||
clippy::borrow_interior_mutable_const,
|
||||
clippy::declare_interior_mutable_const
|
||||
)]
|
||||
|
||||
use super::Group;
|
||||
use crate::errors::InternalError;
|
||||
use core::ops::{Add, Div, Mul, Neg};
|
||||
use core::str::FromStr;
|
||||
use digest::{BlockInput, Digest};
|
||||
use generic_array::typenum::{Unsigned, U1, U2, U32, U33, U48};
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use num_bigint::{BigInt, Sign};
|
||||
use num_integer::Integer;
|
||||
use num_traits::{One, ToPrimitive, Zero};
|
||||
use once_cell::unsync::Lazy;
|
||||
use p256_::elliptic_curve::group::prime::PrimeCurveAffine;
|
||||
use p256_::elliptic_curve::group::GroupEncoding;
|
||||
use p256_::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint};
|
||||
use p256_::elliptic_curve::Field;
|
||||
use p256_::{AffinePoint, EncodedPoint, ProjectivePoint};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use subtle::{Choice, ConditionallySelectable};
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.2
|
||||
// `L: 48`
|
||||
pub type L = U48;
|
||||
|
||||
#[cfg(feature = "p256")]
|
||||
impl Group for ProjectivePoint {
|
||||
const SUITE_ID: usize = 0x0003;
|
||||
|
||||
// Implements the `hash_to_curve()` function from
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
||||
fn hash_to_curve<H: BlockInput + Digest, D: ArrayLength<u8> + Add<U1>>(
|
||||
msg: &[u8],
|
||||
dst: GenericArray<u8, D>,
|
||||
) -> Result<Self, InternalError>
|
||||
where
|
||||
<D as Add<U1>>::Output: ArrayLength<u8>,
|
||||
{
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.2
|
||||
// `p: 2^256 - 2^224 + 2^192 + 2^96 - 1`
|
||||
const P: Lazy<BigInt> = Lazy::new(|| {
|
||||
BigInt::from_str(
|
||||
"115792089210356248762697446949407573530086143415290314195533631308867097853951",
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
// `A: -3`
|
||||
const A: Lazy<BigInt> = Lazy::new(|| BigInt::from(-3));
|
||||
// `B: 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b`
|
||||
const B: Lazy<BigInt> = Lazy::new(|| {
|
||||
BigInt::parse_bytes(
|
||||
b"5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
|
||||
16,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
// `Z: -10`
|
||||
const Z: Lazy<BigInt> = Lazy::new(|| BigInt::from(-10));
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
||||
// `hash_to_curve` calls `hash_to_field` with a `count` of `2`
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
|
||||
// `hash_to_field` calls `expand_message` with a `len_in_bytes` of `count * L`
|
||||
let uniform_bytes =
|
||||
super::expand::expand_message_xmd::<H, <L as Mul<U2>>::Output, _, _>(Some(msg), dst)?;
|
||||
|
||||
// hash to curve
|
||||
let (q0x, q0y) = hash_to_curve_simple_swu(&uniform_bytes[..L::USIZE], &A, &B, &P, &Z);
|
||||
let (q1x, q1y) = hash_to_curve_simple_swu(&uniform_bytes[L::USIZE..], &A, &B, &P, &Z);
|
||||
|
||||
// convert to `p256` types
|
||||
let p0 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates(
|
||||
&q0x, &q0y, false,
|
||||
))
|
||||
.ok_or(InternalError::PointError)?
|
||||
.to_curve();
|
||||
let p1 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates(
|
||||
&q1x, &q1y, false,
|
||||
))
|
||||
.ok_or(InternalError::PointError)?;
|
||||
|
||||
Ok(p0 + p1)
|
||||
}
|
||||
|
||||
// Implements the `HashToScalar()` function
|
||||
fn hash_to_scalar<
|
||||
'a,
|
||||
H: BlockInput + Digest,
|
||||
D: ArrayLength<u8> + Add<U1>,
|
||||
I: IntoIterator<Item = &'a [u8]>,
|
||||
>(
|
||||
input: I,
|
||||
dst: GenericArray<u8, D>,
|
||||
) -> Result<Self::Scalar, InternalError>
|
||||
where
|
||||
<D as Add<U1>>::Output: ArrayLength<u8>,
|
||||
{
|
||||
// https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf#[{%22num%22:211,%22gen%22:0},{%22name%22:%22XYZ%22},70,700,0]
|
||||
// P-256 `n` is defined as `115792089210356248762697446949407573529996955224135760342 422259061068512044369`
|
||||
const N: Lazy<BigInt> = Lazy::new(|| {
|
||||
BigInt::from_str(
|
||||
"115792089210356248762697446949407573529996955224135760342422259061068512044369",
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
|
||||
// `HashToScalar` is `hash_to_field`
|
||||
let uniform_bytes = super::expand::expand_message_xmd::<H, L, _, _>(input, dst)?;
|
||||
let bytes = BigInt::from_bytes_be(Sign::Plus, &uniform_bytes)
|
||||
.mod_floor(&N)
|
||||
.to_bytes_be()
|
||||
.1;
|
||||
let mut result = GenericArray::default();
|
||||
result[..bytes.len()].copy_from_slice(&bytes);
|
||||
|
||||
Ok(p256_::Scalar::from_bytes_reduced(&result))
|
||||
}
|
||||
|
||||
type ElemLen = U33;
|
||||
type Scalar = p256_::Scalar;
|
||||
type ScalarLen = U32;
|
||||
|
||||
fn from_scalar_slice_unchecked(
|
||||
scalar_bits: &GenericArray<u8, Self::ScalarLen>,
|
||||
) -> Result<Self::Scalar, InternalError> {
|
||||
Ok(Self::Scalar::from_bytes_reduced(scalar_bits))
|
||||
}
|
||||
|
||||
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
|
||||
Self::Scalar::random(rng)
|
||||
}
|
||||
|
||||
fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
||||
scalar.into()
|
||||
}
|
||||
|
||||
fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar {
|
||||
scalar.invert().unwrap_or(Self::Scalar::zero())
|
||||
}
|
||||
|
||||
fn from_element_slice_unchecked(
|
||||
element_bits: &GenericArray<u8, Self::ElemLen>,
|
||||
) -> Result<Self, InternalError> {
|
||||
Option::from(Self::from_bytes(element_bits)).ok_or(InternalError::PointError)
|
||||
}
|
||||
|
||||
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
|
||||
let bytes = self.to_affine().to_encoded_point(true);
|
||||
let bytes = bytes.as_bytes();
|
||||
let mut result = GenericArray::default();
|
||||
result[..bytes.len()].copy_from_slice(bytes);
|
||||
result
|
||||
}
|
||||
|
||||
fn base_point() -> Self {
|
||||
Self::generator()
|
||||
}
|
||||
|
||||
fn identity() -> Self {
|
||||
Self::identity()
|
||||
}
|
||||
|
||||
fn scalar_zero() -> Self::Scalar {
|
||||
Self::Scalar::zero()
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the hash_to_curve_simple_swu() function defined in
|
||||
/// <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-F.2>
|
||||
///
|
||||
/// `cmov`, `mod_floor` and `modpow` needs to be made constant-time, which
|
||||
/// will be supported after crypto-bigint is no longer experimental. See
|
||||
/// <https://github.com/novifinancial/voprf/issues/13> for more context.
|
||||
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
fn hash_to_curve_simple_swu<N: ArrayLength<u8>>(
|
||||
u: &[u8],
|
||||
a: &BigInt,
|
||||
b: &BigInt,
|
||||
p: &BigInt,
|
||||
z: &BigInt,
|
||||
) -> (GenericArray<u8, N>, GenericArray<u8, N>) {
|
||||
#[derive(Clone)]
|
||||
struct Field<'a>(&'a BigInt);
|
||||
|
||||
impl<'a> Field<'a> {
|
||||
fn new(p: &'a BigInt) -> Self {
|
||||
Self(p)
|
||||
}
|
||||
|
||||
fn element(&'a self, number: &BigInt) -> FieldElement<'a> {
|
||||
FieldElement {
|
||||
number: number.mod_floor(self.0),
|
||||
f: self,
|
||||
}
|
||||
}
|
||||
|
||||
fn one(&'a self) -> FieldElement<'a> {
|
||||
self.element(&BigInt::one())
|
||||
}
|
||||
}
|
||||
|
||||
/// Finite field arithmetic
|
||||
#[derive(Clone)]
|
||||
struct FieldElement<'a> {
|
||||
number: BigInt,
|
||||
f: &'a Field<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Add for FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
&self + &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Add for &FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
self.f.element(&(&self.number + &rhs.number))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Neg for FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
fn neg(self) -> Self::Output {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Neg for &FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
fn neg(self) -> Self::Output {
|
||||
self.f.element(&-&self.number)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul for FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
&self * &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul<&Self> for FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
fn mul(self, rhs: &Self) -> Self::Output {
|
||||
&self * rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul<FieldElement<'a>> for &FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
fn mul(self, rhs: FieldElement<'a>) -> Self::Output {
|
||||
self * &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul for &FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
self.f.element(&(&self.number * &rhs.number))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Div<&Self> for FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||
fn div(self, rhs: &Self) -> Self::Output {
|
||||
self * rhs.inv0()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FieldElement<'a> {
|
||||
fn square(&self) -> Self {
|
||||
self * self
|
||||
}
|
||||
|
||||
fn pow_internal(&self, exponent: &BigInt) -> Self {
|
||||
let exponent = exponent.mod_floor(&(self.f.0 - 1));
|
||||
Self {
|
||||
number: self.number.modpow(&exponent, self.f.0),
|
||||
f: self.f,
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the sqrt_3mod4() function defined in
|
||||
/// <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-I.1>
|
||||
fn sqrt(&self) -> Self {
|
||||
// constant
|
||||
let c1 = (self.f.0 + 1) >> 2;
|
||||
|
||||
self.pow_internal(&c1)
|
||||
}
|
||||
|
||||
/// Corresponds to the sgn0_m_eq_1() function defined in
|
||||
/// <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-4.1>
|
||||
fn sgn0(&self) -> i32 {
|
||||
(&self.number % 2_usize).to_i32().unwrap()
|
||||
}
|
||||
|
||||
/// See <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-4>
|
||||
fn inv0(&self) -> Self {
|
||||
self.pow_internal(&(self.f.0 - 2))
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
self.number.is_zero()
|
||||
}
|
||||
|
||||
/// Corresponds to the is_square() function defined in
|
||||
/// <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-4>
|
||||
fn is_square(&self) -> bool {
|
||||
// constant
|
||||
let exponent = (self.f.0 - 1) >> 1;
|
||||
|
||||
let result = self.pow_internal(&exponent);
|
||||
result.is_zero() || result.number.is_one()
|
||||
}
|
||||
|
||||
fn to_bytes<N: ArrayLength<u8>>(&self) -> GenericArray<u8, N> {
|
||||
let bytes = self.number.to_bytes_be().1;
|
||||
let mut result = GenericArray::default();
|
||||
result[N::USIZE - bytes.len()..].copy_from_slice(&bytes);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
fn cmov<'a>(x: &FieldElement<'a>, y: &FieldElement<'a>, b: bool) -> FieldElement<'a> {
|
||||
let f = x.f;
|
||||
|
||||
let x_bytes = x.number.to_bytes_le().1;
|
||||
let mut x = [0; 32];
|
||||
x[..x_bytes.len()].copy_from_slice(&x_bytes);
|
||||
|
||||
let y_bytes = y.number.to_bytes_le().1;
|
||||
let mut y = [0; 32];
|
||||
y[..y_bytes.len()].copy_from_slice(&y_bytes);
|
||||
|
||||
let mut bytes = [0; 32];
|
||||
|
||||
let choice = Choice::from(u8::from(b));
|
||||
|
||||
for ((byte, x), y) in bytes.iter_mut().zip(&x).zip(&y) {
|
||||
*byte = u8::conditional_select(x, y, choice);
|
||||
}
|
||||
|
||||
FieldElement {
|
||||
f,
|
||||
number: BigInt::from_bytes_le(Sign::Plus, &bytes),
|
||||
}
|
||||
}
|
||||
|
||||
let f = Field::new(p);
|
||||
let a = f.element(a);
|
||||
let b = f.element(b);
|
||||
let z = f.element(z);
|
||||
let u = f.element(&BigInt::from_bytes_be(Sign::Plus, u));
|
||||
|
||||
// Constants:
|
||||
// 1. c1 = -B / A
|
||||
let c1 = -&b / &a;
|
||||
// 2. c2 = -1 / Z
|
||||
let c2 = -f.one() / &z;
|
||||
|
||||
// Steps:
|
||||
// 1. tv1 = Z * u^2
|
||||
let tv1 = z * u.square();
|
||||
// 2. tv2 = tv1^2
|
||||
let mut tv2 = tv1.square();
|
||||
// 3. x1 = tv1 + tv2
|
||||
let mut x1 = &tv1 + &tv2;
|
||||
// 4. x1 = inv0(x1)
|
||||
x1 = x1.inv0();
|
||||
// 5. e1 = x1 == 0
|
||||
let e1 = x1.is_zero();
|
||||
// 6. x1 = x1 + 1
|
||||
x1 = x1 + f.one();
|
||||
// 7. x1 = CMOV(x1, c2, e1) # If (tv1 + tv2) == 0, set x1 = -1 / Z
|
||||
x1 = cmov(&x1, &c2, e1);
|
||||
// 8. x1 = x1 * c1 # x1 = (-B / A) * (1 + (1 / (Z^2 * u^4 + Z * u^2)))
|
||||
x1 = x1 * c1;
|
||||
// 9. gx1 = x1^2
|
||||
let mut gx1 = x1.square();
|
||||
// 10. gx1 = gx1 + A
|
||||
gx1 = gx1 + a;
|
||||
// 11. gx1 = gx1 * x1
|
||||
gx1 = gx1 * &x1;
|
||||
// 12. gx1 = gx1 + B # gx1 = g(x1) = x1^3 + A * x1 + B
|
||||
gx1 = gx1 + b;
|
||||
// 13. x2 = tv1 * x1 # x2 = Z * u^2 * x1
|
||||
let x2 = &tv1 * &x1;
|
||||
// 14. tv2 = tv1 * tv2
|
||||
tv2 = tv1 * tv2;
|
||||
// 15. gx2 = gx1 * tv2 # gx2 = (Z * u^2)^3 * gx1
|
||||
let gx2 = &gx1 * tv2;
|
||||
// 16. e2 = is_square(gx1)
|
||||
let e2 = gx1.is_square();
|
||||
// 17. x = CMOV(x2, x1, e2) # If is_square(gx1), x = x1, else x = x2
|
||||
let x = cmov(&x2, &x1, e2);
|
||||
// 18. y2 = CMOV(gx2, gx1, e2) # If is_square(gx1), y2 = gx1, else y2 = gx2
|
||||
let y2 = cmov(&gx2, &gx1, e2);
|
||||
// 19. y = sqrt(y2)
|
||||
let mut y = y2.sqrt();
|
||||
// 20. e3 = sgn0(u) == sgn0(y) # Fix sign of y
|
||||
let e3 = u.sgn0() == y.sgn0();
|
||||
// 21. y = CMOV(-y, y, e3)
|
||||
y = cmov(&-&y, &y, e3);
|
||||
// 22. return (x, y)
|
||||
(x.to_bytes(), y.to_bytes())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use generic_array::typenum::U96;
|
||||
|
||||
struct Params {
|
||||
msg: &'static str,
|
||||
px: &'static str,
|
||||
py: &'static str,
|
||||
u0: &'static str,
|
||||
u1: &'static str,
|
||||
q0x: &'static str,
|
||||
q0y: &'static str,
|
||||
q1x: &'static str,
|
||||
q1y: &'static str,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hash_to_curve_simple_swu() {
|
||||
const P: Lazy<BigInt> = Lazy::new(|| {
|
||||
BigInt::from_str(
|
||||
"115792089210356248762697446949407573530086143415290314195533631308867097853951",
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
const A: Lazy<BigInt> = Lazy::new(|| BigInt::from(-3));
|
||||
const B: Lazy<BigInt> = Lazy::new(|| {
|
||||
BigInt::parse_bytes(
|
||||
b"5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
|
||||
16,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
const Z: Lazy<BigInt> = Lazy::new(|| BigInt::from(-10));
|
||||
|
||||
// Test vectors taken from https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-J.1.1
|
||||
let test_vectors = alloc::vec![
|
||||
Params {
|
||||
msg: "",
|
||||
px: "2c15230b26dbc6fc9a37051158c95b79656e17a1a920b11394ca91c44247d3e4",
|
||||
py: "8a7a74985cc5c776cdfe4b1f19884970453912e9d31528c060be9ab5c43e8415",
|
||||
u0: "ad5342c66a6dd0ff080df1da0ea1c04b96e0330dd89406465eeba11582515009",
|
||||
u1: "8c0f1d43204bd6f6ea70ae8013070a1518b43873bcd850aafa0a9e220e2eea5a",
|
||||
q0x: "ab640a12220d3ff283510ff3f4b1953d09fad35795140b1c5d64f313967934d5",
|
||||
q0y: "dccb558863804a881d4fff3455716c836cef230e5209594ddd33d85c565b19b1",
|
||||
q1x: "51cce63c50d972a6e51c61334f0f4875c9ac1cd2d3238412f84e31da7d980ef5",
|
||||
q1y: "b45d1a36d00ad90e5ec7840a60a4de411917fbe7c82c3949a6e699e5a1b66aac",
|
||||
},
|
||||
Params {
|
||||
msg: "abc",
|
||||
px: "0bb8b87485551aa43ed54f009230450b492fead5f1cc91658775dac4a3388a0f",
|
||||
py: "5c41b3d0731a27a7b14bc0bf0ccded2d8751f83493404c84a88e71ffd424212e",
|
||||
u0: "afe47f2ea2b10465cc26ac403194dfb68b7f5ee865cda61e9f3e07a537220af1",
|
||||
u1: "379a27833b0bfe6f7bdca08e1e83c760bf9a338ab335542704edcd69ce9e46e0",
|
||||
q0x: "5219ad0ddef3cc49b714145e91b2f7de6ce0a7a7dc7406c7726c7e373c58cb48",
|
||||
q0y: "7950144e52d30acbec7b624c203b1996c99617d0b61c2442354301b191d93ecf",
|
||||
q1x: "019b7cb4efcfeaf39f738fe638e31d375ad6837f58a852d032ff60c69ee3875f",
|
||||
q1y: "589a62d2b22357fed5449bc38065b760095ebe6aeac84b01156ee4252715446e",
|
||||
},
|
||||
Params {
|
||||
msg: "abcdef0123456789",
|
||||
px: "65038ac8f2b1def042a5df0b33b1f4eca6bff7cb0f9c6c1526811864e544ed80",
|
||||
py: "cad44d40a656e7aff4002a8de287abc8ae0482b5ae825822bb870d6df9b56ca3",
|
||||
u0: "0fad9d125a9477d55cf9357105b0eb3a5c4259809bf87180aa01d651f53d312c",
|
||||
u1: "b68597377392cd3419d8fcc7d7660948c8403b19ea78bbca4b133c9d2196c0fb",
|
||||
q0x: "a17bdf2965eb88074bc01157e644ed409dac97cfcf0c61c998ed0fa45e79e4a2",
|
||||
q0y: "4f1bc80c70d411a3cc1d67aeae6e726f0f311639fee560c7f5a664554e3c9c2e",
|
||||
q1x: "7da48bb67225c1a17d452c983798113f47e438e4202219dd0715f8419b274d66",
|
||||
q1y: "b765696b2913e36db3016c47edb99e24b1da30e761a8a3215dc0ec4d8f96e6f9",
|
||||
},
|
||||
Params {
|
||||
msg: "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
|
||||
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
|
||||
qqqqqqqqqqqqqqqqqqqqqqqqq",
|
||||
px: "4be61ee205094282ba8a2042bcb48d88dfbb609301c49aa8b078533dc65a0b5d",
|
||||
py: "98f8df449a072c4721d241a3b1236d3caccba603f916ca680f4539d2bfb3c29e",
|
||||
u0: "3bbc30446f39a7befad080f4d5f32ed116b9534626993d2cc5033f6f8d805919",
|
||||
u1: "76bb02db019ca9d3c1e02f0c17f8baf617bbdae5c393a81d9ce11e3be1bf1d33",
|
||||
q0x: "c76aaa823aeadeb3f356909cb08f97eee46ecb157c1f56699b5efebddf0e6398",
|
||||
q0y: "776a6f45f528a0e8d289a4be12c4fab80762386ec644abf2bffb9b627e4352b1",
|
||||
q1x: "418ac3d85a5ccc4ea8dec14f750a3a9ec8b85176c95a7022f391826794eb5a75",
|
||||
q1y: "fd6604f69e9d9d2b74b072d14ea13050db72c932815523305cb9e807cc900aff",
|
||||
},
|
||||
Params {
|
||||
msg: "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
px: "457ae2981f70ca85d8e24c308b14db22f3e3862c5ea0f652ca38b5e49cd64bc5",
|
||||
py: "ecb9f0eadc9aeed232dabc53235368c1394c78de05dd96893eefa62b0f4757dc",
|
||||
u0: "4ebc95a6e839b1ae3c63b847798e85cb3c12d3817ec6ebc10af6ee51adb29fec",
|
||||
u1: "4e21af88e22ea80156aff790750121035b3eefaa96b425a8716e0d20b4e269ee",
|
||||
q0x: "d88b989ee9d1295df413d4456c5c850b8b2fb0f5402cc5c4c7e815412e926db8",
|
||||
q0y: "bb4a1edeff506cf16def96afff41b16fc74f6dbd55c2210e5b8f011ba32f4f40",
|
||||
q1x: "a281e34e628f3a4d2a53fa87ff973537d68ad4fbc28d3be5e8d9f6a2571c5a4b",
|
||||
q1y: "f6ed88a7aab56a488100e6f1174fa9810b47db13e86be999644922961206e184",
|
||||
},
|
||||
];
|
||||
let dst = GenericArray::from(*b"QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_");
|
||||
|
||||
for tv in test_vectors {
|
||||
let uniform_bytes =
|
||||
super::super::expand::expand_message_xmd::<sha2::Sha256, U96, _, _>(
|
||||
Some(tv.msg.as_bytes()),
|
||||
dst,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let u0 = BigInt::from_bytes_be(Sign::Plus, &uniform_bytes[..48]).mod_floor(&P);
|
||||
let u1 = BigInt::from_bytes_be(Sign::Plus, &uniform_bytes[48..]).mod_floor(&P);
|
||||
|
||||
assert_eq!(BigInt::parse_bytes(tv.u0.as_bytes(), 16).unwrap(), u0);
|
||||
assert_eq!(BigInt::parse_bytes(tv.u1.as_bytes(), 16).unwrap(), u1);
|
||||
|
||||
let (q0x, q0y) = super::hash_to_curve_simple_swu(&u0.to_bytes_be().1, &A, &B, &P, &Z);
|
||||
let (q1x, q1y) = super::hash_to_curve_simple_swu(&u1.to_bytes_be().1, &A, &B, &P, &Z);
|
||||
|
||||
assert_eq!(tv.q0x, hex::encode(q0x));
|
||||
assert_eq!(tv.q0y, hex::encode(q0y));
|
||||
assert_eq!(tv.q1x, hex::encode(q1x));
|
||||
assert_eq!(tv.q1y, hex::encode(q1y));
|
||||
|
||||
let p0 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates(
|
||||
&q0x, &q0y, false,
|
||||
))
|
||||
.unwrap()
|
||||
.to_curve();
|
||||
let p1 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates(
|
||||
&q1x, &q1y, false,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
let p = (p0 + p1).to_encoded_point(false);
|
||||
|
||||
assert_eq!(tv.px, hex::encode(p.x().unwrap()));
|
||||
assert_eq!(tv.py, hex::encode(p.y().unwrap()));
|
||||
}
|
||||
}
|
||||
}
|
||||
+130
-111
@@ -1,134 +1,153 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
// Copyright (c) VexaHub and contributors.
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
use core::num::NonZeroU16;
|
||||
|
||||
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::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,
|
||||
};
|
||||
use rand_core::{TryCryptoRng, TryRng};
|
||||
use subtle::ConstantTimeEq;
|
||||
|
||||
use super::Group;
|
||||
use crate::errors::InternalError;
|
||||
use core::convert::TryInto;
|
||||
use core::ops::Add;
|
||||
use curve25519_dalek::{
|
||||
constants::RISTRETTO_BASEPOINT_POINT,
|
||||
ristretto::{CompressedRistretto, RistrettoPoint},
|
||||
scalar::Scalar,
|
||||
traits::Identity,
|
||||
};
|
||||
use digest::{BlockInput, Digest};
|
||||
use generic_array::{
|
||||
typenum::{U1, U32, U64},
|
||||
ArrayLength, GenericArray,
|
||||
};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use crate::{Error, InternalError, Result};
|
||||
|
||||
/// The implementation of such a subgroup for Ristretto
|
||||
#[cfg(any(
|
||||
feature = "ristretto255_u64",
|
||||
feature = "ristretto255_u32",
|
||||
feature = "ristretto255_fiat_u64",
|
||||
feature = "ristretto255_fiat_u32",
|
||||
feature = "ristretto255_simd",
|
||||
))]
|
||||
impl Group for RistrettoPoint {
|
||||
const SUITE_ID: usize = 0x0001;
|
||||
/// [`Group`] implementation for Ristretto255.
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct Ristretto255;
|
||||
|
||||
#[cfg(feature = "ristretto255-ciphersuite")]
|
||||
impl crate::CipherSuite for Ristretto255 {
|
||||
const ID: &'static [u8] = b"ristretto255-SHA512";
|
||||
|
||||
type Group = Ristretto255;
|
||||
|
||||
type Hash = sha2::Sha512;
|
||||
}
|
||||
|
||||
impl Group for Ristretto255 {
|
||||
type Elem = RistrettoPoint;
|
||||
|
||||
type ElemLen = U32;
|
||||
|
||||
type Scalar = Scalar;
|
||||
|
||||
type ScalarLen = U32;
|
||||
|
||||
type SecurityLevel = U16;
|
||||
|
||||
// Implements the `hash_to_ristretto255()` function from
|
||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt
|
||||
fn hash_to_curve<H: BlockInput + Digest, D: ArrayLength<u8> + Add<U1>>(
|
||||
msg: &[u8],
|
||||
dst: GenericArray<u8, D>,
|
||||
) -> Result<Self, InternalError>
|
||||
// https://www.rfc-editor.org/rfc/rfc9380.html#appendix-B
|
||||
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError>
|
||||
where
|
||||
<D as Add<U1>>::Output: ArrayLength<u8>,
|
||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||
H::OutputSize: IsLess<U256>
|
||||
+ IsLessOrEqual<H::BlockSize, Output = True>
|
||||
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>,
|
||||
{
|
||||
let uniform_bytes = super::expand::expand_message_xmd::<H, U64, _, _>(Some(msg), dst)?;
|
||||
let uniform_bytes = expand_uniform_bytes::<H>(input, dst)?;
|
||||
|
||||
Ok(RistrettoPoint::from_uniform_bytes(
|
||||
uniform_bytes
|
||||
.as_slice()
|
||||
.try_into()
|
||||
.map_err(|_| InternalError::HashToCurveError)?,
|
||||
))
|
||||
Ok(RistrettoPoint::from_uniform_bytes(&uniform_bytes))
|
||||
}
|
||||
|
||||
// Implements the `HashToScalar()` function from
|
||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.1
|
||||
fn hash_to_scalar<
|
||||
'a,
|
||||
H: BlockInput + Digest,
|
||||
D: ArrayLength<u8> + Add<U1>,
|
||||
I: IntoIterator<Item = &'a [u8]>,
|
||||
>(
|
||||
input: I,
|
||||
dst: GenericArray<u8, D>,
|
||||
) -> Result<Self::Scalar, InternalError>
|
||||
// https://www.rfc-editor.org/rfc/rfc9497#section-4.1
|
||||
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
|
||||
where
|
||||
<D as Add<U1>>::Output: ArrayLength<u8>,
|
||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||
H::OutputSize: IsLess<U256>
|
||||
+ IsLessOrEqual<H::BlockSize, Output = True>
|
||||
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>,
|
||||
{
|
||||
let uniform_bytes = super::expand::expand_message_xmd::<H, U64, _, _>(input, dst)?;
|
||||
let uniform_bytes = expand_uniform_bytes::<H>(input, dst)?;
|
||||
|
||||
Ok(Scalar::from_bytes_mod_order_wide(
|
||||
uniform_bytes
|
||||
.as_slice()
|
||||
.try_into()
|
||||
.map_err(|_| InternalError::HashToCurveError)?,
|
||||
))
|
||||
Ok(Scalar::from_bytes_mod_order_wide(&uniform_bytes))
|
||||
}
|
||||
|
||||
type Scalar = Scalar;
|
||||
type ScalarLen = U32;
|
||||
fn from_scalar_slice_unchecked(
|
||||
scalar_bits: &GenericArray<u8, Self::ScalarLen>,
|
||||
) -> Result<Self::Scalar, InternalError> {
|
||||
Ok(Scalar::from_bytes_mod_order(*scalar_bits.as_ref()))
|
||||
}
|
||||
|
||||
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
|
||||
loop {
|
||||
let scalar = {
|
||||
let mut scalar_bytes = [0u8; 64];
|
||||
rng.fill_bytes(&mut scalar_bytes);
|
||||
Scalar::from_bytes_mod_order_wide(&scalar_bytes)
|
||||
};
|
||||
|
||||
if scalar != Scalar::zero() {
|
||||
break scalar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
||||
scalar.to_bytes().into()
|
||||
}
|
||||
|
||||
fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar {
|
||||
scalar.invert()
|
||||
}
|
||||
|
||||
// The byte length necessary to represent group elements
|
||||
type ElemLen = U32;
|
||||
fn from_element_slice_unchecked(
|
||||
element_bits: &GenericArray<u8, Self::ElemLen>,
|
||||
) -> Result<Self, InternalError> {
|
||||
CompressedRistretto::from_slice(element_bits)
|
||||
.decompress()
|
||||
.ok_or(InternalError::PointError)
|
||||
}
|
||||
// serialization of a group element
|
||||
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
|
||||
self.compress().to_bytes().into()
|
||||
}
|
||||
|
||||
fn base_point() -> Self {
|
||||
fn base_elem() -> Self::Elem {
|
||||
RISTRETTO_BASEPOINT_POINT
|
||||
}
|
||||
|
||||
fn identity() -> Self {
|
||||
<Self as Identity>::identity()
|
||||
fn identity_elem() -> Self::Elem {
|
||||
RistrettoPoint::identity()
|
||||
}
|
||||
|
||||
fn scalar_zero() -> Self::Scalar {
|
||||
Self::Scalar::zero()
|
||||
// serialization of a group element
|
||||
fn serialize_elem(elem: Self::Elem) -> Array<u8, Self::ElemLen> {
|
||||
elem.compress().to_bytes().into()
|
||||
}
|
||||
|
||||
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem> {
|
||||
CompressedRistretto::from_slice(element_bits)
|
||||
.map_err(|_| Error::Deserialization)?
|
||||
.decompress()
|
||||
.filter(|point| point != &RistrettoPoint::identity())
|
||||
.ok_or(Error::Deserialization)
|
||||
}
|
||||
|
||||
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)
|
||||
.map_err(|_| Error::Rng)?;
|
||||
|
||||
if let Ok(scalar) = Self::deserialize_scalar(&scalar_bytes) {
|
||||
break Ok(scalar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar {
|
||||
scalar.invert()
|
||||
}
|
||||
|
||||
fn is_zero_scalar(scalar: Self::Scalar) -> subtle::Choice {
|
||||
scalar.ct_eq(&Scalar::ZERO)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn zero_scalar() -> Self::Scalar {
|
||||
Scalar::ZERO
|
||||
}
|
||||
|
||||
fn serialize_scalar(scalar: Self::Scalar) -> Array<u8, Self::ScalarLen> {
|
||||
scalar.to_bytes().into()
|
||||
}
|
||||
|
||||
fn deserialize_scalar(scalar_bits: &[u8]) -> Result<Self::Scalar> {
|
||||
scalar_bits
|
||||
.try_into()
|
||||
.ok()
|
||||
.and_then(|bytes| Scalar::from_canonical_bytes(bytes).into())
|
||||
.filter(|scalar| scalar != &Scalar::ZERO)
|
||||
.ok_or(Error::Deserialization)
|
||||
}
|
||||
}
|
||||
|
||||
// HELPERS
|
||||
|
||||
fn expand_uniform_bytes<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<[u8; 64], InternalError>
|
||||
where
|
||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||
H::OutputSize: IsLess<U256>
|
||||
+ IsLessOrEqual<H::BlockSize, Output = True>
|
||||
+ IsGreaterOrEqual<Prod<U16, U2>, Output = True>,
|
||||
{
|
||||
let mut uniform_bytes = [0u8; 64];
|
||||
|
||||
<ExpandMsgXmd<H> as ExpandMsg<U16>>::expand_message(input, dst, NonZeroU16::new(64).unwrap())
|
||||
.map_err(|_| InternalError::Input)?
|
||||
.fill_bytes(&mut uniform_bytes)
|
||||
.map_err(|_| InternalError::Input)?;
|
||||
|
||||
Ok(uniform_bytes)
|
||||
}
|
||||
|
||||
+31
-27
@@ -1,50 +1,54 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
// Copyright (c) VexaHub and contributors.
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
//! Includes a series of tests for the group implementations
|
||||
|
||||
use crate::errors::InternalError;
|
||||
use crate::group::Group;
|
||||
use crate::{Error, Group, Result};
|
||||
|
||||
// Test that the deserialization of a group element should throw an error
|
||||
// if the identity element can be deserialized properly
|
||||
// Test that the deserialization of a group element should throw an error if the
|
||||
// identity element can be deserialized properly
|
||||
|
||||
#[test]
|
||||
fn test_group_properties() -> Result<(), InternalError> {
|
||||
use curve25519_dalek::ristretto::RistrettoPoint;
|
||||
fn test_group_properties() -> Result<()> {
|
||||
use p256::NistP256;
|
||||
use p384::NistP384;
|
||||
use p521::NistP521;
|
||||
|
||||
test_identity_element_error::<RistrettoPoint>()?;
|
||||
test_zero_scalar_error::<RistrettoPoint>()?;
|
||||
|
||||
#[cfg(feature = "p256")]
|
||||
#[cfg(feature = "ristretto255")]
|
||||
{
|
||||
use p256_::ProjectivePoint;
|
||||
use crate::Ristretto255;
|
||||
|
||||
test_identity_element_error::<ProjectivePoint>()?;
|
||||
test_zero_scalar_error::<ProjectivePoint>()?;
|
||||
test_identity_element_error::<Ristretto255>()?;
|
||||
test_zero_scalar_error::<Ristretto255>()?;
|
||||
}
|
||||
|
||||
test_identity_element_error::<NistP256>()?;
|
||||
test_zero_scalar_error::<NistP256>()?;
|
||||
|
||||
test_identity_element_error::<NistP384>()?;
|
||||
test_zero_scalar_error::<NistP384>()?;
|
||||
|
||||
test_identity_element_error::<NistP521>()?;
|
||||
test_zero_scalar_error::<NistP521>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Checks that the identity element cannot be deserialized
|
||||
fn test_identity_element_error<G: Group>() -> Result<(), InternalError> {
|
||||
let identity = G::identity();
|
||||
let result = G::from_element_slice(&identity.to_arr());
|
||||
assert!(matches!(result, Err(InternalError::PointError)));
|
||||
fn test_identity_element_error<G: Group>() -> Result<()> {
|
||||
let identity = G::identity_elem();
|
||||
let result = G::deserialize_elem(&G::serialize_elem(identity));
|
||||
assert!(matches!(result, Err(Error::Deserialization)));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Checks that the zero scalar cannot be deserialized
|
||||
fn test_zero_scalar_error<G: Group>() -> Result<(), InternalError> {
|
||||
let zero_scalar = G::scalar_zero();
|
||||
let result = G::from_scalar_slice(&G::scalar_as_bytes(zero_scalar));
|
||||
assert!(matches!(result, Err(InternalError::ZeroScalarError)));
|
||||
fn test_zero_scalar_error<G: Group>() -> Result<()> {
|
||||
let zero_scalar = G::zero_scalar();
|
||||
let result = G::deserialize_scalar(&G::serialize_scalar(zero_scalar));
|
||||
assert!(matches!(result, Err(Error::Deserialization)));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
-153
@@ -1,153 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
/// Implement multiple similar traits at the same time. Additionally used to
|
||||
/// find `#[bind]` markers to build `while` constraint.
|
||||
macro_rules! impl_with_bounds {
|
||||
(
|
||||
$name:ident$(<$($gen:ident$(: $bound1:tt $(+ $bound2:tt)*)?),+>)?
|
||||
// only collect types marked with `#bind`
|
||||
// `|` prevents error about a possibly empty token
|
||||
// `@` prevents ambiguity between `$_2` and `$trait1`
|
||||
// `#` prevents ambiguity between marker traits and `$_2`
|
||||
$(|$(@#bind: $type:ty|,)? $(@#pd: $_1:ty|,)? $(@$_2:ty|,)?)+
|
||||
$trait1:path => { $($fn1:item)? },
|
||||
$($trait2:path => { $($fn2:item)? },)*
|
||||
) => {
|
||||
impl$(<$($gen$(: $bound1 $(+ $bound2)*)?),+>)? $trait1 for $name$(<$($gen),+>)?
|
||||
where
|
||||
$($($type: $trait1,)?)+
|
||||
{
|
||||
$($fn1)?
|
||||
}
|
||||
|
||||
impl_with_bounds!(
|
||||
$name$(<$($gen$(: $bound1 $(+ $bound2)*)?),+>)?
|
||||
$(|$(@#bind: $type|,)? $(@#pd: $_1|,)? $(@$_2|,)?)+
|
||||
$($trait2 => { $($fn2)? },)*
|
||||
);
|
||||
};
|
||||
// signature triggered when all traits are exhausted
|
||||
(
|
||||
$name:ident$(<$($gen:ident$(: $bound1:tt$( + $bound2:tt)*)?),+>)?
|
||||
$(|$(@#bind: $type:ty|,)? $(@#pd: $_1:ty|,)? $(@$_2:ty|,)?)+
|
||||
) => { };
|
||||
}
|
||||
|
||||
/// Skips attempt to call [`zeroize()`](zeroize::Zeroize::zeroize) on
|
||||
/// [`PhantomData`](core::marker::PhantomData).
|
||||
macro_rules! impl_internal_zeroize {
|
||||
($self_:ident, #pd $field:ident) => {};
|
||||
($self_:ident, #bind $field:ident) => {
|
||||
$self_.$field.zeroize();
|
||||
};
|
||||
($self_:ident, $field:ident) => {
|
||||
$self_.$field.zeroize();
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_traits_for {
|
||||
(
|
||||
// include documentation, Rust can't connect documentation from outside
|
||||
// a macro to a `struct` generated by a macro
|
||||
$(#[doc = $doc:literal])*
|
||||
$vis:vis struct $name:ident$(<$($gen:ident$(: $bound1:tt $(+ $bound2:tt)*)?),+$(,)?>)? {
|
||||
$(#[$attr1:ident])? $vis1:vis $field1:ident: $type1:ty$(,
|
||||
$(#[$attr2:ident])? $vis2:vis $field2:ident: $type2:ty)*$(,)?
|
||||
}
|
||||
) => {
|
||||
// build `struct` itself
|
||||
$(#[doc = $doc])*
|
||||
$vis struct $name$(<$($gen$(: $bound1 $(+$bound2)*)?),+>)? {
|
||||
$vis1 $field1: $type1,
|
||||
$($vis2 $field2: $type2),*
|
||||
}
|
||||
|
||||
// implement traits that require specific `where` constraints with the
|
||||
// help of `#[bind]`
|
||||
impl_with_bounds!(
|
||||
$name$(<$($gen$(: $bound1 $(+ $bound2)*)?),+>)?
|
||||
|@$(#$attr1:)? $type1|, $(|@$(#$attr2:)? $type2|,)*
|
||||
core::fmt::Debug => {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("$name")
|
||||
.field("$field1", &self.$field1)
|
||||
$(.field("$field2", &self.$field2))*
|
||||
.finish()
|
||||
}
|
||||
},
|
||||
Eq => { },
|
||||
PartialEq => {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
PartialEq::eq(&self.$field1, &other.$field1)
|
||||
$(&& PartialEq::eq(&self.$field2, &other.$field2))*
|
||||
}
|
||||
},
|
||||
core::hash::Hash => {
|
||||
fn hash<_H: core::hash::Hasher>(&self, state: &mut _H) {
|
||||
core::hash::Hash::hash(&self.$field1, state);
|
||||
$(core::hash::Hash::hash(&self.$field2, state);)*
|
||||
}
|
||||
},
|
||||
Clone => {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
$field1: self.$field1.clone(),
|
||||
$($field2: self.$field2.clone(),)*
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
impl$(<$($gen$(: $bound1 $(+ $bound2)*)?),+>)? zeroize::Zeroize for $name$(<$($gen),+>)?
|
||||
{
|
||||
fn zeroize(&mut self) {
|
||||
impl_internal_zeroize!(self, $(#$attr1)? $field1);
|
||||
$(impl_internal_zeroize!(self, $(#$attr2)? $field2);)*
|
||||
}
|
||||
}
|
||||
|
||||
impl$(<$($gen$(: $bound1 $(+ $bound2)*)?),+>)? Drop for $name$(<$($gen),+>)?
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
zeroize::Zeroize::zeroize(self);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl$(<$($gen$(: $bound1 $(+ $bound2)*)?),+>)? serde_::Serialize for $name$(<$($gen),+>)? {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde_::Serializer,
|
||||
{
|
||||
if serializer.is_human_readable() {
|
||||
serializer.serialize_str(&base64::encode(&self.serialize()))
|
||||
} else {
|
||||
serializer.serialize_bytes(&self.serialize())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, $($($gen$(: $bound1 $(+ $bound2)*)?),+)?> serde_::Deserialize<'de> for $name$(<$($gen),+>)? {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde_::Deserializer<'de>,
|
||||
{
|
||||
use serde_::de::Error;
|
||||
|
||||
if deserializer.is_human_readable() {
|
||||
let s = <&str>::deserialize(deserializer)?;
|
||||
Self::deserialize(&base64::decode(s).map_err(Error::custom)?)
|
||||
} else {
|
||||
Self::deserialize(<&[u8]>::deserialize(deserializer)?)
|
||||
}
|
||||
.map_err(Error::custom)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+420
-269
@@ -1,275 +1,350 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
// Copyright (c) VexaHub and contributors.
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
//! An implementation of a verifiable oblivious pseudorandom function (VOPRF)
|
||||
//!
|
||||
//! Note: This implementation is in sync with
|
||||
//! [draft-irtf-cfrg-voprf-08](https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-08.html),
|
||||
//! but this specification is subject to change, until the final version
|
||||
//! published by the IETF.
|
||||
//! [RFC 9497](https://www.rfc-editor.org/rfc/rfc9497).
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! A verifiable oblivious pseudorandom function is a protocol that is
|
||||
//! evaluated between a client and a server. They must first agree on a
|
||||
//! collection of primitives to be kept consistent throughout protocol
|
||||
//! execution. These include:
|
||||
//! - a finite cyclic group along with a point representation, and
|
||||
//! - a hashing function.
|
||||
//! A verifiable oblivious pseudorandom function is a protocol that is evaluated
|
||||
//! between a client and a server. They must first agree on a finite cyclic
|
||||
//! group along with a point representation.
|
||||
//!
|
||||
//! We will use the following choices in this example:
|
||||
//! We will use the following choice in this example:
|
||||
//!
|
||||
//! ```
|
||||
//! type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! type Hash = sha2::Sha512;
|
||||
//! ```ignore
|
||||
//! type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! ```
|
||||
//!
|
||||
//! ## Modes of Operation
|
||||
//!
|
||||
//! VOPRF can be used in two modes:
|
||||
//! - [Base Mode](#base-mode), which corresponds to a normal OPRF evaluation with no
|
||||
//! support for the verification of the OPRF outputs
|
||||
//! - [Verifiable Mode](#verifiable-mode), which corresponds to an OPRF evaluation where
|
||||
//! the outputs can be verified against a server public key
|
||||
//! VOPRF can be used in three modes:
|
||||
//! - [Base Mode](#base-mode), which corresponds to a normal OPRF evaluation
|
||||
//! with no support for the verification of the OPRF outputs
|
||||
//! - [Verifiable Mode](#verifiable-mode), which corresponds to an OPRF
|
||||
//! evaluation where the outputs can be verified against a server public key
|
||||
//! (VOPRF)
|
||||
//! - [Partially Oblivious Verifiable Mode](#metadata), which corresponds to a
|
||||
//! VOPRF, where a public input can be supplied to the PRF computation
|
||||
//!
|
||||
//! In either mode, the protocol begins with a client blinding, followed by
|
||||
//! a server evaluation, and finishes with a client finalization.
|
||||
//! In all of these modes, the protocol begins with a client blinding, followed
|
||||
//! by a server evaluation, and finishes with a client finalization and server
|
||||
//! evaluation.
|
||||
//!
|
||||
//! ## Base Mode
|
||||
//!
|
||||
//! In base mode, a [NonVerifiableClient] interacts with a
|
||||
//! [NonVerifiableServer] to compute the output of the VOPRF.
|
||||
//! In base mode, an [OprfClient] interacts with an [OprfServer] to compute the
|
||||
//! output of the OPRF.
|
||||
//!
|
||||
//! ### Server Setup
|
||||
//!
|
||||
//! The protocol begins with a setup phase, in which the server must run
|
||||
//! [NonVerifiableServer::new()] to produce an instance of itself. This
|
||||
//! instance must be persisted on the server and used for online
|
||||
//! client evaluations.
|
||||
//! [OprfServer::new()] to produce an instance of itself. This instance must be
|
||||
//! persisted on the server and used for online client evaluations.
|
||||
//!
|
||||
//! ```
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! use voprf::NonVerifiableServer;
|
||||
//! use rand::{rngs::OsRng, RngCore};
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! use rand::rngs::SysRng;
|
||||
//! use rand::Rng;
|
||||
//! use voprf_vx::OprfServer;
|
||||
//!
|
||||
//! let mut server_rng = OsRng;
|
||||
//! let server = NonVerifiableServer::<Group, Hash>::new(&mut server_rng)
|
||||
//! .expect("Unable to construct server");
|
||||
//! let mut server_rng = SysRng;
|
||||
//! let server = OprfServer::<CipherSuite>::new(&mut server_rng);
|
||||
//! ```
|
||||
//!
|
||||
//! ### Client Blinding
|
||||
//!
|
||||
//! In the first step, the client chooses an input, and runs
|
||||
//! [NonVerifiableClient::blind] to produce a [NonVerifiableClientBlindResult],
|
||||
//! which consists of a [BlindedElement] to be sent to the server and a
|
||||
//! [NonVerifiableClient] which must be persisted on the client for the final
|
||||
//! step of the VOPRF protocol.
|
||||
//! In the first step, the client chooses an input, and runs [OprfClient::blind]
|
||||
//! to produce an [OprfClientBlindResult], which consists of a [BlindedElement]
|
||||
//! to be sent to the server and an [OprfClient] which must be persisted on the
|
||||
//! client for the final step of the VOPRF protocol.
|
||||
//!
|
||||
//! ```
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! use voprf::NonVerifiableClient;
|
||||
//! use rand::{rngs::OsRng, RngCore};
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! use rand::rngs::SysRng;
|
||||
//! use rand::Rng;
|
||||
//! use voprf_vx::OprfClient;
|
||||
//!
|
||||
//! let mut client_rng = OsRng;
|
||||
//! let client_blind_result = NonVerifiableClient::<Group, Hash>::blind(
|
||||
//! b"input".to_vec(),
|
||||
//! &mut client_rng,
|
||||
//! ).expect("Unable to construct client");
|
||||
//! let mut client_rng = SysRng;
|
||||
//! let client_blind_result = OprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
|
||||
//! .expect("Unable to construct client");
|
||||
//! ```
|
||||
//!
|
||||
//! ### Server Evaluation
|
||||
//! ### Server Blind Evaluation
|
||||
//!
|
||||
//! In the second step, the server takes as input the message from
|
||||
//! [NonVerifiableClient::blind] (a [BlindedElement]), and runs
|
||||
//! [NonVerifiableServer::evaluate] to produce a
|
||||
//! [NonVerifiableServerEvaluateResult], which consists of an
|
||||
//! [EvaluationElement] to be sent to the client.
|
||||
//! [OprfClient::blind] (a [BlindedElement]), and runs
|
||||
//! [OprfServer::blind_evaluate] to produce [EvaluationElement] to be sent to
|
||||
//! the client.
|
||||
//!
|
||||
//! ```
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # use voprf::NonVerifiableClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::OprfClient;
|
||||
//! # use rand::{rngs::SysRng, Rng};
|
||||
//! #
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_blind_result = NonVerifiableClient::<Group, Hash>::blind(
|
||||
//! # b"input".to_vec(),
|
||||
//! # let mut client_rng = SysRng;
|
||||
//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
|
||||
//! # b"input",
|
||||
//! # &mut client_rng,
|
||||
//! # ).expect("Unable to construct client");
|
||||
//! # use voprf::NonVerifiableServer;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! # let server = NonVerifiableServer::<Group, Hash>::new(&mut server_rng)
|
||||
//! # .expect("Unable to construct server");
|
||||
//! let server_evaluate_result = server.evaluate(
|
||||
//! client_blind_result.message,
|
||||
//! None,
|
||||
//! ).expect("Unable to perform server evaluate");
|
||||
//! # use voprf_vx::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);
|
||||
//! ```
|
||||
//!
|
||||
//! ### Client Finalization
|
||||
//!
|
||||
//! In the final step, the client takes as input the message from
|
||||
//! [NonVerifiableServer::evaluate] (an [EvaluationElement]), and runs
|
||||
//! [NonVerifiableClient::finalize] to produce an output for the protocol.
|
||||
//! In the final step on the client side, the client takes as input the message
|
||||
//! from [OprfServer::evaluate] (an [EvaluationElement]), and runs
|
||||
//! [OprfClient::finalize] to produce an output for the protocol.
|
||||
//!
|
||||
//! ```
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # use voprf::NonVerifiableClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::OprfClient;
|
||||
//! # use rand::{rngs::SysRng, Rng};
|
||||
//! #
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_blind_result = NonVerifiableClient::<Group, Hash>::blind(
|
||||
//! # b"input".to_vec(),
|
||||
//! # let mut client_rng = SysRng;
|
||||
//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
|
||||
//! # b"input",
|
||||
//! # &mut client_rng,
|
||||
//! # ).expect("Unable to construct client");
|
||||
//! # use voprf::NonVerifiableServer;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! # let server = NonVerifiableServer::<Group, Hash>::new(&mut server_rng)
|
||||
//! # .expect("Unable to construct server");
|
||||
//! # let server_evaluate_result = server.evaluate(
|
||||
//! # client_blind_result.message,
|
||||
//! # None,
|
||||
//! # ).expect("Unable to perform server evaluate");
|
||||
//! let client_finalize_result = client_blind_result.state.finalize(
|
||||
//! server_evaluate_result.message,
|
||||
//! None,
|
||||
//! ).expect("Unable to perform client finalization");
|
||||
//! # use voprf_vx::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
|
||||
//! .state
|
||||
//! .finalize(b"input", &message)
|
||||
//! .expect("Unable to perform client finalization");
|
||||
//!
|
||||
//! println!("VOPRF output: {:?}", client_finalize_result.to_vec());
|
||||
//! ```
|
||||
//!
|
||||
//! ### Server Evaluation
|
||||
//!
|
||||
//! Optionally, if the server has direct access to the PRF input, then it need
|
||||
//! not perform the oblivious computation and can simply run
|
||||
//! [OprfServer::evaluate] to generate an output which matches the output
|
||||
//! produced by an execution of the oblivious protocol on the same input and
|
||||
//! key.
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::OprfClient;
|
||||
//! # use rand::{rngs::SysRng, Rng};
|
||||
//! #
|
||||
//! # let mut client_rng = SysRng;
|
||||
//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
|
||||
//! # b"input",
|
||||
//! # &mut client_rng,
|
||||
//! # ).expect("Unable to construct client");
|
||||
//! # use voprf_vx::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
|
||||
//! .state
|
||||
//! .finalize(b"input", &message)
|
||||
//! .expect("Unable to perform client finalization");
|
||||
//!
|
||||
//! let server_evaluate_result = server
|
||||
//! .evaluate(b"input")
|
||||
//! .expect("Unable to perform the server evaluation");
|
||||
//!
|
||||
//! assert_eq!(client_finalize_result, server_evaluate_result);
|
||||
//! ```
|
||||
//!
|
||||
//! ## Verifiable Mode
|
||||
//!
|
||||
//! In verifiable mode, a [VerifiableClient] interacts with a
|
||||
//! [VerifiableServer] to compute the output of the VOPRF. In order to
|
||||
//! verify the server's computation, the client checks a server-generated
|
||||
//! proof against the server's public key. If the proof fails to verify,
|
||||
//! then the client does not receive an output.
|
||||
//! In verifiable mode, a [VoprfClient] interacts with a [VoprfServer] to
|
||||
//! compute the output of the VOPRF. In order to verify the server's
|
||||
//! computation, the client checks a server-generated proof against the server's
|
||||
//! public key. If the proof fails to verify, then the client does not receive
|
||||
//! an output.
|
||||
//!
|
||||
//! In batch mode, a single proof can be used for multiple VOPRF evaluations.
|
||||
//! See [the batching section](#batching)
|
||||
//! for more details on how to perform batch evaluations.
|
||||
//! See [the batching section](#batching) for more details on how to perform
|
||||
//! batch evaluations.
|
||||
//!
|
||||
//! ### Server Setup
|
||||
//!
|
||||
//! The protocol begins with a setup phase, in which the server must run
|
||||
//! [VerifiableServer::new()] to produce an instance of itself. This
|
||||
//! instance must be persisted on the server and used for online
|
||||
//! client evaluations.
|
||||
//! [VoprfServer::new()] to produce an instance of itself. This instance must be
|
||||
//! persisted on the server and used for online client evaluations.
|
||||
//!
|
||||
//! ```
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! use voprf::VerifiableServer;
|
||||
//! use rand::{rngs::OsRng, RngCore};
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! use rand::rngs::SysRng;
|
||||
//! use rand::Rng;
|
||||
//! use voprf_vx::VoprfServer;
|
||||
//!
|
||||
//! let mut server_rng = OsRng;
|
||||
//! let server = VerifiableServer::<Group, Hash>::new(&mut server_rng)
|
||||
//! .expect("Unable to construct server");
|
||||
//! let mut server_rng = SysRng;
|
||||
//! let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//!
|
||||
//! // To be sent to the client
|
||||
//! println!("Server public key: {:?}", server.get_public_key());
|
||||
//! ```
|
||||
//!
|
||||
//! The public key should be sent to the client, since the client will
|
||||
//! need it in the final step of the protocol in order to complete
|
||||
//! the evaluation of the VOPRF.
|
||||
//! The public key should be sent to the client, since the client will need it
|
||||
//! in the final step of the protocol in order to complete the evaluation of the
|
||||
//! VOPRF.
|
||||
//!
|
||||
//! ### Client Blinding
|
||||
//!
|
||||
//! In the first step, the client chooses an input, and runs
|
||||
//! [VerifiableClient::blind] to produce a [VerifiableClientBlindResult],
|
||||
//! which consists of a [BlindedElement] to be sent to the server and a
|
||||
//! [VerifiableClient] which must be persisted on the client for the final
|
||||
//! step of the VOPRF protocol.
|
||||
//! [VoprfClient::blind] to produce a [VoprfClientBlindResult], which consists
|
||||
//! of a [BlindedElement] to be sent to the server and a [VoprfClient] which
|
||||
//! must be persisted on the client for the final step of the VOPRF protocol.
|
||||
//!
|
||||
//! ```
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! use voprf::VerifiableClient;
|
||||
//! use rand::{rngs::OsRng, RngCore};
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! use rand::rngs::SysRng;
|
||||
//! use rand::Rng;
|
||||
//! use voprf_vx::VoprfClient;
|
||||
//!
|
||||
//! let mut client_rng = OsRng;
|
||||
//! let client_blind_result = VerifiableClient::<Group, Hash>::blind(
|
||||
//! b"input".to_vec(),
|
||||
//! &mut client_rng,
|
||||
//! ).expect("Unable to construct client");
|
||||
//! let mut client_rng = SysRng;
|
||||
//! let client_blind_result = VoprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
|
||||
//! .expect("Unable to construct client");
|
||||
//! ```
|
||||
//!
|
||||
//! ### Server Evaluation
|
||||
//! ### Server Blind Evaluation
|
||||
//!
|
||||
//! In the second step, the server takes as input the message from
|
||||
//! [VerifiableClient::blind] (a [BlindedElement]), and runs
|
||||
//! [VerifiableServer::evaluate] to produce a
|
||||
//! [VerifiableServerEvaluateResult], which consists of an
|
||||
//! [EvaluationElement] to be sent to the client along with a proof.
|
||||
//! [VoprfClient::blind] (a [BlindedElement]), and runs
|
||||
//! [VoprfServer::blind_evaluate] to produce a [VoprfServerEvaluateResult],
|
||||
//! which consists of an [EvaluationElement] to be sent to the client along with
|
||||
//! a proof.
|
||||
//!
|
||||
//! ```
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # use voprf::VerifiableClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::{VoprfServerEvaluateResult, VoprfClient};
|
||||
//! # use rand::{rngs::SysRng, Rng};
|
||||
//! #
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_blind_result = VerifiableClient::<Group, Hash>::blind(
|
||||
//! # b"input".to_vec(),
|
||||
//! # let mut client_rng = SysRng;
|
||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
|
||||
//! # b"input",
|
||||
//! # &mut client_rng,
|
||||
//! # ).expect("Unable to construct client");
|
||||
//! # use voprf::VerifiableServer;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! # let server = VerifiableServer::<Group, Hash>::new(&mut server_rng)
|
||||
//! # .expect("Unable to construct server");
|
||||
//! let server_evaluate_result = server.evaluate(
|
||||
//! &mut server_rng,
|
||||
//! client_blind_result.message,
|
||||
//! None,
|
||||
//! ).expect("Unable to perform server evaluate");
|
||||
//! # use voprf_vx::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);
|
||||
//! ```
|
||||
//!
|
||||
//! ### Client Finalization
|
||||
//!
|
||||
//! In the final step, the client takes as input the message from
|
||||
//! [VerifiableServer::evaluate] (an [EvaluationElement]),
|
||||
//! the proof, and the server's public key, and runs
|
||||
//! [VerifiableClient::finalize] to produce an output for the protocol.
|
||||
//! [VoprfServer::blind_evaluate] (an [EvaluationElement]), the proof, and the
|
||||
//! server's public key, and runs [VoprfClient::finalize] to produce an output
|
||||
//! for the protocol.
|
||||
//!
|
||||
//! ```
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # use voprf::VerifiableClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::VoprfClient;
|
||||
//! # use rand::{rngs::SysRng, Rng};
|
||||
//! #
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_blind_result = VerifiableClient::<Group, Hash>::blind(
|
||||
//! # b"input".to_vec(),
|
||||
//! # let mut client_rng = SysRng;
|
||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
|
||||
//! # b"input",
|
||||
//! # &mut client_rng,
|
||||
//! # ).expect("Unable to construct client");
|
||||
//! # use voprf::VerifiableServer;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! # let server = VerifiableServer::<Group, Hash>::new(&mut server_rng)
|
||||
//! # .expect("Unable to construct server");
|
||||
//! # let server_evaluate_result = server.evaluate(
|
||||
//! # use voprf_vx::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,
|
||||
//! # client_blind_result.message,
|
||||
//! # None,
|
||||
//! # ).expect("Unable to perform server evaluate");
|
||||
//! let client_finalize_result = client_blind_result.state.finalize(
|
||||
//! server_evaluate_result.message,
|
||||
//! server_evaluate_result.proof,
|
||||
//! # &client_blind_result.message,
|
||||
//! # );
|
||||
//! let client_finalize_result = client_blind_result
|
||||
//! .state
|
||||
//! .finalize(
|
||||
//! b"input",
|
||||
//! &server_evaluate_result.message,
|
||||
//! &server_evaluate_result.proof,
|
||||
//! server.get_public_key(),
|
||||
//! None,
|
||||
//! ).expect("Unable to perform client finalization");
|
||||
//! )
|
||||
//! .expect("Unable to perform client finalization");
|
||||
//!
|
||||
//! println!("VOPRF output: {:?}", client_finalize_result.to_vec());
|
||||
//! ```
|
||||
//!
|
||||
//! ### Server Evaluation
|
||||
//!
|
||||
//! Optionally, if the server has direct access to the PRF input, then it need
|
||||
//! not perform the oblivious computation and can simply run
|
||||
//! [VoprfServer::evaluate] to generate an output which matches the output
|
||||
//! produced by an execution of the oblivious protocol on the same input and
|
||||
//! key.
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::VoprfClient;
|
||||
//! # use rand::{rngs::SysRng, Rng};
|
||||
//! #
|
||||
//! # let mut client_rng = SysRng;
|
||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
|
||||
//! # b"input",
|
||||
//! # &mut client_rng,
|
||||
//! # ).expect("Unable to construct client");
|
||||
//! # use voprf_vx::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,
|
||||
//! # &client_blind_result.message,
|
||||
//! # );
|
||||
//! let client_finalize_result = client_blind_result
|
||||
//! .state
|
||||
//! .finalize(
|
||||
//! b"input",
|
||||
//! &server_evaluate_result.message,
|
||||
//! &server_evaluate_result.proof,
|
||||
//! server.get_public_key(),
|
||||
//! )
|
||||
//! .expect("Unable to perform client finalization");
|
||||
//!
|
||||
//! let server_evaluate_result = server
|
||||
//! .evaluate(b"input")
|
||||
//! .expect("Unable to perform the server evaluation");
|
||||
//!
|
||||
//! assert_eq!(client_finalize_result, server_evaluate_result);
|
||||
//! ```
|
||||
//!
|
||||
//! # Advanced Usage
|
||||
//!
|
||||
//! There are two additional (and optional) extensions to the core VOPRF
|
||||
@@ -278,157 +353,209 @@
|
||||
//!
|
||||
//! ## Batching
|
||||
//!
|
||||
//! It is sometimes desirable to generate only a single, constant-size
|
||||
//! proof for an unbounded number of VOPRF evaluations (on arbitrary inputs).
|
||||
//! [VerifiableClient] and [VerifiableServer] support a batch API for
|
||||
//! handling this case. In the following example, we show how to use
|
||||
//! the batch API to produce a single proof for 10 parallel
|
||||
//! VOPRF evaluations.
|
||||
//! It is sometimes desirable to generate only a single, constant-size proof for
|
||||
//! an unbounded number of VOPRF evaluations (on arbitrary inputs).
|
||||
//! [VoprfClient] and [VoprfServer] support a batch API for handling this case.
|
||||
//! In the following example, we show how to use the batch API to produce a
|
||||
//! single proof for 10 parallel VOPRF evaluations.
|
||||
//!
|
||||
//! First, the client produces 10 blindings, storing their resulting
|
||||
//! states and messages:
|
||||
//! First, the client produces 10 blindings, storing their resulting states and
|
||||
//! messages:
|
||||
//!
|
||||
//! ```
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # use voprf::VerifiableClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::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 {
|
||||
//! let client_blind_result = VerifiableClient::<Group, Hash>::blind(
|
||||
//! b"input".to_vec(),
|
||||
//! &mut client_rng,
|
||||
//! ).expect("Unable to construct client");
|
||||
//! let client_blind_result = VoprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
|
||||
//! .expect("Unable to construct client");
|
||||
//! client_states.push(client_blind_result.state);
|
||||
//! client_messages.push(client_blind_result.message);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Next, the server calls the [VerifiableServer::batch_evaluate]
|
||||
//! function on a set of client messages, to produce a corresponding
|
||||
//! set of messages to be returned to the client (returned in the same order),
|
||||
//! along with a single proof:
|
||||
//! Next, the server calls the [VoprfServer::batch_blind_evaluate_prepare] and
|
||||
//! [VoprfServer::batch_blind_evaluate_finish] function on a set of client
|
||||
//! messages, to produce a corresponding set of messages to be returned to the
|
||||
//! client (returned in the same order), along with a single proof:
|
||||
//!
|
||||
//! ```
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # use voprf::VerifiableClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::{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 {
|
||||
//! # let client_blind_result = VerifiableClient::<Group, Hash>::blind(
|
||||
//! # b"input".to_vec(),
|
||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
|
||||
//! # b"input",
|
||||
//! # &mut client_rng,
|
||||
//! # ).expect("Unable to construct client");
|
||||
//! # client_states.push(client_blind_result.state);
|
||||
//! # client_messages.push(client_blind_result.message);
|
||||
//! # }
|
||||
//! # use voprf::VerifiableServer;
|
||||
//! let mut server_rng = OsRng;
|
||||
//! # let server = VerifiableServer::<Group, Hash>::new(&mut server_rng)
|
||||
//! # .expect("Unable to construct server");
|
||||
//! let server_batch_evaluate_result = server.batch_evaluate(
|
||||
//! &mut server_rng,
|
||||
//! &client_messages,
|
||||
//! None,
|
||||
//! ).expect("Unable to perform server batch evaluate");
|
||||
//! # use voprf_vx::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();
|
||||
//! let VoprfServerBatchEvaluateFinishResult { messages, proof } = server
|
||||
//! .batch_blind_evaluate_finish(&mut server_rng, client_messages.iter(), &prepared_elements)
|
||||
//! .expect("Unable to perform server batch evaluate");
|
||||
//! let messages: Vec<_> = messages.collect();
|
||||
//! ```
|
||||
//!
|
||||
//! Then, the client calls [VerifiableClient::batch_finalize] on
|
||||
//! the client states saved from the first step, along with the messages
|
||||
//! returned by the server, along with the server's proof, in order to produce
|
||||
//! a vector of outputs if the proof verifies correctly.
|
||||
//! If `alloc` is available, `VoprfServer::batch_blind_evaluate` can be called
|
||||
//! to avoid having to collect output manually:
|
||||
//!
|
||||
//! ```
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # use voprf::VerifiableClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! # #[cfg(feature = "alloc")] {
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::{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 {
|
||||
//! # let client_blind_result = VerifiableClient::<Group, Hash>::blind(
|
||||
//! # b"input".to_vec(),
|
||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
|
||||
//! # b"input",
|
||||
//! # &mut client_rng,
|
||||
//! # ).expect("Unable to construct client");
|
||||
//! # client_states.push(client_blind_result.state);
|
||||
//! # client_messages.push(client_blind_result.message);
|
||||
//! # }
|
||||
//! # use voprf::VerifiableServer;
|
||||
//! let mut server_rng = OsRng;
|
||||
//! # let server = VerifiableServer::<Group, Hash>::new(&mut server_rng)
|
||||
//! # .expect("Unable to construct server");
|
||||
//! # let server_batch_evaluate_result = server.batch_evaluate(
|
||||
//! # &mut server_rng,
|
||||
//! # &client_messages,
|
||||
//! # None,
|
||||
//! # ).expect("Unable to perform server batch evaluate");
|
||||
//! let client_batch_finalize_result = VerifiableClient::batch_finalize(
|
||||
//! # use voprf_vx::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)
|
||||
//! .expect("Unable to perform server batch evaluate");
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! Then, the client calls [VoprfClient::batch_finalize] on the client states
|
||||
//! saved from the first step, along with the messages returned by the server,
|
||||
//! along with the server's proof, in order to produce a vector of outputs if
|
||||
//! the proof verifies correctly.
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "alloc")] {
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::{VoprfServerBatchEvaluateResult, VoprfClient};
|
||||
//! # use rand::{rngs::SysRng, Rng};
|
||||
//! #
|
||||
//! # let mut client_rng = SysRng;
|
||||
//! # let mut client_states = vec![];
|
||||
//! # let mut client_messages = vec![];
|
||||
//! # for _ in 0..10 {
|
||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
|
||||
//! # b"input",
|
||||
//! # &mut client_rng,
|
||||
//! # ).expect("Unable to construct client");
|
||||
//! # client_states.push(client_blind_result.state);
|
||||
//! # client_messages.push(client_blind_result.message);
|
||||
//! # }
|
||||
//! # use voprf_vx::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)
|
||||
//! # .expect("Unable to perform server batch evaluate");
|
||||
//! let client_batch_finalize_result = VoprfClient::batch_finalize(
|
||||
//! &[b"input"; 10],
|
||||
//! &client_states,
|
||||
//! &server_batch_evaluate_result.messages,
|
||||
//! server_batch_evaluate_result.proof,
|
||||
//! &messages,
|
||||
//! &proof,
|
||||
//! server.get_public_key(),
|
||||
//! None,
|
||||
//! ).expect("Unable to perform client batch finalization");
|
||||
//! )
|
||||
//! .expect("Unable to perform client batch finalization")
|
||||
//! .collect::<Vec<_>>();
|
||||
//!
|
||||
//! println!("VOPRF batch outputs: {:?}", client_batch_finalize_result);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Metadata
|
||||
//!
|
||||
//! The optional metadata parameter included in the protocol allows clients and
|
||||
//! servers (of either mode) to cryptographically bind additional data to the
|
||||
//! VOPRF output. This metadata is known to both parties at the start of the protocol,
|
||||
//! and is inserted under the server's evaluate step and the client's finalize step.
|
||||
//! This metadata can be constructed with some type of higher-level domain separation
|
||||
//! to avoid cross-protocol attacks or related issues.
|
||||
//! The optional metadata parameter included in the POPRF mode allows clients
|
||||
//! and servers to cryptographically bind additional data to the VOPRF output.
|
||||
//! This metadata is known to both parties at the start of the protocol, and is
|
||||
//! inserted under the server's blind evaluate step and the client's finalize
|
||||
//! step. This metadata can be constructed with some type of higher-level domain
|
||||
//! separation to avoid cross-protocol attacks or related issues.
|
||||
//!
|
||||
//! A custom metadata can be specified, for example, by: `Some(b"custom metadata")`.
|
||||
//! The API for POPRF mode is similar to VOPRF mode, except that a [PoprfServer]
|
||||
//! and [PoprfClient] are used, and that each of the functions accept an
|
||||
//! additional (and optional) info parameter which represents the public input.
|
||||
//! See
|
||||
//! <https://www.rfc-editor.org/rfc/rfc9497#name-poprf-public-input>
|
||||
//! for more detailed information on how this public input should be used.
|
||||
//!
|
||||
//! # Features
|
||||
//!
|
||||
//! - The `p256` feature enables using p256 as the underlying group for the [Group](group::Group) choice.
|
||||
//! Note that this is currently an experimental feature ⚠️, and is not yet ready for production use.
|
||||
//! - The `alloc` feature requires Rust's `alloc` crate and enables batching
|
||||
//! VOPRF evaluations.
|
||||
//!
|
||||
//! - The `serde` feature, enabled by default, provides convenience functions for serializing and deserializing with
|
||||
//! [serde](https://serde.rs/).
|
||||
//! - The `serde` feature, enabled by default, provides convenience functions
|
||||
//! for serializing and deserializing with [serde](https://serde.rs/).
|
||||
//!
|
||||
//! - The `danger` feature, disabled by default, exposes functions for setting and getting
|
||||
//! internal values not available in the default API. These functions are intended for use in
|
||||
//! by higher-level cryptographic protocols that need access to these raw values and are able to
|
||||
//! perform the necessary validations on them (such as being valid group elements).
|
||||
//! - The `danger` feature, disabled by default, exposes functions for setting
|
||||
//! and getting internal values not available in the default API. These
|
||||
//! functions are intended for use in by higher-level cryptographic protocols
|
||||
//! that need access to these raw values and are able to perform the necessary
|
||||
//! validations on them (such as being valid group elements).
|
||||
//!
|
||||
//! - The backend features are re-exported from
|
||||
//! [curve25519-dalek](https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) and allow for selecting
|
||||
//! the corresponding backend for the curve arithmetic used. The `ristretto255_u64` feature is included as the default.
|
||||
//! Other features are mapped as `ristretto255_u32`, `ristretto255_fiat_u64` and `ristretto255_fiat_u32`.
|
||||
//! - The `ristretto255-ciphersuite` features enables using [`Ristretto255`] as
|
||||
//! a [`CipherSuite`].
|
||||
//!
|
||||
//! - The `ristretto255_simd` feature is re-exported from
|
||||
//! [curve25519-dalek](https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) and enables parallel formulas,
|
||||
//! using either AVX2 or AVX512-IFMA. This will automatically enable the `ristretto255_u64` feature and requires Rust nightly.
|
||||
//! - The `ristretto255` feature enables using [`Ristretto255`] as the
|
||||
//! underlying group for the [Group] choice. To select a specific backend see
|
||||
//! the [curve25519-dalek] documentation.
|
||||
//!
|
||||
//! [curve25519-dalek]:
|
||||
//! (https://docs.rs/curve25519-dalek/4.0.0-pre.5/curve25519_dalek/index.html#backends)
|
||||
|
||||
#![deny(unsafe_code)]
|
||||
#![warn(clippy::cargo, missing_docs)]
|
||||
#![allow(clippy::multiple_crate_versions)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![no_std]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(not(test), deny(unsafe_code))]
|
||||
#![warn(
|
||||
clippy::cargo,
|
||||
clippy::missing_errors_doc,
|
||||
missing_debug_implementations,
|
||||
missing_docs
|
||||
)]
|
||||
#![allow(clippy::multiple_crate_versions)]
|
||||
|
||||
#[cfg(any(feature = "alloc", test))]
|
||||
extern crate alloc;
|
||||
|
||||
#[macro_use]
|
||||
mod impls;
|
||||
#[macro_use]
|
||||
mod util;
|
||||
pub mod errors;
|
||||
pub mod group;
|
||||
#[cfg(feature = "std")]
|
||||
extern crate std;
|
||||
|
||||
mod ciphersuite;
|
||||
mod common;
|
||||
mod error;
|
||||
mod group;
|
||||
mod oprf;
|
||||
mod poprf;
|
||||
mod serialization;
|
||||
mod voprf;
|
||||
|
||||
@@ -437,8 +564,32 @@ mod tests;
|
||||
|
||||
// Exports
|
||||
|
||||
pub use crate::voprf::{
|
||||
BlindedElement, EvaluationElement, NonVerifiableClient, NonVerifiableClientBlindResult,
|
||||
NonVerifiableServer, NonVerifiableServerEvaluateResult, VerifiableClient,
|
||||
VerifiableClientBlindResult, VerifiableServer, VerifiableServerEvaluateResult,
|
||||
pub use crate::ciphersuite::CipherSuite;
|
||||
#[cfg(feature = "danger")]
|
||||
pub use crate::common::derive_key;
|
||||
pub use crate::common::{
|
||||
BlindedElement, EvaluationElement, Mode, PreparedEvaluationElement, Proof,
|
||||
};
|
||||
pub use crate::error::{Error, InternalError, Result};
|
||||
pub use crate::group::Group;
|
||||
#[cfg(feature = "ristretto255")]
|
||||
pub use crate::group::Ristretto255;
|
||||
pub use crate::oprf::{OprfClient, OprfClientBlindResult, OprfServer};
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use crate::poprf::PoprfServerBatchEvaluateResult;
|
||||
pub use crate::poprf::{
|
||||
PoprfClient, PoprfClientBatchFinalizeResult, PoprfPreparedTweak, PoprfServer,
|
||||
PoprfServerBatchEvaluateFinishResult, PoprfServerBatchEvaluateFinishedMessages,
|
||||
PoprfServerBatchEvaluatePrepareResult, PoprfServerBatchEvaluatePreparedEvaluationElements,
|
||||
};
|
||||
pub use crate::serialization::{
|
||||
BlindedElementLen, EvaluationElementLen, OprfClientLen, OprfServerLen, PoprfClientLen,
|
||||
PoprfServerLen, ProofLen, VoprfClientLen, VoprfServerLen,
|
||||
};
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use crate::voprf::VoprfServerBatchEvaluateResult;
|
||||
pub use crate::voprf::{
|
||||
VoprfClient, VoprfClientBatchFinalizeResult, VoprfClientBlindResult, VoprfServer,
|
||||
VoprfServerBatchEvaluateFinishResult, VoprfServerBatchEvaluateFinishedMessages,
|
||||
VoprfServerBatchEvaluatePreparedEvaluationElements, VoprfServerEvaluateResult,
|
||||
};
|
||||
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
// Copyright (c) VexaHub and contributors.
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
//! Contains the main OPRF API
|
||||
|
||||
use core::iter::{self};
|
||||
|
||||
use derive_where::derive_where;
|
||||
use digest::Output;
|
||||
use hybrid_array::Array;
|
||||
use rand_core::{TryCryptoRng, TryRng};
|
||||
|
||||
use crate::common::{
|
||||
BlindedElement, EvaluationElement, Mode, derive_key_internal, deterministic_blind_unchecked,
|
||||
finalize_after_unblind, hash_to_group, server_evaluate_hash_input,
|
||||
};
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::serialization::serde::Scalar;
|
||||
use crate::{CipherSuite, Error, Group, Result};
|
||||
|
||||
///////////////
|
||||
// Constants //
|
||||
// ========= //
|
||||
///////////////
|
||||
|
||||
////////////////////////////
|
||||
// High-level API Structs //
|
||||
// ====================== //
|
||||
////////////////////////////
|
||||
|
||||
/// A client which engages with a [OprfServer] in base mode, meaning
|
||||
/// that the OPRF outputs are not verifiable.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct OprfClient<CS: CipherSuite> {
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) blind: <CS::Group as Group>::Scalar,
|
||||
}
|
||||
|
||||
/// A server which engages with a [OprfClient] in base mode, meaning
|
||||
/// that the OPRF outputs are not verifiable.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct OprfServer<CS: CipherSuite> {
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) sk: <CS::Group as Group>::Scalar,
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// API Implementations //
|
||||
// =================== //
|
||||
/////////////////////////
|
||||
|
||||
impl<CS: CipherSuite> OprfClient<CS> {
|
||||
/// Computes the first step for the multiplicative blinding version of
|
||||
/// DH-OPRF.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`].
|
||||
pub fn blind<R: TryRng + TryCryptoRng>(
|
||||
input: &[u8],
|
||||
blinding_factor_rng: &mut R,
|
||||
) -> Result<OprfClientBlindResult<CS>> {
|
||||
let blind = CS::Group::random_scalar(blinding_factor_rng)?;
|
||||
Self::deterministic_blind_unchecked_inner(input, blind)
|
||||
}
|
||||
|
||||
/// Computes the first step for the multiplicative blinding version of
|
||||
/// DH-OPRF, taking a blinding factor scalar as input instead of sampling
|
||||
/// from an RNG.
|
||||
///
|
||||
/// # Caution
|
||||
///
|
||||
/// This should be used with caution, since it does not perform any checks
|
||||
/// on the validity of the blinding factor!
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`].
|
||||
#[cfg(any(feature = "danger", test))]
|
||||
pub fn deterministic_blind_unchecked(
|
||||
input: &[u8],
|
||||
blind: <CS::Group as Group>::Scalar,
|
||||
) -> Result<OprfClientBlindResult<CS>> {
|
||||
Self::deterministic_blind_unchecked_inner(input, blind)
|
||||
}
|
||||
|
||||
/// Can only fail with [`Error::Input`].
|
||||
fn deterministic_blind_unchecked_inner(
|
||||
input: &[u8],
|
||||
blind: <CS::Group as Group>::Scalar,
|
||||
) -> Result<OprfClientBlindResult<CS>> {
|
||||
let blinded_element = deterministic_blind_unchecked::<CS>(input, &blind, Mode::Oprf)?;
|
||||
Ok(OprfClientBlindResult {
|
||||
state: Self { blind },
|
||||
message: BlindedElement(blinded_element),
|
||||
})
|
||||
}
|
||||
|
||||
/// Computes the third step for the multiplicative blinding version of
|
||||
/// DH-OPRF, in which the client unblinds the server's message.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`].
|
||||
pub fn finalize(
|
||||
&self,
|
||||
input: &[u8],
|
||||
evaluation_element: &EvaluationElement<CS>,
|
||||
) -> Result<Output<CS::Hash>> {
|
||||
let unblinded_element = evaluation_element.0 * &CS::Group::invert_scalar(self.blind);
|
||||
let mut outputs =
|
||||
finalize_after_unblind::<CS, _, _>(iter::once((input, unblinded_element)));
|
||||
outputs.next().unwrap()
|
||||
}
|
||||
|
||||
/// Only used for test functions
|
||||
#[cfg(test)]
|
||||
pub fn from_blind(blind: <CS::Group as Group>::Scalar) -> Self {
|
||||
Self { blind }
|
||||
}
|
||||
|
||||
/// Exposes the blind group element
|
||||
#[cfg(feature = "danger")]
|
||||
pub fn get_blind(&self) -> <CS::Group as Group>::Scalar {
|
||||
self.blind
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> OprfServer<CS> {
|
||||
/// Produces a new instance of a [OprfServer] using a supplied RNG
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
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, &[])
|
||||
}
|
||||
|
||||
/// Produces a new instance of a [OprfServer] using a supplied set
|
||||
/// of bytes to represent the server's private key
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Deserialization`] if the private key is not a valid point on
|
||||
/// the group or zero.
|
||||
pub fn new_with_key(private_key_bytes: &[u8]) -> Result<Self> {
|
||||
let sk = CS::Group::deserialize_scalar(private_key_bytes)?;
|
||||
Ok(Self { sk })
|
||||
}
|
||||
|
||||
/// Produces a new instance of a [OprfServer] using a supplied set
|
||||
/// of bytes which are used as a seed to derive the server's private key.
|
||||
///
|
||||
/// Corresponds to DeriveKeyPair() function from the VOPRF specification.
|
||||
///
|
||||
/// # Errors
|
||||
/// - [`Error::DeriveKeyPair`] if the `input` and `seed` together are longer
|
||||
/// then `u16::MAX - 3`.
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
pub fn new_from_seed(seed: &[u8], info: &[u8]) -> Result<Self> {
|
||||
let sk = derive_key_internal::<CS>(seed, info, Mode::Oprf)?;
|
||||
Ok(Self { sk })
|
||||
}
|
||||
|
||||
/// Only used for tests
|
||||
#[cfg(test)]
|
||||
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
|
||||
self.sk
|
||||
}
|
||||
|
||||
/// 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(&self, blinded_element: &BlindedElement<CS>) -> EvaluationElement<CS> {
|
||||
EvaluationElement(blinded_element.0 * &self.sk)
|
||||
}
|
||||
|
||||
/// Computes the output of the OPRF on the server side
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Input`] if the `input` is longer then [`u16::MAX`].
|
||||
pub fn evaluate(&self, input: &[u8]) -> Result<Output<<CS as CipherSuite>::Hash>> {
|
||||
let input_element = hash_to_group::<CS>(input, Mode::Oprf)?;
|
||||
if CS::Group::is_identity_elem(input_element).into() {
|
||||
return Err(Error::Input);
|
||||
};
|
||||
let evaluated_element = input_element * &self.sk;
|
||||
|
||||
let issued_element = CS::Group::serialize_elem(evaluated_element);
|
||||
|
||||
server_evaluate_hash_input::<CS>(input, None, issued_element)
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// Convenience Structs //
|
||||
//==================== //
|
||||
/////////////////////////
|
||||
|
||||
/// Contains the fields that are returned by a non-verifiable client blind
|
||||
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||
pub struct OprfClientBlindResult<CS: CipherSuite> {
|
||||
/// The state to be persisted on the client
|
||||
pub state: OprfClient<CS>,
|
||||
/// The message to send to the server
|
||||
pub message: BlindedElement<CS>,
|
||||
}
|
||||
|
||||
///////////
|
||||
// Tests //
|
||||
// ===== //
|
||||
///////////
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::ptr;
|
||||
|
||||
use rand::TryRng;
|
||||
use rand::rngs::SysRng;
|
||||
|
||||
use super::*;
|
||||
use crate::Group;
|
||||
use crate::common::{Dst, STR_HASH_TO_GROUP};
|
||||
use crate::tests::helpers::prf;
|
||||
|
||||
fn base_retrieval<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
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);
|
||||
let client_finalize_result = client_blind_result.state.finalize(input, &message).unwrap();
|
||||
let res2 = prf::<CS>(input, server.get_private_key(), Mode::Oprf);
|
||||
assert_eq!(client_finalize_result, res2);
|
||||
}
|
||||
|
||||
fn base_inversion_unsalted<CS: CipherSuite>() {
|
||||
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();
|
||||
let client_finalize_result = client_blind_result
|
||||
.state
|
||||
.finalize(&input, &EvaluationElement(client_blind_result.message.0))
|
||||
.unwrap();
|
||||
|
||||
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()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(client_finalize_result, res2);
|
||||
}
|
||||
|
||||
fn server_evaluate<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
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);
|
||||
|
||||
let client_finalize = client_blind_result
|
||||
.state
|
||||
.finalize(input, &server_result)
|
||||
.unwrap();
|
||||
|
||||
// We expect the outputs from client and server to be equal given an identical
|
||||
// input
|
||||
let server_evaluate = server.evaluate(input).unwrap();
|
||||
assert_eq!(client_finalize, server_evaluate);
|
||||
|
||||
// We expect the outputs from client and server to be different given different
|
||||
// inputs
|
||||
let wrong_input = b"wrong input";
|
||||
let server_evaluate = server.evaluate(wrong_input).unwrap();
|
||||
assert!(client_finalize != server_evaluate);
|
||||
}
|
||||
|
||||
fn zeroize_oprf_client<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
let mut rng = SysRng;
|
||||
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
|
||||
let mut state = client_blind_result.state;
|
||||
unsafe { ptr::drop_in_place(&mut state) };
|
||||
assert!(state.serialize().iter().all(|&x| x == 0));
|
||||
|
||||
let mut message = client_blind_result.message;
|
||||
unsafe { ptr::drop_in_place(&mut message) };
|
||||
assert!(message.serialize().iter().all(|&x| x == 0));
|
||||
}
|
||||
|
||||
fn zeroize_oprf_server<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
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);
|
||||
|
||||
let mut state = server;
|
||||
unsafe { ptr::drop_in_place(&mut state) };
|
||||
assert!(state.serialize().iter().all(|&x| x == 0));
|
||||
|
||||
unsafe { ptr::drop_in_place(&mut message) };
|
||||
assert!(message.serialize().iter().all(|&x| x == 0));
|
||||
}
|
||||
|
||||
crate::tests::test_all_curves!(
|
||||
base_retrieval,
|
||||
base_inversion_unsalted,
|
||||
server_evaluate,
|
||||
zeroize_oprf_client,
|
||||
zeroize_oprf_server,
|
||||
);
|
||||
}
|
||||
+883
@@ -0,0 +1,883 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
// Copyright (c) VexaHub and contributors.
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
//! Contains the main POPRF API
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::vec::Vec;
|
||||
use core::iter::{self, Map, Repeat, Zip};
|
||||
|
||||
use derive_where::derive_where;
|
||||
use digest::{Digest, Output, OutputSizeUser};
|
||||
use hybrid_array::typenum::Unsigned;
|
||||
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,
|
||||
};
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::serialization::serde::{Element, Scalar};
|
||||
use crate::{CipherSuite, Error, Group, Result};
|
||||
|
||||
////////////////////////////
|
||||
// High-level API Structs //
|
||||
// ====================== //
|
||||
////////////////////////////
|
||||
|
||||
/// A client which engages with a [PoprfServer] in verifiable mode, meaning
|
||||
/// that the OPRF outputs can be checked against a server public key.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct PoprfClient<CS: CipherSuite> {
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) blind: <CS::Group as Group>::Scalar,
|
||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||
pub(crate) blinded_element: <CS::Group as Group>::Elem,
|
||||
}
|
||||
|
||||
/// A server which engages with a [PoprfClient] in verifiable mode, meaning
|
||||
/// that the OPRF outputs can be checked against a server public key.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct PoprfServer<CS: CipherSuite> {
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) sk: <CS::Group as Group>::Scalar,
|
||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||
pub(crate) pk: <CS::Group as Group>::Elem,
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// API Implementations //
|
||||
// =================== //
|
||||
/////////////////////////
|
||||
|
||||
impl<CS: CipherSuite> PoprfClient<CS> {
|
||||
/// Computes the first step for the multiplicative blinding version of
|
||||
/// DH-OPRF.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`].
|
||||
pub fn blind<R: TryRng + TryCryptoRng>(
|
||||
input: &[u8],
|
||||
blinding_factor_rng: &mut R,
|
||||
) -> Result<PoprfClientBlindResult<CS>> {
|
||||
let blind = CS::Group::random_scalar(blinding_factor_rng)?;
|
||||
Self::deterministic_blind_unchecked_inner(input, blind)
|
||||
}
|
||||
|
||||
/// Computes the first step for the multiplicative blinding version of
|
||||
/// DH-OPRF, taking a blinding factor scalar as input instead of sampling
|
||||
/// from an RNG.
|
||||
///
|
||||
/// # Caution
|
||||
///
|
||||
/// This should be used with caution, since it does not perform any checks
|
||||
/// on the validity of the blinding factor!
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`].
|
||||
#[cfg(any(feature = "danger", test))]
|
||||
pub fn deterministic_blind_unchecked(
|
||||
input: &[u8],
|
||||
blind: <CS::Group as Group>::Scalar,
|
||||
) -> Result<PoprfClientBlindResult<CS>> {
|
||||
Self::deterministic_blind_unchecked_inner(input, blind)
|
||||
}
|
||||
|
||||
/// Can only fail with [`Error::Input`].
|
||||
fn deterministic_blind_unchecked_inner(
|
||||
input: &[u8],
|
||||
blind: <CS::Group as Group>::Scalar,
|
||||
) -> Result<PoprfClientBlindResult<CS>> {
|
||||
let blinded_element = deterministic_blind_unchecked::<CS>(input, &blind, Mode::Poprf)?;
|
||||
Ok(PoprfClientBlindResult {
|
||||
state: Self {
|
||||
blind,
|
||||
blinded_element,
|
||||
},
|
||||
message: BlindedElement(blinded_element),
|
||||
})
|
||||
}
|
||||
|
||||
/// Computes the third step for the multiplicative blinding version of
|
||||
/// DH-OPRF, in which the client unblinds the server's message.
|
||||
///
|
||||
/// # Errors
|
||||
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
|
||||
/// - [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`].
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
/// - [`Error::ProofVerification`] if the `proof` failed to verify.
|
||||
pub fn finalize(
|
||||
&self,
|
||||
input: &[u8],
|
||||
evaluation_element: &EvaluationElement<CS>,
|
||||
proof: &Proof<CS>,
|
||||
pk: <CS::Group as Group>::Elem,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<Output<CS::Hash>>
|
||||
where
|
||||
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArraySize,
|
||||
{
|
||||
let clients = core::array::from_ref(self);
|
||||
let messages = core::array::from_ref(evaluation_element);
|
||||
|
||||
let mut batch_result =
|
||||
Self::batch_finalize(iter::once(input), clients, messages, proof, pk, info)?;
|
||||
batch_result.next().unwrap()
|
||||
}
|
||||
|
||||
/// Allows for batching of the finalization of multiple [PoprfClient]
|
||||
/// and [EvaluationElement] pairs
|
||||
///
|
||||
/// # Errors
|
||||
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
/// - [`Error::Batch`] if the number of `inputs`, `clients` and `messages`
|
||||
/// don't match or is longer than [`u16::MAX`].
|
||||
/// - [`Error::ProofVerification`] if the `proof` failed to verify.
|
||||
///
|
||||
/// The resulting messages can each fail individually with [`Error::Input`]
|
||||
/// if the `input` is empty or longer than [`u16::MAX`].
|
||||
pub fn batch_finalize<'a, II: 'a + Iterator<Item = &'a [u8]> + ExactSizeIterator, IC, IM>(
|
||||
inputs: II,
|
||||
clients: &'a IC,
|
||||
messages: &'a IM,
|
||||
proof: &Proof<CS>,
|
||||
pk: <CS::Group as Group>::Elem,
|
||||
info: Option<&'a [u8]>,
|
||||
) -> Result<PoprfClientBatchFinalizeResult<'a, CS, II, IC, IM>>
|
||||
where
|
||||
CS: 'a,
|
||||
&'a IC: 'a + IntoIterator<Item = &'a 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: ArraySize,
|
||||
{
|
||||
let unblinded_elements = poprf_unblind(clients, messages, pk, proof, info)?;
|
||||
|
||||
finalize_after_unblind::<'a, CS, _, _>(unblinded_elements, inputs, info)
|
||||
}
|
||||
|
||||
/// Only used for test functions
|
||||
#[cfg(test)]
|
||||
pub fn get_blind(&self) -> <CS::Group as Group>::Scalar {
|
||||
self.blind
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> PoprfServer<CS> {
|
||||
/// Produces a new instance of a [PoprfServer] using a supplied RNG
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
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, &[])
|
||||
}
|
||||
|
||||
/// Produces a new instance of a [PoprfServer] using a supplied set of
|
||||
/// bytes to represent the server's private key
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Deserialization`] if the private key is not a valid point on
|
||||
/// the group or zero.
|
||||
pub fn new_with_key(key: &[u8]) -> Result<Self> {
|
||||
let sk = CS::Group::deserialize_scalar(key)?;
|
||||
let pk = CS::Group::base_elem() * &sk;
|
||||
Ok(Self { sk, pk })
|
||||
}
|
||||
|
||||
/// Produces a new instance of a [PoprfServer] using a supplied set of
|
||||
/// bytes which are used as a seed to derive the server's private key.
|
||||
///
|
||||
/// Corresponds to DeriveKeyPair() function from the VOPRF specification.
|
||||
///
|
||||
/// # Errors
|
||||
/// - [`Error::DeriveKeyPair`] if the `input` and `seed` together are longer
|
||||
/// then `u16::MAX - 3`.
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
pub fn new_from_seed(seed: &[u8], info: &[u8]) -> Result<Self> {
|
||||
let (sk, pk) = derive_keypair::<CS>(seed, info, Mode::Poprf)?;
|
||||
Ok(Self { sk, pk })
|
||||
}
|
||||
|
||||
/// Only used for tests
|
||||
#[cfg(test)]
|
||||
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
|
||||
self.sk
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// # 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: TryRng + TryCryptoRng>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
blinded_element: &BlindedElement<CS>,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<PoprfServerEvaluateResult<CS>> {
|
||||
let PoprfServerBatchEvaluatePrepareResult {
|
||||
mut prepared_evaluation_elements,
|
||||
prepared_tweak,
|
||||
} = self.batch_blind_evaluate_prepare(iter::once(blinded_element), info)?;
|
||||
|
||||
let prepared_evaluation_element = prepared_evaluation_elements.next().unwrap();
|
||||
let prepared_evaluation_elements = core::array::from_ref(&prepared_evaluation_element);
|
||||
|
||||
let PoprfServerBatchEvaluateFinishResult {
|
||||
mut messages,
|
||||
proof,
|
||||
} = Self::batch_blind_evaluate_finish(
|
||||
rng,
|
||||
iter::once(blinded_element),
|
||||
prepared_evaluation_elements,
|
||||
&prepared_tweak,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Ok(PoprfServerEvaluateResult {
|
||||
message: messages.next().unwrap(),
|
||||
proof,
|
||||
})
|
||||
}
|
||||
|
||||
/// Allows for batching of the evaluation of multiple [BlindedElement]
|
||||
/// messages from a [PoprfClient]
|
||||
///
|
||||
/// # Errors
|
||||
/// - [`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: TryRng + TryCryptoRng, IE>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
blinded_elements: &'a IE,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<PoprfServerBatchEvaluateResult<CS>>
|
||||
where
|
||||
CS: 'a,
|
||||
&'a IE: 'a + IntoIterator<Item = &'a BlindedElement<CS>>,
|
||||
<&'a IE as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let PoprfServerBatchEvaluatePrepareResult {
|
||||
prepared_evaluation_elements,
|
||||
prepared_tweak,
|
||||
} = self.batch_blind_evaluate_prepare(blinded_elements.into_iter(), info)?;
|
||||
|
||||
let prepared_evaluation_elements: Vec<_> = prepared_evaluation_elements.collect();
|
||||
|
||||
// This can't fail because we know the size of the inputs.
|
||||
let PoprfServerBatchEvaluateFinishResult { messages, proof } =
|
||||
Self::batch_blind_evaluate_finish::<_, _, Vec<_>>(
|
||||
rng,
|
||||
blinded_elements.into_iter(),
|
||||
&prepared_evaluation_elements,
|
||||
&prepared_tweak,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let messages: Vec<_> = messages.collect();
|
||||
|
||||
Ok(PoprfServerBatchEvaluateResult { messages, proof })
|
||||
}
|
||||
|
||||
/// Alternative version of `batch_blind_evaluate` without
|
||||
/// memory allocation. Returned [`PreparedEvaluationElement`] have to
|
||||
/// be [`collect`](Iterator::collect)ed and passed into
|
||||
/// [`batch_blind_evaluate_finish`](Self::batch_blind_evaluate_finish).
|
||||
///
|
||||
/// # Errors
|
||||
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
pub fn batch_blind_evaluate_prepare<'a, I: Iterator<Item = &'a BlindedElement<CS>>>(
|
||||
&self,
|
||||
blinded_elements: I,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<PoprfServerBatchEvaluatePrepareResult<CS, I>>
|
||||
where
|
||||
CS: 'a,
|
||||
{
|
||||
let tweak = compute_tweak::<CS>(self.sk, info)?;
|
||||
|
||||
Ok(PoprfServerBatchEvaluatePrepareResult {
|
||||
prepared_evaluation_elements: blinded_elements.zip(iter::repeat(tweak)).map(
|
||||
|(blinded_element, tweak)| {
|
||||
PreparedEvaluationElement(EvaluationElement(
|
||||
blinded_element.0 * &CS::Group::invert_scalar(tweak),
|
||||
))
|
||||
},
|
||||
),
|
||||
prepared_tweak: PoprfPreparedTweak(tweak),
|
||||
})
|
||||
}
|
||||
|
||||
/// See [`batch_blind_evaluate_prepare`](Self::batch_blind_evaluate_prepare)
|
||||
/// for more details.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Batch`] if the number of `blinded_elements` and
|
||||
/// `prepared_evaluation_elements` don't match or is longer then
|
||||
/// [`u16::MAX`]
|
||||
pub fn batch_blind_evaluate_finish<
|
||||
'a,
|
||||
'b,
|
||||
R: TryRng + TryCryptoRng,
|
||||
IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator,
|
||||
IE,
|
||||
>(
|
||||
rng: &mut R,
|
||||
blinded_elements: IB,
|
||||
prepared_evaluation_elements: &'b IE,
|
||||
prepared_tweak: &PoprfPreparedTweak<CS>,
|
||||
) -> Result<PoprfServerBatchEvaluateFinishResult<'b, CS, IE>>
|
||||
where
|
||||
CS: 'a,
|
||||
&'b IE: IntoIterator<Item = &'b PreparedEvaluationElement<CS>>,
|
||||
<&'b IE as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let g = CS::Group::base_elem();
|
||||
let tweak = prepared_tweak.0;
|
||||
let tweaked_key = g * &tweak;
|
||||
|
||||
let proof = generate_proof(
|
||||
rng,
|
||||
tweak,
|
||||
g,
|
||||
tweaked_key,
|
||||
prepared_evaluation_elements
|
||||
.into_iter()
|
||||
.map(|element| element.0.0),
|
||||
blinded_elements.map(|element| element.0),
|
||||
Mode::Poprf,
|
||||
)?;
|
||||
|
||||
let messages = prepared_evaluation_elements.into_iter().map(<fn(
|
||||
&PreparedEvaluationElement<CS>,
|
||||
) -> _>::from(
|
||||
|element| EvaluationElement(element.0.0),
|
||||
));
|
||||
|
||||
Ok(PoprfServerBatchEvaluateFinishResult { messages, proof })
|
||||
}
|
||||
|
||||
/// Computes the output of the VOPRF on the server side
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Input`] if the `input` is longer then [`u16::MAX`].
|
||||
pub fn evaluate(
|
||||
&self,
|
||||
input: &[u8],
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<Output<<CS as CipherSuite>::Hash>> {
|
||||
let input_element = hash_to_group::<CS>(input, Mode::Poprf)?;
|
||||
if CS::Group::is_identity_elem(input_element).into() {
|
||||
return Err(Error::Input);
|
||||
};
|
||||
|
||||
let tweak = compute_tweak::<CS>(self.sk, info)?;
|
||||
|
||||
let evaluated_element = input_element * &CS::Group::invert_scalar(tweak);
|
||||
|
||||
let issued_element = CS::Group::serialize_elem(evaluated_element);
|
||||
|
||||
server_evaluate_hash_input::<CS>(input, info, issued_element)
|
||||
}
|
||||
|
||||
/// Retrieves the server's public key
|
||||
pub fn get_public_key(&self) -> <CS::Group as Group>::Elem {
|
||||
self.pk
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> BlindedElement<CS> {
|
||||
/// Creates a [BlindedElement] from a raw group element.
|
||||
///
|
||||
/// # Caution
|
||||
///
|
||||
/// This should be used with caution, since it does not perform any checks
|
||||
/// on the validity of the value itself!
|
||||
#[cfg(feature = "danger")]
|
||||
pub fn from_value_unchecked(value: <CS::Group as Group>::Elem) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
|
||||
/// Exposes the internal value
|
||||
#[cfg(feature = "danger")]
|
||||
pub fn value(&self) -> <CS::Group as Group>::Elem {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> EvaluationElement<CS> {
|
||||
/// Creates an [EvaluationElement] from a raw group element.
|
||||
///
|
||||
/// # Caution
|
||||
///
|
||||
/// This should be used with caution, since it does not perform any checks
|
||||
/// on the validity of the value itself!
|
||||
#[cfg(feature = "danger")]
|
||||
pub fn from_value_unchecked(value: <CS::Group as Group>::Elem) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
|
||||
/// Exposes the internal value
|
||||
#[cfg(feature = "danger")]
|
||||
pub fn value(&self) -> <CS::Group as Group>::Elem {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// Convenience Structs //
|
||||
//==================== //
|
||||
/////////////////////////
|
||||
|
||||
/// Contains the fields that are returned by a verifiable client blind
|
||||
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||
pub struct PoprfClientBlindResult<CS: CipherSuite> {
|
||||
/// The state to be persisted on the client
|
||||
pub state: PoprfClient<CS>,
|
||||
/// The message to send to the server
|
||||
pub message: BlindedElement<CS>,
|
||||
}
|
||||
|
||||
/// Concrete return type for [`PoprfClient::batch_finalize`].
|
||||
pub type PoprfClientBatchFinalizeResult<'a, CS, II, IC, IM> =
|
||||
FinalizeAfterUnblindResult<'a, CS, PoprfUnblindResult<'a, CS, IC, IM>, II>;
|
||||
|
||||
/// Contains the fields that are returned by a verifiable server evaluate
|
||||
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||
pub struct PoprfServerEvaluateResult<CS: CipherSuite> {
|
||||
/// The message to send to the client
|
||||
pub message: EvaluationElement<CS>,
|
||||
/// The proof for the client to verify
|
||||
pub proof: Proof<CS>,
|
||||
}
|
||||
|
||||
/// Contains the fields that are returned by a verifiable server batch evaluate
|
||||
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||
#[cfg(feature = "alloc")]
|
||||
pub struct PoprfServerBatchEvaluateResult<CS: CipherSuite> {
|
||||
/// The messages to send to the client
|
||||
pub messages: Vec<EvaluationElement<CS>>,
|
||||
/// The proof for the client to verify
|
||||
pub proof: Proof<CS>,
|
||||
}
|
||||
|
||||
/// Concrete type of [`EvaluationElement`]s in
|
||||
/// [`PoprfServerBatchEvaluatePrepareResult`].
|
||||
pub type PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I> = Map<
|
||||
Zip<I, Repeat<<<CS as CipherSuite>::Group as Group>::Scalar>>,
|
||||
fn(
|
||||
(
|
||||
&BlindedElement<CS>,
|
||||
<<CS as CipherSuite>::Group as Group>::Scalar,
|
||||
),
|
||||
) -> PreparedEvaluationElement<CS>,
|
||||
>;
|
||||
|
||||
/// Prepared tweak by a partially verifiable server batch evaluate prepare.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct PoprfPreparedTweak<CS: CipherSuite>(
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
<CS::Group as Group>::Scalar,
|
||||
);
|
||||
|
||||
/// Contains the fields that are returned by a partially verifiable server batch
|
||||
/// evaluate prepare
|
||||
#[derive_where(Debug; I, <CS::Group as Group>::Scalar)]
|
||||
pub struct PoprfServerBatchEvaluatePrepareResult<CS: CipherSuite, I> {
|
||||
/// Prepared [`EvaluationElement`].
|
||||
pub prepared_evaluation_elements: PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I>,
|
||||
/// Prepared tweak.
|
||||
pub prepared_tweak: PoprfPreparedTweak<CS>,
|
||||
}
|
||||
|
||||
/// Concrete type of [`EvaluationElement`]s in
|
||||
/// [`PoprfServerBatchEvaluateFinishResult`].
|
||||
pub type PoprfServerBatchEvaluateFinishedMessages<'a, CS, I> = Map<
|
||||
<&'a I as IntoIterator>::IntoIter,
|
||||
fn(&PreparedEvaluationElement<CS>) -> EvaluationElement<CS>,
|
||||
>;
|
||||
|
||||
/// Contains the fields that are returned by a verifiable server batch evaluate
|
||||
/// finish.
|
||||
#[derive_where(Debug; <&'a I as IntoIterator>::IntoIter, <CS::Group as Group>::Scalar)]
|
||||
pub struct PoprfServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I>
|
||||
where
|
||||
&'a I: IntoIterator<Item = &'a PreparedEvaluationElement<CS>>,
|
||||
{
|
||||
/// The [`EvaluationElement`]s to send to the client
|
||||
pub messages: PoprfServerBatchEvaluateFinishedMessages<'a, CS, I>,
|
||||
/// The proof for the client to verify
|
||||
pub proof: Proof<CS>,
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// Inner functions //
|
||||
// =============== //
|
||||
/////////////////////
|
||||
|
||||
/// Inner function for POPRF blind. Computes the tweaked key from the server
|
||||
/// public key and info.
|
||||
///
|
||||
/// Can only fail with [`Error::Info`] or [`Error::Protocol`]
|
||||
fn compute_tweaked_key<CS: CipherSuite>(
|
||||
pk: <CS::Group as Group>::Elem,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<<CS::Group as Group>::Elem> {
|
||||
// None for info is treated the same as empty bytes
|
||||
let info = info.unwrap_or_default();
|
||||
|
||||
// framedInfo = "Info" || I2OSP(len(info), 2) || info
|
||||
// m = G.HashToScalar(framedInfo)
|
||||
// T = G.ScalarBaseMult(m)
|
||||
// tweakedKey = T + pkS
|
||||
// if tweakedKey == G.Identity():
|
||||
// raise InvalidInputError
|
||||
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);
|
||||
// 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();
|
||||
|
||||
let t = CS::Group::base_elem() * &m;
|
||||
let tweaked_key = t + &pk;
|
||||
|
||||
// Check if resulting element
|
||||
match bool::from(CS::Group::is_identity_elem(tweaked_key)) {
|
||||
true => Err(Error::Protocol),
|
||||
false => Ok(tweaked_key),
|
||||
}
|
||||
}
|
||||
|
||||
/// Inner function for POPRF evaluate. Computes the tweak from the server
|
||||
/// private key and info.
|
||||
///
|
||||
/// Can only fail with [`Error::Info`] and [`Error::Protocol`].
|
||||
fn compute_tweak<CS: CipherSuite>(
|
||||
sk: <CS::Group as Group>::Scalar,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<<CS::Group as Group>::Scalar> {
|
||||
// None for info is treated the same as empty bytes
|
||||
let info = info.unwrap_or_default();
|
||||
|
||||
// framedInfo = "Info" || I2OSP(len(info), 2) || info
|
||||
// m = G.HashToScalar(framedInfo)
|
||||
// t = skS + m
|
||||
// if t == 0:
|
||||
// raise InverseError
|
||||
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);
|
||||
// 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();
|
||||
|
||||
let t = sk + &m;
|
||||
|
||||
// Check if resulting element is equal to zero
|
||||
match bool::from(CS::Group::is_zero_scalar(t)) {
|
||||
true => Err(Error::Protocol),
|
||||
false => Ok(t),
|
||||
}
|
||||
}
|
||||
|
||||
type PoprfUnblindResult<'a, CS, IC, IM> = Map<
|
||||
Zip<
|
||||
Map<
|
||||
<&'a IC as IntoIterator>::IntoIter,
|
||||
fn(&PoprfClient<CS>) -> <<CS as CipherSuite>::Group as Group>::Scalar,
|
||||
>,
|
||||
<&'a IM as IntoIterator>::IntoIter,
|
||||
>,
|
||||
fn(
|
||||
(
|
||||
<<CS as CipherSuite>::Group as Group>::Scalar,
|
||||
&'a EvaluationElement<CS>,
|
||||
),
|
||||
) -> <<CS as CipherSuite>::Group as Group>::Elem,
|
||||
>;
|
||||
|
||||
/// Can only fail with [`Error::Info`], [`Error::Protocol`], [`Error::Batch] or
|
||||
/// [`Error::ProofVerification`].
|
||||
fn poprf_unblind<'a, CS: 'a + CipherSuite, IC, IM>(
|
||||
clients: &'a IC,
|
||||
messages: &'a IM,
|
||||
pk: <CS::Group as Group>::Elem,
|
||||
proof: &Proof<CS>,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<PoprfUnblindResult<'a, CS, IC, IM>>
|
||||
where
|
||||
&'a IC: 'a + IntoIterator<Item = &'a PoprfClient<CS>>,
|
||||
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
|
||||
<&'a IM as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let info = info.unwrap_or_default();
|
||||
let tweaked_key = compute_tweaked_key::<CS>(pk, Some(info))?;
|
||||
|
||||
let g = CS::Group::base_elem();
|
||||
|
||||
let blinds = clients
|
||||
.into_iter()
|
||||
// Convert to `fn` pointer to make a return type possible.
|
||||
.map(<fn(&PoprfClient<CS>) -> _>::from(|x| x.blind));
|
||||
let evaluation_elements = messages.into_iter().map(|element| element.0);
|
||||
let blinded_elements = clients.into_iter().map(|client| client.blinded_element);
|
||||
|
||||
verify_proof(
|
||||
g,
|
||||
tweaked_key,
|
||||
evaluation_elements,
|
||||
blinded_elements,
|
||||
proof,
|
||||
Mode::Poprf,
|
||||
)?;
|
||||
|
||||
Ok(blinds
|
||||
.zip(messages)
|
||||
.map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
|
||||
}
|
||||
|
||||
type FinalizeAfterUnblindResult<'a, CS, IE, II> = Map<
|
||||
Zip<Zip<IE, II>, Repeat<&'a [u8]>>,
|
||||
fn(
|
||||
((<<CS as CipherSuite>::Group as Group>::Elem, &[u8]), &[u8]),
|
||||
) -> Result<Output<<CS as CipherSuite>::Hash>>,
|
||||
>;
|
||||
|
||||
/// Can only fail with [`Error::Batch`] and returned values can only fail with
|
||||
/// [`Error::Info`] or [`Error::Input`] individually.
|
||||
fn finalize_after_unblind<
|
||||
'a,
|
||||
CS: CipherSuite,
|
||||
IE: 'a + Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
||||
II: 'a + Iterator<Item = &'a [u8]> + ExactSizeIterator,
|
||||
>(
|
||||
unblinded_elements: IE,
|
||||
inputs: II,
|
||||
info: Option<&'a [u8]>,
|
||||
) -> Result<FinalizeAfterUnblindResult<'a, CS, IE, II>>
|
||||
where
|
||||
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArraySize,
|
||||
{
|
||||
if unblinded_elements.len() != inputs.len() {
|
||||
return Err(Error::Batch);
|
||||
}
|
||||
|
||||
let info = info.unwrap_or_default();
|
||||
|
||||
Ok(unblinded_elements.zip(inputs).zip(iter::repeat(info)).map(
|
||||
|((unblinded_element, input), info)| {
|
||||
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
|
||||
|
||||
// hashInput = I2OSP(len(input), 2) || input ||
|
||||
// I2OSP(len(info), 2) || info ||
|
||||
// I2OSP(len(unblindedElement), 2) || unblindedElement ||
|
||||
// "Finalize"
|
||||
// return Hash(hashInput)
|
||||
let output = CS::Hash::new()
|
||||
.chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?)
|
||||
.chain_update(input.as_ref())
|
||||
.chain_update(i2osp_2(info.as_ref().len()).map_err(|_| Error::Info)?)
|
||||
.chain_update(info.as_ref())
|
||||
.chain_update(elem_len)
|
||||
.chain_update(CS::Group::serialize_elem(unblinded_element))
|
||||
.chain_update(STR_FINALIZE)
|
||||
.finalize();
|
||||
|
||||
Ok(output)
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
///////////
|
||||
// Tests //
|
||||
// ===== //
|
||||
///////////
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::ptr;
|
||||
|
||||
use rand::rngs::SysRng;
|
||||
|
||||
use super::*;
|
||||
use crate::Group;
|
||||
use crate::common::STR_HASH_TO_GROUP;
|
||||
|
||||
fn prf<CS: CipherSuite>(
|
||||
input: &[u8],
|
||||
key: <CS::Group as Group>::Scalar,
|
||||
info: &[u8],
|
||||
mode: Mode,
|
||||
) -> Output<CS::Hash> {
|
||||
let t = compute_tweak::<CS>(key, Some(info)).unwrap();
|
||||
|
||||
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
|
||||
let res = point * &CS::Group::invert_scalar(t);
|
||||
|
||||
finalize_after_unblind::<CS, _, _>(iter::once(res), iter::once(input), Some(info))
|
||||
.unwrap()
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn verifiable_retrieval<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
let info = b"info";
|
||||
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 client_finalize_result = client_blind_result
|
||||
.state
|
||||
.finalize(
|
||||
input,
|
||||
&server_result.message,
|
||||
&server_result.proof,
|
||||
server.get_public_key(),
|
||||
Some(info),
|
||||
)
|
||||
.unwrap();
|
||||
let res2 = prf::<CS>(input, server.get_private_key(), info, Mode::Poprf);
|
||||
assert_eq!(client_finalize_result, res2);
|
||||
}
|
||||
|
||||
fn verifiable_bad_public_key<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
let info = b"info";
|
||||
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);
|
||||
// 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()
|
||||
};
|
||||
let client_finalize_result = client_blind_result.state.finalize(
|
||||
input,
|
||||
&server_result.message,
|
||||
&server_result.proof,
|
||||
wrong_pk,
|
||||
Some(info),
|
||||
);
|
||||
assert!(client_finalize_result.is_err());
|
||||
}
|
||||
|
||||
fn verifiable_server_evaluate<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
let info = Some(b"info".as_slice());
|
||||
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
|
||||
.blind_evaluate(&mut rng, &client_blind_result.message, info)
|
||||
.unwrap();
|
||||
|
||||
let client_finalize = client_blind_result
|
||||
.state
|
||||
.finalize(
|
||||
input,
|
||||
&server_result.message,
|
||||
&server_result.proof,
|
||||
server.get_public_key(),
|
||||
info,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// We expect the outputs from client and server to be equal given an identical
|
||||
// input
|
||||
let server_evaluate = server.evaluate(input, info).unwrap();
|
||||
assert_eq!(client_finalize, server_evaluate);
|
||||
|
||||
// We expect the outputs from client and server to be different given different
|
||||
// inputs
|
||||
let wrong_input = b"wrong input";
|
||||
let server_evaluate = server.evaluate(wrong_input, info).unwrap();
|
||||
assert!(client_finalize != server_evaluate);
|
||||
}
|
||||
|
||||
fn zeroize_verifiable_client<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
let mut rng = SysRng;
|
||||
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
|
||||
let mut state = client_blind_result.state;
|
||||
unsafe { ptr::drop_in_place(&mut state) };
|
||||
assert!(state.serialize().iter().all(|&x| x == 0));
|
||||
|
||||
let mut message = client_blind_result.message;
|
||||
unsafe { ptr::drop_in_place(&mut message) };
|
||||
assert!(message.serialize().iter().all(|&x| x == 0));
|
||||
}
|
||||
|
||||
fn zeroize_verifiable_server<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
let info = b"info";
|
||||
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 mut state = server;
|
||||
unsafe { ptr::drop_in_place(&mut state) };
|
||||
assert!(state.serialize().iter().all(|&x| x == 0));
|
||||
|
||||
let mut message = server_result.message;
|
||||
unsafe { ptr::drop_in_place(&mut message) };
|
||||
assert!(message.serialize().iter().all(|&x| x == 0));
|
||||
|
||||
let mut proof = server_result.proof;
|
||||
unsafe { ptr::drop_in_place(&mut proof) };
|
||||
assert!(proof.serialize().iter().all(|&x| x == 0));
|
||||
}
|
||||
|
||||
crate::tests::test_all_curves!(
|
||||
verifiable_retrieval,
|
||||
verifiable_bad_public_key,
|
||||
verifiable_server_evaluate,
|
||||
zeroize_verifiable_client,
|
||||
zeroize_verifiable_server,
|
||||
);
|
||||
}
|
||||
+403
-141
@@ -1,192 +1,454 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
// Copyright (c) VexaHub and contributors.
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
//! Handles the serialization of each of the components used
|
||||
//! in the VOPRF protocol
|
||||
//! Handles the serialization of each of the components used in the VOPRF
|
||||
//! protocol
|
||||
|
||||
use hybrid_array::Array;
|
||||
use hybrid_array::typenum::{Sum, Unsigned};
|
||||
|
||||
use crate::{
|
||||
errors::InternalError,
|
||||
group::Group,
|
||||
voprf::{
|
||||
BlindedElement, EvaluationElement, NonVerifiableClient, NonVerifiableServer, Proof,
|
||||
VerifiableClient, VerifiableServer,
|
||||
},
|
||||
BlindedElement, CipherSuite, Error, EvaluationElement, Group, OprfClient, OprfServer,
|
||||
PoprfClient, PoprfServer, Proof, Result, VoprfClient, VoprfServer,
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use core::marker::PhantomData;
|
||||
use digest::{BlockInput, Digest};
|
||||
use generic_array::typenum::Unsigned;
|
||||
|
||||
/////////////////////////////
|
||||
// Deserialization Helpers //
|
||||
// ======================= //
|
||||
/////////////////////////////
|
||||
|
||||
fn deserialize_elem<G: Group>(input: &mut &[u8]) -> Result<G::Elem> {
|
||||
let input = input
|
||||
.take_ext(G::ElemLen::USIZE)
|
||||
.ok_or(Error::Deserialization)?;
|
||||
G::deserialize_elem(input)
|
||||
}
|
||||
|
||||
fn deserialize_scalar<G: Group>(input: &mut &[u8]) -> Result<G::Scalar> {
|
||||
let input = input
|
||||
.take_ext(G::ScalarLen::USIZE)
|
||||
.ok_or(Error::Deserialization)?;
|
||||
G::deserialize_scalar(input)
|
||||
}
|
||||
|
||||
trait SliceExt {
|
||||
fn take_ext<'a>(self: &mut &'a Self, take: usize) -> Option<&'a Self>;
|
||||
}
|
||||
|
||||
impl<T> SliceExt for [T] {
|
||||
fn take_ext<'a>(self: &mut &'a Self, take: usize) -> Option<&'a Self> {
|
||||
if take > self.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (front, back) = self.split_at(take);
|
||||
*self = back;
|
||||
Some(front)
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// Serialization Macros //
|
||||
// ======================== //
|
||||
//////////////////////////////
|
||||
|
||||
macro_rules! impl_serde_scalar {
|
||||
($ty:ident, $len:ident, $field:ident) => {
|
||||
/// Length in bytes for serialization.
|
||||
pub type $len<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
|
||||
|
||||
impl<CS: CipherSuite> $ty<CS> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Array<u8, $len<CS>> {
|
||||
CS::Group::serialize_scalar(self.$field)
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
||||
let $field = deserialize_scalar::<CS::Group>(&mut input)?;
|
||||
|
||||
if !input.is_empty() {
|
||||
return Err(Error::Deserialization);
|
||||
}
|
||||
|
||||
Ok(Self { $field })
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_serde_scalar_elem {
|
||||
($ty:ident, $len:ident, $scalar_field:ident, $elem_field:ident) => {
|
||||
/// Length in bytes for serialization.
|
||||
pub type $len<CS> = Sum<
|
||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
||||
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
||||
>;
|
||||
|
||||
impl<CS: CipherSuite> $ty<CS> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Array<u8, $len<CS>> {
|
||||
<CS::Group as Group>::serialize_scalar(self.$scalar_field)
|
||||
.concat(<CS::Group as Group>::serialize_elem(self.$elem_field))
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
||||
let $scalar_field = deserialize_scalar::<CS::Group>(&mut input)?;
|
||||
let $elem_field = deserialize_elem::<CS::Group>(&mut input)?;
|
||||
|
||||
if !input.is_empty() {
|
||||
return Err(Error::Deserialization);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
$scalar_field,
|
||||
$elem_field,
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_serde_elem {
|
||||
($ty:ident, $len:ident) => {
|
||||
/// Length in bytes for serialization.
|
||||
pub type $len<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen;
|
||||
|
||||
impl<CS: CipherSuite> $ty<CS> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Array<u8, $len<CS>> {
|
||||
CS::Group::serialize_elem(self.0)
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
||||
let value = deserialize_elem::<CS::Group>(&mut input)?;
|
||||
|
||||
if !input.is_empty() {
|
||||
return Err(Error::Deserialization);
|
||||
}
|
||||
|
||||
Ok(Self(value))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
// Serialization and Deserialization for High-Level API //
|
||||
// ==================================================== //
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
impl<G: Group, H: BlockInput + Digest> NonVerifiableClient<G, H> {
|
||||
impl_serde_scalar!(OprfClient, OprfClientLen, blind);
|
||||
impl_serde_scalar!(OprfServer, OprfServerLen, sk);
|
||||
|
||||
impl_serde_elem!(BlindedElement, BlindedElementLen);
|
||||
impl_serde_elem!(EvaluationElement, EvaluationElementLen);
|
||||
|
||||
impl_serde_scalar_elem!(VoprfClient, VoprfClientLen, blind, blinded_element);
|
||||
impl_serde_scalar_elem!(PoprfClient, PoprfClientLen, blind, blinded_element);
|
||||
impl_serde_scalar_elem!(VoprfServer, VoprfServerLen, sk, pk);
|
||||
impl_serde_scalar_elem!(PoprfServer, PoprfServerLen, sk, pk);
|
||||
|
||||
/////////////////////
|
||||
// Proof (One-Off) //
|
||||
// =============== //
|
||||
/////////////////////
|
||||
|
||||
/// Length of [`Proof`] in bytes for serialization.
|
||||
pub type ProofLen<CS> = Sum<
|
||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
||||
>;
|
||||
|
||||
impl<CS: CipherSuite> Proof<CS> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
[G::scalar_as_bytes(self.blind).as_slice(), &self.data].concat()
|
||||
pub fn serialize(&self) -> Array<u8, ProofLen<CS>> {
|
||||
CS::Group::serialize_scalar(self.c_scalar)
|
||||
.concat(CS::Group::serialize_scalar(self.s_scalar))
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
|
||||
let scalar_len = <G as Group>::ScalarLen::USIZE;
|
||||
if input.len() < scalar_len {
|
||||
return Err(InternalError::SizeError);
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
||||
let c_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
|
||||
let s_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
|
||||
|
||||
if !input.is_empty() {
|
||||
return Err(Error::Deserialization);
|
||||
}
|
||||
|
||||
let blind = G::from_scalar_slice(&input[..scalar_len])?;
|
||||
let data = input[scalar_len..].to_vec();
|
||||
|
||||
Ok(Self {
|
||||
blind,
|
||||
data,
|
||||
hash: PhantomData,
|
||||
})
|
||||
Ok(Proof { c_scalar, s_scalar })
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Group, H: BlockInput + Digest> VerifiableClient<G, H> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
[
|
||||
G::scalar_as_bytes(self.blind).as_slice(),
|
||||
&self.blinded_element.to_arr(),
|
||||
&self.data,
|
||||
]
|
||||
.concat()
|
||||
///////////////////////////
|
||||
// Serde Support //
|
||||
// ===================== //
|
||||
///////////////////////////
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
pub(crate) mod serde {
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use hybrid_array::Array;
|
||||
use serde::de::{Deserializer, Error};
|
||||
use serde::ser::Serializer;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::Group;
|
||||
|
||||
pub(crate) struct Element<G: Group>(PhantomData<G>);
|
||||
|
||||
impl<'de, G: Group> Element<G> {
|
||||
pub(crate) fn deserialize<D>(deserializer: D) -> Result<G::Elem, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Array::<_, G::ElemLen>::deserialize(deserializer)
|
||||
.and_then(|bytes| G::deserialize_elem(&bytes).map_err(D::Error::custom))
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
|
||||
let scalar_len = <G as Group>::ScalarLen::USIZE;
|
||||
let elem_len = <G as Group>::ElemLen::USIZE;
|
||||
if input.len() < scalar_len + elem_len {
|
||||
return Err(InternalError::SizeError);
|
||||
}
|
||||
|
||||
let blind = G::from_scalar_slice(&input[..scalar_len])?;
|
||||
let blinded_element = G::from_element_slice(&input[scalar_len..scalar_len + elem_len])?;
|
||||
let data = input[scalar_len + elem_len..].to_vec();
|
||||
|
||||
Ok(Self {
|
||||
blind,
|
||||
blinded_element,
|
||||
data,
|
||||
hash: PhantomData,
|
||||
})
|
||||
pub(crate) fn serialize<S>(self_: &G::Elem, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
G::serialize_elem(*self_).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Group, H: BlockInput + Digest> NonVerifiableServer<G, H> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
G::scalar_as_bytes(self.sk).to_vec()
|
||||
pub(crate) struct Scalar<G: Group>(PhantomData<G>);
|
||||
|
||||
impl<'de, G: Group> Scalar<G> {
|
||||
pub(crate) fn deserialize<D>(deserializer: D) -> Result<G::Scalar, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Array::<_, G::ScalarLen>::deserialize(deserializer)
|
||||
.and_then(|bytes| G::deserialize_scalar(&bytes).map_err(D::Error::custom))
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
|
||||
let scalar_len = <G as Group>::ScalarLen::USIZE;
|
||||
if input.len() != scalar_len {
|
||||
return Err(InternalError::SizeError);
|
||||
pub(crate) fn serialize<S>(self_: &G::Scalar, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
G::serialize_scalar(*self_).serialize(serializer)
|
||||
}
|
||||
|
||||
let sk = G::from_scalar_slice(input)?;
|
||||
|
||||
Ok(Self {
|
||||
sk,
|
||||
hash: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Group, H: BlockInput + Digest> VerifiableServer<G, H> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
[G::scalar_as_bytes(self.sk).as_slice(), &self.pk.to_arr()].concat()
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use proptest::collection::vec;
|
||||
use proptest::prelude::*;
|
||||
|
||||
use crate::{
|
||||
BlindedElement, EvaluationElement, OprfClient, OprfServer, PoprfClient, PoprfServer, Proof,
|
||||
VoprfClient, VoprfServer,
|
||||
};
|
||||
|
||||
// Fuzz: no panics on arbitrary input
|
||||
macro_rules! test_deserialize {
|
||||
($item:ident, $bytes:ident) => {
|
||||
#[cfg(feature = "ristretto255")]
|
||||
{
|
||||
let _ = $item::<crate::Ristretto255>::deserialize(&$bytes[..]);
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
|
||||
let scalar_len = <G as Group>::ScalarLen::USIZE;
|
||||
let elem_len = <G as Group>::ElemLen::USIZE;
|
||||
if input.len() != scalar_len + elem_len {
|
||||
return Err(InternalError::SizeError);
|
||||
let _ = $item::<::p256::NistP256>::deserialize(&$bytes[..]);
|
||||
let _ = $item::<::p384::NistP384>::deserialize(&$bytes[..]);
|
||||
let _ = $item::<::p521::NistP521>::deserialize(&$bytes[..]);
|
||||
};
|
||||
}
|
||||
|
||||
let sk = G::from_scalar_slice(&input[..scalar_len])?;
|
||||
let pk = G::from_element_slice(&input[scalar_len..])?;
|
||||
// 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());
|
||||
}};
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
sk,
|
||||
pk,
|
||||
hash: PhantomData,
|
||||
})
|
||||
// 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)) {
|
||||
test_deserialize!(OprfClient, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_voprf_client(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(VoprfClient, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_poprf_client(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(PoprfClient, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_oprf_server(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(OprfServer, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_voprf_server(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(VoprfServer, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_poprf_server(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(PoprfServer, bytes);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_blinded_element(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(BlindedElement, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_evaluation_element(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(EvaluationElement, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_proof(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(Proof, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Group, H: BlockInput + Digest> Proof<G, H> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
[
|
||||
G::scalar_as_bytes(self.c_scalar),
|
||||
G::scalar_as_bytes(self.s_scalar),
|
||||
]
|
||||
.concat()
|
||||
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);
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
|
||||
let scalar_len = <G as Group>::ScalarLen::USIZE;
|
||||
if input.len() != scalar_len + scalar_len {
|
||||
return Err(InternalError::SizeError);
|
||||
}
|
||||
Ok(Proof {
|
||||
c_scalar: G::from_scalar_slice(&input[..scalar_len])?,
|
||||
s_scalar: G::from_scalar_slice(&input[scalar_len..])?,
|
||||
hash: PhantomData,
|
||||
})
|
||||
}
|
||||
#[test]
|
||||
fn roundtrip_oprf_server() {
|
||||
let server = OprfServer::<$cs>::new(&mut SysRng).expect("new");
|
||||
test_roundtrip!(OprfServer, $cs, server);
|
||||
}
|
||||
|
||||
impl<G: Group, H: BlockInput + Digest> BlindedElement<G, H> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
self.value.to_arr().to_vec()
|
||||
#[test]
|
||||
fn roundtrip_voprf_client() {
|
||||
let client = VoprfClient::<$cs>::blind(b"input", &mut SysRng)
|
||||
.expect("blind")
|
||||
.state;
|
||||
test_roundtrip!(VoprfClient, $cs, client);
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
|
||||
let elem_len = <G as Group>::ElemLen::USIZE;
|
||||
if input.len() != elem_len {
|
||||
return Err(InternalError::SizeError);
|
||||
}
|
||||
Ok(Self {
|
||||
value: G::from_element_slice(input)?,
|
||||
hash: PhantomData,
|
||||
})
|
||||
}
|
||||
#[test]
|
||||
fn roundtrip_voprf_server() {
|
||||
let server = VoprfServer::<$cs>::new(&mut SysRng).expect("new");
|
||||
test_roundtrip!(VoprfServer, $cs, server);
|
||||
}
|
||||
|
||||
impl<G: Group, H: BlockInput + Digest> EvaluationElement<G, H> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
self.value.to_arr().to_vec()
|
||||
#[test]
|
||||
fn roundtrip_poprf_client() {
|
||||
let client = PoprfClient::<$cs>::blind(b"input", &mut SysRng)
|
||||
.expect("blind")
|
||||
.state;
|
||||
test_roundtrip!(PoprfClient, $cs, client);
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
|
||||
let elem_len = <G as Group>::ElemLen::USIZE;
|
||||
if input.len() != elem_len {
|
||||
return Err(InternalError::SizeError);
|
||||
#[test]
|
||||
fn roundtrip_poprf_server() {
|
||||
let server = PoprfServer::<$cs>::new(&mut SysRng).expect("new");
|
||||
test_roundtrip!(PoprfServer, $cs, server);
|
||||
}
|
||||
Ok(Self {
|
||||
value: G::from_element_slice(input)?,
|
||||
hash: PhantomData,
|
||||
})
|
||||
|
||||
#[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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,918 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
// Copyright (c) VexaHub and contributors.
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
//! The VOPRF test vectors taken from:
|
||||
//! https://www.rfc-editor.org/rfc/rfc9497#appendix-A
|
||||
|
||||
pub(crate) const VECTORS: &str = r#"
|
||||
A.1. ristretto255-SHA512
|
||||
|
||||
A.1.1. OPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 5ebcea5ee37023ccb9fc2d2019f9d7737be85591ae8652ffa9ef0f4d37063
|
||||
b0e
|
||||
|
||||
A.1.1.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
|
||||
6706
|
||||
BlindedElement = 609a0ae68c15a3cf6903766461307e5c8bb2f95e7e6550e1ffa
|
||||
2dc99e412803c
|
||||
EvaluationElement = 7ec6578ae5120958eb2db1745758ff379e77cb64fe77b0b2
|
||||
d8cc917ea0869c7e
|
||||
Output = 527759c3d9366f277d8c6020418d96bb393ba2afb20ff90df23fb770826
|
||||
4e2f3ab9135e3bd69955851de4b1f9fe8a0973396719b7912ba9ee8aa7d0b5e24bcf
|
||||
6
|
||||
|
||||
A.1.1.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
|
||||
6706
|
||||
BlindedElement = da27ef466870f5f15296299850aa088629945a17d1f5b7f5ff0
|
||||
43f76b3c06418
|
||||
EvaluationElement = b4cbf5a4f1eeda5a63ce7b77c7d23f461db3fcab0dd28e4e
|
||||
17cecb5c90d02c25
|
||||
Output = f4a74c9c592497375e796aa837e907b1a045d34306a749db9f34221f7e7
|
||||
50cb4f2a6413a6bf6fa5e19ba6348eb673934a722a7ede2e7621306d18951e7cf2c7
|
||||
3
|
||||
|
||||
A.1.2. VOPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = e6f73f344b79b379f1a0dd37e07ff62e38d9f71345ce62ae3a9bc60b04ccd
|
||||
909
|
||||
pkSm = c803e2cc6b05fc15064549b5920659ca4a77b2cca6f04f6b357009335476a
|
||||
d4e
|
||||
|
||||
A.1.2.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
|
||||
6706
|
||||
BlindedElement = 863f330cc1a1259ed5a5998a23acfd37fb4351a793a5b3c090b
|
||||
642ddc439b945
|
||||
EvaluationElement = aa8fa048764d5623868679402ff6108d2521884fa138cd7f
|
||||
9c7669a9a014267e
|
||||
Proof = ddef93772692e535d1a53903db24367355cc2cc78de93b3be5a8ffcc6985
|
||||
dd066d4346421d17bf5117a2a1ff0fcb2a759f58a539dfbe857a40bce4cf49ec600d
|
||||
ProofRandomScalar = 222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d98
|
||||
81aa6f61d645fc0e
|
||||
Output = b58cfbe118e0cb94d79b5fd6a6dafb98764dff49c14e1770b566e42402d
|
||||
a1a7da4d8527693914139caee5bd03903af43a491351d23b430948dd50cde10d32b3
|
||||
c
|
||||
|
||||
A.1.2.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
|
||||
6706
|
||||
BlindedElement = cc0b2a350101881d8a4cba4c80241d74fb7dcbfde4a61fde2f9
|
||||
1443c2bf9ef0c
|
||||
EvaluationElement = 60a59a57208d48aca71e9e850d22674b611f752bed48b36f
|
||||
7a91b372bd7ad468
|
||||
Proof = 401a0da6264f8cf45bb2f5264bc31e109155600babb3cd4e5af7d181a2c9
|
||||
dc0a67154fabf031fd936051dec80b0b6ae29c9503493dde7393b722eafdf5a50b02
|
||||
ProofRandomScalar = 222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d98
|
||||
81aa6f61d645fc0e
|
||||
Output = 8a9a2f3c7f085b65933594309041fc1898d42d0858e59f90814ae90571a
|
||||
6df60356f4610bf816f27afdd84f47719e480906d27ecd994985890e5f539e7ea74b
|
||||
6
|
||||
|
||||
A.1.2.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
|
||||
6706,222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0
|
||||
e
|
||||
BlindedElement = 863f330cc1a1259ed5a5998a23acfd37fb4351a793a5b3c090b
|
||||
642ddc439b945,90a0145ea9da29254c3a56be4fe185465ebb3bf2a1801f7124bbba
|
||||
dac751e654
|
||||
EvaluationElement = aa8fa048764d5623868679402ff6108d2521884fa138cd7f
|
||||
9c7669a9a014267e,cc5ac221950a49ceaa73c8db41b82c20372a4c8d63e5dded2db
|
||||
920b7eee36a2a
|
||||
Proof = cc203910175d786927eeb44ea847328047892ddf8590e723c37205cb7460
|
||||
0b0a5ab5337c8eb4ceae0494c2cf89529dcf94572ed267473d567aeed6ab873dee08
|
||||
ProofRandomScalar = 419c4f4f5052c53c45f3da494d2b67b220d02118e0857cdb
|
||||
cf037f9ea84bbe0c
|
||||
Output = b58cfbe118e0cb94d79b5fd6a6dafb98764dff49c14e1770b566e42402d
|
||||
a1a7da4d8527693914139caee5bd03903af43a491351d23b430948dd50cde10d32b3
|
||||
c,8a9a2f3c7f085b65933594309041fc1898d42d0858e59f90814ae90571a6df6035
|
||||
6f4610bf816f27afdd84f47719e480906d27ecd994985890e5f539e7ea74b6
|
||||
|
||||
A.1.3. POPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 145c79c108538421ac164ecbe131942136d5570b16d8bf41a24d4337da981
|
||||
e07
|
||||
pkSm = c647bef38497bc6ec077c22af65b696efa43bff3b4a1975a3e8e0a1c5a79d
|
||||
631
|
||||
|
||||
A.1.3.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
|
||||
6706
|
||||
BlindedElement = c8713aa89241d6989ac142f22dba30596db635c772cbf25021f
|
||||
dd8f3d461f715
|
||||
EvaluationElement = 1a4b860d808ff19624731e67b5eff20ceb2df3c3c03b906f
|
||||
5693e2078450d874
|
||||
Proof = 41ad1a291aa02c80b0915fbfbb0c0afa15a57e2970067a602ddb9e8fd6b7
|
||||
100de32e1ecff943a36f0b10e3dae6bd266cdeb8adf825d86ef27dbc6c0e30c52206
|
||||
ProofRandomScalar = 222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d98
|
||||
81aa6f61d645fc0e
|
||||
Output = ca688351e88afb1d841fde4401c79efebb2eb75e7998fa9737bd5a82a15
|
||||
2406d38bd29f680504e54fd4587eddcf2f37a2617ac2fbd2993f7bdf45442ace7d22
|
||||
1
|
||||
|
||||
A.1.3.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
|
||||
6706
|
||||
BlindedElement = f0f0b209dd4d5f1844dac679acc7761b91a2e704879656cb7c2
|
||||
01e82a99ab07d
|
||||
EvaluationElement = 8c3c9d064c334c6991e99f286ea2301d1bde170b54003fb9
|
||||
c44c6d7bd6fc1540
|
||||
Proof = 4c39992d55ffba38232cdac88fe583af8a85441fefd7d1d4a8d0394cd1de
|
||||
77018bf135c174f20281b3341ab1f453fe72b0293a7398703384bed822bfdeec8908
|
||||
ProofRandomScalar = 222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d98
|
||||
81aa6f61d645fc0e
|
||||
Output = 7c6557b276a137922a0bcfc2aa2b35dd78322bd500235eb6d6b6f91bc5b
|
||||
56a52de2d65612d503236b321f5d0bebcbc52b64b92e426f29c9b8b69f52de98ae50
|
||||
7
|
||||
|
||||
A.1.3.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
|
||||
6706,222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0
|
||||
e
|
||||
BlindedElement = c8713aa89241d6989ac142f22dba30596db635c772cbf25021f
|
||||
dd8f3d461f715,423a01c072e06eb1cce96d23acce06e1ea64a609d7ec9e9023f304
|
||||
9f2d64e50c
|
||||
EvaluationElement = 1a4b860d808ff19624731e67b5eff20ceb2df3c3c03b906f
|
||||
5693e2078450d874,aa1f16e903841036e38075da8a46655c94fc92341887eb5819f
|
||||
46312adfc0504
|
||||
Proof = 43fdb53be399cbd3561186ae480320caa2b9f36cca0e5b160c4a677b8bbf
|
||||
4301b28f12c36aa8e11e5a7ef551da0781e863a6dc8c0b2bf5a149c9e00621f02006
|
||||
ProofRandomScalar = 419c4f4f5052c53c45f3da494d2b67b220d02118e0857cdb
|
||||
cf037f9ea84bbe0c
|
||||
Output = ca688351e88afb1d841fde4401c79efebb2eb75e7998fa9737bd5a82a15
|
||||
2406d38bd29f680504e54fd4587eddcf2f37a2617ac2fbd2993f7bdf45442ace7d22
|
||||
1,7c6557b276a137922a0bcfc2aa2b35dd78322bd500235eb6d6b6f91bc5b56a52de
|
||||
2d65612d503236b321f5d0bebcbc52b64b92e426f29c9b8b69f52de98ae507
|
||||
|
||||
A.2. decaf448-SHAKE256
|
||||
|
||||
A.2.1. OPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = e8b1375371fd11ebeb224f832dcc16d371b4188951c438f751425699ed29e
|
||||
cc80c6c13e558ccd67634fd82eac94aa8d1f0d7fee990695d1e
|
||||
|
||||
A.2.1.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
|
||||
3833a26e9388336361686ff1f83df55046504dfecad8549ba112
|
||||
BlindedElement = e0ae01c4095f08e03b19baf47ffdc19cb7d98e583160522a3c7
|
||||
d6a0b2111cd93a126a46b7b41b730cd7fc943d4e28e590ed33ae475885f6c
|
||||
EvaluationElement = 50ce4e60eed006e22e7027454b5a4b8319eb2bc8ced609eb
|
||||
19eb3ad42fb19e06ba12d382cbe7ae342a0cad6ead0ef8f91f00bb7f0cd9c0a2
|
||||
Output = 37d3f7922d9388a15b561de5829bbf654c4089ede89c0ce0f3f85bcdba0
|
||||
9e382ce0ab3507e021f9e79706a1798ffeac68ebd5cf62e5eb9838c7068351d97ae3
|
||||
7
|
||||
|
||||
A.2.1.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
|
||||
3833a26e9388336361686ff1f83df55046504dfecad8549ba112
|
||||
BlindedElement = 86a88dc5c6331ecfcb1d9aacb50a68213803c462e377577cacc
|
||||
00af28e15f0ddbc2e3d716f2f39ef95f3ec1314a2c64d940a9f295d8f13bb
|
||||
EvaluationElement = 162e9fa6e9d527c3cd734a31bf122a34dbd5bcb7bb23651f
|
||||
1768a7a9274cc116c03b58afa6f0dede3994a60066c76370e7328e7062fd5819
|
||||
Output = a2a652290055cb0f6f8637a249ee45e32ef4667db0b4c80c0a70d2a6416
|
||||
4d01525cfdad5d870a694ec77972b9b6ec5d2596a5223e5336913f945101f0137f55
|
||||
e
|
||||
|
||||
A.2.2. VOPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = e3c01519a076a326a0eb566343e9b21c115fa18e6e85577ddbe890b33104f
|
||||
cc2835ddfb14a928dc3f5d79b936e17c76b99e0bf6a1680930e
|
||||
pkSm = 945fc518c47695cf65217ace04b86ac5e4cbe26ca649d52854bb16c494ce0
|
||||
9069d6add96b20d4b0ae311a87c9a73e3a146b525763ab2f955
|
||||
|
||||
A.2.2.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
|
||||
3833a26e9388336361686ff1f83df55046504dfecad8549ba112
|
||||
BlindedElement = 7261bbc335c664ba788f1b1a1a4cd5190cc30e787ef277665ac
|
||||
1d314f8861e3ec11854ce3ddd42035d9e0f5cddde324c332d8c880abc00eb
|
||||
EvaluationElement = ca1491a526c28d880806cf0fb0122222392cf495657be6e4
|
||||
c9d203bceffa46c86406caf8217859d3fb259077af68e5d41b3699410781f467
|
||||
Proof = f84bbeee47aedf43558dae4b95b3853635a9fc1a9ea7eac9b454c64c66c4
|
||||
f49cd1c72711c7ac2e06c681e16ea693d5500bbd7b56455df52f69e00b76b4126961
|
||||
e1562fdbaaac40b7701065cbeece3febbfe09e00160f81775d36daed99d8a2a10be0
|
||||
759e01b7ee81217203416c9db208
|
||||
ProofRandomScalar = b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0
|
||||
627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b
|
||||
Output = e2ac40b634f36cccd8262b285adff7c9dcc19cd308564a5f4e581d1a853
|
||||
5773b86fa4fc9f2203c370763695c5093aea4a7aedec4488b1340ba3bf663a23098c
|
||||
1
|
||||
|
||||
A.2.2.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
|
||||
3833a26e9388336361686ff1f83df55046504dfecad8549ba112
|
||||
BlindedElement = 88287e553939090b888ddc15913e1807dc4757215555e1c3a79
|
||||
488ef311594729c7fa74c772a732b78440b7d66d0aa35f3bb316f1d93e1b2
|
||||
EvaluationElement = c00978c73e8e4ee1d447ab0d3ad1754055e72cc85c08e3a0
|
||||
db170909a9c61cbff1f1e7015f289e3038b0f341faea5d7780c130106065c231
|
||||
Proof = 7a2831a6b237e11ac1657d440df93bc5ce00f552e6020a99d5c956ffc4d0
|
||||
7b5ade3e82ecdc257fd53d76239e733e0a1313e84ce16cc0d82734806092a693d7e8
|
||||
d3c420c2cb6ccd5d0ca32514fb78e9ad0973ebdcb52eba438fc73948d76339ee7101
|
||||
21d83e2fe6f001cfdf551aff9f36
|
||||
ProofRandomScalar = b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0
|
||||
627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b
|
||||
Output = 862952380e07ec840d9f6e6f909c5a25d16c3dacb586d89a181b4aa7380
|
||||
c959baa8c480fe8e6c64e089d68ea7aeeb5817bd524d7577905b5bab487690048c94
|
||||
1
|
||||
|
||||
A.2.2.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
|
||||
3833a26e9388336361686ff1f83df55046504dfecad8549ba112,b1b748135d405ce
|
||||
48c6973401d9455bb8ccd18b01d0295c0627f67661200dbf9569f73fbb3925daa043
|
||||
a070e5f953d80bb464ea369e5522b
|
||||
BlindedElement = 7261bbc335c664ba788f1b1a1a4cd5190cc30e787ef277665ac
|
||||
1d314f8861e3ec11854ce3ddd42035d9e0f5cddde324c332d8c880abc00eb,2e15f3
|
||||
93c035492a1573627a3606e528c6294c767c8d43b8c691ef70a52cc7dc7d1b53fe45
|
||||
8350a270abb7c231b87ba58266f89164f714d9
|
||||
EvaluationElement = ca1491a526c28d880806cf0fb0122222392cf495657be6e4
|
||||
c9d203bceffa46c86406caf8217859d3fb259077af68e5d41b3699410781f467,8ec
|
||||
68e9871b296e81c55647ce64a04fe75d19932f1400544cd601468c60f998408bbb54
|
||||
6601d4a636e8be279e558d70b95c8d4a4f61892be
|
||||
Proof = 167d922f0a6ffa845eed07f8aa97b6ac746d902ecbeb18f49c009adc0521
|
||||
eab1e4d275b74a2dc266b7a194c854e85e7eb54a9a36376dfc04ec7f3bd55fc9618c
|
||||
3970cb548e064f8a2f06183a5702933dbc3e4c25a73438f2108ee1981c306181003c
|
||||
7ea92fce963ec7b4ba4f270e6d38
|
||||
ProofRandomScalar = 63798726803c9451ba405f00ef3acb633ddf0c420574a2ec
|
||||
6cbf28f840800e355c9fbaac10699686de2724ed22e797a00f3bd93d105a7f23
|
||||
Output = e2ac40b634f36cccd8262b285adff7c9dcc19cd308564a5f4e581d1a853
|
||||
5773b86fa4fc9f2203c370763695c5093aea4a7aedec4488b1340ba3bf663a23098c
|
||||
1,862952380e07ec840d9f6e6f909c5a25d16c3dacb586d89a181b4aa7380c959baa
|
||||
8c480fe8e6c64e089d68ea7aeeb5817bd524d7577905b5bab487690048c941
|
||||
|
||||
A.2.3. POPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 792a10dcbd3ba4a52a054f6f39186623208695301e7adb9634b74709ab22d
|
||||
e402990eb143fd7c67ac66be75e0609705ecea800992aac8e19
|
||||
pkSm = 6c9d12723a5bbcf305522cc04b4a34d9ced2e12831826018ea7b5dcf54526
|
||||
47ad262113059bf0f6e4354319951b9d513c74f29cb0eec38c1
|
||||
|
||||
A.2.3.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
|
||||
3833a26e9388336361686ff1f83df55046504dfecad8549ba112
|
||||
BlindedElement = 161183c13c6cb33b0e4f9b7365f8c5c12d13c72f8b62d276ca0
|
||||
9368d093dce9b42198276b9e9d870ac392dda53efd28d1b7e6e8c060cdc42
|
||||
EvaluationElement = 06ec89dfde25bb2a6f0145ac84b91ac277b35de39ad1d6f4
|
||||
02a8e46414952ce0d9ea1311a4ece283e2b01558c7078b040cfaa40dd63b3e6c
|
||||
Proof = 66caee75bf2460429f620f6ad3e811d524cb8ddd848a435fc5d89af48877
|
||||
abf6506ee341a0b6f67c2d76cd021e5f3d1c9abe5aa9f0dce016da746135fedba2af
|
||||
41ed1d01659bfd6180d96bc1b7f320c0cb6926011ce392ecca748662564892bae665
|
||||
16acaac6ca39aadf6fcca95af406
|
||||
ProofRandomScalar = b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0
|
||||
627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b
|
||||
Output = 4423f6dcc1740688ea201de57d76824d59cd6b859e1f9884b7eebc49b0b
|
||||
971358cf9cb075df1536a8ea31bcf55c3e31c2ba9cfa8efe54448d17091daeb9924e
|
||||
d
|
||||
|
||||
A.2.3.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
|
||||
3833a26e9388336361686ff1f83df55046504dfecad8549ba112
|
||||
BlindedElement = 12082b6a381c6c51e85d00f2a3d828cdeab3f5cb19a10b9c014
|
||||
c33826764ab7e7cfb8b4ff6f411bddb2d64e62a472af1cd816e5b712790c6
|
||||
EvaluationElement = f2919b7eedc05ab807c221fce2b12c4ae9e19e6909c47845
|
||||
64b690d1972d2994ca623f273afc67444d84ea40cbc58fcdab7945f321a52848
|
||||
Proof = a295677c54d1bc4286330907fc2490a7de163da26f9ce03a462a452fea42
|
||||
2b19ade296ba031359b3b6841e48455d20519ad01b4ac4f0b92e76d3cf16fbef0a3f
|
||||
72791a8401ef2d7081d361e502e96b2c60608b9fa566f43d4611c2f161d83aabef7f
|
||||
8017332b26ed1daaf80440772022
|
||||
ProofRandomScalar = b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0
|
||||
627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b
|
||||
Output = 8691905500510843902c44bdd9730ab9dc3925aa58ff9dd42765a2baf63
|
||||
3126de0c3adb93bef5652f38e5827b6396e87643960163a560fc4ac9738c8de4e4a8
|
||||
d
|
||||
|
||||
A.2.3.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
|
||||
3833a26e9388336361686ff1f83df55046504dfecad8549ba112,b1b748135d405ce
|
||||
48c6973401d9455bb8ccd18b01d0295c0627f67661200dbf9569f73fbb3925daa043
|
||||
a070e5f953d80bb464ea369e5522b
|
||||
BlindedElement = 161183c13c6cb33b0e4f9b7365f8c5c12d13c72f8b62d276ca0
|
||||
9368d093dce9b42198276b9e9d870ac392dda53efd28d1b7e6e8c060cdc42,fc8847
|
||||
d43fb4cea4e408f585661a8f2867533fa91d22155d3127a22f18d3b007add480f7d3
|
||||
00bca93fa47fe87ae06a57b7d0f0d4c30b12f0
|
||||
EvaluationElement = 06ec89dfde25bb2a6f0145ac84b91ac277b35de39ad1d6f4
|
||||
02a8e46414952ce0d9ea1311a4ece283e2b01558c7078b040cfaa40dd63b3e6c,2e7
|
||||
4c626d07de49b1c8c21d87120fd78105f485e36816af9bde3e3efbeef76815326062
|
||||
fd333925b66c5ce5a20f100bf01770c16609f990a
|
||||
Proof = fd94db736f97ea4efe9d0d4ad2933072697a6bbeb32834057b23edf7c700
|
||||
9f011dfa72157f05d2a507c2bbf0b54cad99ab99de05921c021fda7d70e65bcecdb0
|
||||
5f9a30154127ace983c74d10fd910b554c5e95f6bd1565fd1f3dbbe3c523ece5c72d
|
||||
57a559b7be1368c4786db4a3c910
|
||||
ProofRandomScalar = 63798726803c9451ba405f00ef3acb633ddf0c420574a2ec
|
||||
6cbf28f840800e355c9fbaac10699686de2724ed22e797a00f3bd93d105a7f23
|
||||
Output = 4423f6dcc1740688ea201de57d76824d59cd6b859e1f9884b7eebc49b0b
|
||||
971358cf9cb075df1536a8ea31bcf55c3e31c2ba9cfa8efe54448d17091daeb9924e
|
||||
d,8691905500510843902c44bdd9730ab9dc3925aa58ff9dd42765a2baf633126de0
|
||||
c3adb93bef5652f38e5827b6396e87643960163a560fc4ac9738c8de4e4a8d
|
||||
|
||||
A.3. P256-SHA256
|
||||
|
||||
A.3.1. OPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 159749d750713afe245d2d39ccfaae8381c53ce92d098a9375ee70739c7ac
|
||||
0bf
|
||||
|
||||
A.3.1.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 03723a1e5c09b8b9c18d1dcbca29e8007e95f14f4732d9346d4
|
||||
90ffc195110368d
|
||||
EvaluationElement = 030de02ffec47a1fd53efcdd1c6faf5bdc270912b8749e78
|
||||
3c7ca75bb412958832
|
||||
Output = a0b34de5fa4c5b6da07e72af73cc507cceeb48981b97b7285fc375345fe
|
||||
495dd
|
||||
|
||||
A.3.1.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 03cc1df781f1c2240a64d1c297b3f3d16262ef5d4cf10273488
|
||||
2675c26231b0838
|
||||
EvaluationElement = 03a0395fe3828f2476ffcd1f4fe540e5a8489322d398be3c
|
||||
4e5a869db7fcb7c52c
|
||||
Output = c748ca6dd327f0ce85f4ae3a8cd6d4d5390bbb804c9e12dcf94f853fece
|
||||
3dcce
|
||||
|
||||
A.3.2. VOPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = ca5d94c8807817669a51b196c34c1b7f8442fde4334a7121ae4736364312f
|
||||
ca6
|
||||
pkSm = 03e17e70604bcabe198882c0a1f27a92441e774224ed9c702e51dd17038b1
|
||||
02462
|
||||
|
||||
A.3.2.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 02dd05901038bb31a6fae01828fd8d0e49e35a486b5c5d4b499
|
||||
4013648c01277da
|
||||
EvaluationElement = 0209f33cab60cf8fe69239b0afbcfcd261af4c1c5632624f
|
||||
2e9ba29b90ae83e4a2
|
||||
Proof = e7c2b3c5c954c035949f1f74e6bce2ed539a3be267d1481e9ddb178533df
|
||||
4c2664f69d065c604a4fd953e100b856ad83804eb3845189babfa5a702090d6fc5fa
|
||||
ProofRandomScalar = f9db001266677f62c095021db018cd8cbb55941d4073698c
|
||||
e45c405d1348b7b1
|
||||
Output = 0412e8f78b02c415ab3a288e228978376f99927767ff37c5718d420010a
|
||||
645a1
|
||||
|
||||
A.3.2.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 03cd0f033e791c4d79dfa9c6ed750f2ac009ec46cd4195ca6fd
|
||||
3800d1e9b887dbd
|
||||
EvaluationElement = 030d2985865c693bf7af47ba4d3a3813176576383d19aff0
|
||||
03ef7b0784a0d83cf1
|
||||
Proof = 2787d729c57e3d9512d3aa9e8708ad226bc48e0f1750b0767aaff73482c4
|
||||
4b8d2873d74ec88aebd3504961acea16790a05c542d9fbff4fe269a77510db00abab
|
||||
ProofRandomScalar = f9db001266677f62c095021db018cd8cbb55941d4073698c
|
||||
e45c405d1348b7b1
|
||||
Output = 771e10dcd6bcd3664e23b8f2a710cfaaa8357747c4a8cbba03133967b5c
|
||||
24f18
|
||||
|
||||
A.3.2.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364,f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b
|
||||
1
|
||||
BlindedElement = 02dd05901038bb31a6fae01828fd8d0e49e35a486b5c5d4b499
|
||||
4013648c01277da,03462e9ae64cae5b83ba98a6b360d942266389ac369b923eb3d5
|
||||
57213b1922f8ab
|
||||
EvaluationElement = 0209f33cab60cf8fe69239b0afbcfcd261af4c1c5632624f
|
||||
2e9ba29b90ae83e4a2,02bb24f4d838414aef052a8f044a6771230ca69c0a5677540
|
||||
fff738dd31bb69771
|
||||
Proof = bdcc351707d02a72ce49511c7db990566d29d6153ad6f8982fad2b435d6c
|
||||
e4d60da1e6b3fa740811bde34dd4fe0aa1b5fe6600d0440c9ddee95ea7fad7a60cf2
|
||||
ProofRandomScalar = 350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba
|
||||
51943c8026877963
|
||||
Output = 0412e8f78b02c415ab3a288e228978376f99927767ff37c5718d420010a
|
||||
645a1,771e10dcd6bcd3664e23b8f2a710cfaaa8357747c4a8cbba03133967b5c24f
|
||||
18
|
||||
|
||||
A.3.3. POPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 6ad2173efa689ef2c27772566ad7ff6e2d59b3b196f00219451fb2c89ee4d
|
||||
ae2
|
||||
pkSm = 030d7ff077fddeec965db14b794f0cc1ba9019b04a2f4fcc1fa525dedf72e
|
||||
2a3e3
|
||||
|
||||
A.3.3.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 031563e127099a8f61ed51eeede05d747a8da2be329b40ba1f0
|
||||
db0b2bd9dd4e2c0
|
||||
EvaluationElement = 02c5e5300c2d9e6ba7f3f4ad60500ad93a0157e6288eb04b
|
||||
67e125db024a2c74d2
|
||||
Proof = f8a33690b87736c854eadfcaab58a59b8d9c03b569110b6f31f8bf7577f3
|
||||
fbb85a8a0c38468ccde1ba942be501654adb106167c8eb178703ccb42bccffb9231a
|
||||
ProofRandomScalar = f9db001266677f62c095021db018cd8cbb55941d4073698c
|
||||
e45c405d1348b7b1
|
||||
Output = 193a92520bd8fd1f37accb918040a57108daa110dc4f659abe212636d24
|
||||
5c592
|
||||
|
||||
A.3.3.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 021a440ace8ca667f261c10ac7686adc66a12be31e3520fca31
|
||||
7643a1eee9dcd4d
|
||||
EvaluationElement = 0208ca109cbae44f4774fc0bdd2783efdcb868cb4523d521
|
||||
96f700210e777c5de3
|
||||
Proof = 043a8fb7fc7fd31e35770cabda4753c5bf0ecc1e88c68d7d35a62bf2631e
|
||||
875af4613641be2d1875c31d1319d191c4bbc0d04875f4fd03c31d3d17dd8e069b69
|
||||
ProofRandomScalar = f9db001266677f62c095021db018cd8cbb55941d4073698c
|
||||
e45c405d1348b7b1
|
||||
Output = 1e6d164cfd835d88a31401623549bf6b9b306628ef03a7962921d62bc5f
|
||||
fce8c
|
||||
|
||||
A.3.3.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364,f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b
|
||||
1
|
||||
BlindedElement = 031563e127099a8f61ed51eeede05d747a8da2be329b40ba1f0
|
||||
db0b2bd9dd4e2c0,03ca4ff41c12fadd7a0bc92cf856732b21df652e01a3abdf0fa8
|
||||
847da053db213c
|
||||
EvaluationElement = 02c5e5300c2d9e6ba7f3f4ad60500ad93a0157e6288eb04b
|
||||
67e125db024a2c74d2,02f0b6bcd467343a8d8555a99dc2eed0215c71898c5edb77a
|
||||
3d97ddd0dbad478e8
|
||||
Proof = 8fbd85a32c13aba79db4b42e762c00687d6dbf9c8cb97b2a225645ccb00d
|
||||
9d7580b383c885cdfd07df448d55e06f50f6173405eee5506c0ed0851ff718d13e68
|
||||
ProofRandomScalar = 350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba
|
||||
51943c8026877963
|
||||
Output = 193a92520bd8fd1f37accb918040a57108daa110dc4f659abe212636d24
|
||||
5c592,1e6d164cfd835d88a31401623549bf6b9b306628ef03a7962921d62bc5ffce
|
||||
8c
|
||||
|
||||
A.4. P384-SHA384
|
||||
|
||||
A.4.1. OPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = dfe7ddc41a4646901184f2b432616c8ba6d452f9bcd0c4f75a5150ef2b2ed
|
||||
02ef40b8b92f60ae591bcabd72a6518f188
|
||||
|
||||
A.4.1.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
|
||||
889d89dbfa691d1cde91517fa222ed7ad364
|
||||
BlindedElement = 02a36bc90e6db34096346eaf8b7bc40ee1113582155ad379700
|
||||
3ce614c835a874343701d3f2debbd80d97cbe45de6e5f1f
|
||||
EvaluationElement = 03af2a4fc94770d7a7bf3187ca9cc4faf3732049eded2442
|
||||
ee50fbddda58b70ae2999366f72498cdbc43e6f2fc184afe30
|
||||
Output = ed84ad3f31a552f0456e58935fcc0a3039db42e7f356dcb32aa6d487b6b
|
||||
815a07d5813641fb1398c03ddab5763874357
|
||||
|
||||
A.4.1.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
|
||||
889d89dbfa691d1cde91517fa222ed7ad364
|
||||
BlindedElement = 02def6f418e3484f67a124a2ce1bfb19de7a4af568ede6a1ebb
|
||||
2733882510ddd43d05f2b1ab5187936a55e50a847a8b900
|
||||
EvaluationElement = 034e9b9a2960b536f2ef47d8608b21597ba400d5abfa1825
|
||||
fd21c36b75f927f396bf3716c96129d1fa4a77fa1d479c8d7b
|
||||
Output = dd4f29da869ab9355d60617b60da0991e22aaab243a3460601e48b07585
|
||||
9d1c526d36597326f1b985778f781a1682e75
|
||||
|
||||
A.4.2. VOPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 051646b9e6e7a71ae27c1e1d0b87b4381db6d3595eeeb1adb41579adbf992
|
||||
f4278f9016eafc944edaa2b43183581779d
|
||||
pkSm = 031d689686c611991b55f1a1d8f4305ccd6cb719446f660a30db61b7aa87b
|
||||
46acf59b7c0d4a9077b3da21c25dd482229a0
|
||||
|
||||
A.4.2.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
|
||||
889d89dbfa691d1cde91517fa222ed7ad364
|
||||
BlindedElement = 02d338c05cbecb82de13d6700f09cb61190543a7b7e2c6cd4fc
|
||||
a56887e564ea82653b27fdad383995ea6d02cf26d0e24d9
|
||||
EvaluationElement = 02a7bba589b3e8672aa19e8fd258de2e6aae20101c8d7612
|
||||
46de97a6b5ee9cf105febce4327a326255a3c604f63f600ef6
|
||||
Proof = bfc6cf3859127f5fe25548859856d6b7fa1c7459f0ba5712a806fc091a30
|
||||
00c42d8ba34ff45f32a52e40533efd2a03bc87f3bf4f9f58028297ccb9ccb18ae718
|
||||
2bcd1ef239df77e3be65ef147f3acf8bc9cbfc5524b702263414f043e3b7ca2e
|
||||
ProofRandomScalar = 803d955f0e073a04aa5d92b3fb739f56f9db001266677f62
|
||||
c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
|
||||
Output = 3333230886b562ffb8329a8be08fea8025755372817ec969d114d1203d0
|
||||
26b4a622beab60220bf19078bca35a529b35c
|
||||
|
||||
A.4.2.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
|
||||
889d89dbfa691d1cde91517fa222ed7ad364
|
||||
BlindedElement = 02f27469e059886f221be5f2cca03d2bdc61e55221721c3b3e5
|
||||
6fc012e36d31ae5f8dc058109591556a6dbd3a8c69c433b
|
||||
EvaluationElement = 03f16f903947035400e96b7f531a38d4a07ac89a80f89d86
|
||||
a1bf089c525a92c7f4733729ca30c56ce78b1ab4f7d92db8b4
|
||||
Proof = d005d6daaad7571414c1e0c75f7e57f2113ca9f4604e84bc90f9be52da89
|
||||
6fff3bee496dcde2a578ae9df315032585f801fb21c6080ac05672b291e575a40295
|
||||
b306d967717b28e08fcc8ad1cab47845d16af73b3e643ddcc191208e71c64630
|
||||
ProofRandomScalar = 803d955f0e073a04aa5d92b3fb739f56f9db001266677f62
|
||||
c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
|
||||
Output = b91c70ea3d4d62ba922eb8a7d03809a441e1c3c7af915cbc2226f485213
|
||||
e895942cd0f8580e6d99f82221e66c40d274f
|
||||
|
||||
A.4.2.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
|
||||
889d89dbfa691d1cde91517fa222ed7ad364,803d955f0e073a04aa5d92b3fb739f5
|
||||
6f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
|
||||
BlindedElement = 02d338c05cbecb82de13d6700f09cb61190543a7b7e2c6cd4fc
|
||||
a56887e564ea82653b27fdad383995ea6d02cf26d0e24d9,02fa02470d7f151018b4
|
||||
1e82223c32fad824de6ad4b5ce9f8e9f98083c9a726de9a1fc39d7a0cb6f4f188dd9
|
||||
cea01474cd
|
||||
EvaluationElement = 02a7bba589b3e8672aa19e8fd258de2e6aae20101c8d7612
|
||||
46de97a6b5ee9cf105febce4327a326255a3c604f63f600ef6,028e9e115625ff4c2
|
||||
f07bf87ce3fd73fc77994a7a0c1df03d2a630a3d845930e2e63a165b114d98fe34e6
|
||||
1b68d23c0b50a
|
||||
Proof = 6d8dcbd2fc95550a02211fb78afd013933f307d21e7d855b0b1ed0af7807
|
||||
6d8137ad8b0a1bfa05676d325249c1dbb9a52bd81b1c2b7b0efc77cf7b278e1c947f
|
||||
6283f1d4c513053fc0ad19e026fb0c30654b53d9cea4b87b037271b5d2e2d0ea
|
||||
ProofRandomScalar = a097e722ed2427de86966910acba9f5c350e8040f828bf6c
|
||||
eca27405420cdf3d63cb3aef005f40ba51943c8026877963
|
||||
Output = 3333230886b562ffb8329a8be08fea8025755372817ec969d114d1203d0
|
||||
26b4a622beab60220bf19078bca35a529b35c,b91c70ea3d4d62ba922eb8a7d03809
|
||||
a441e1c3c7af915cbc2226f485213e895942cd0f8580e6d99f82221e66c40d274f
|
||||
|
||||
A.4.3. POPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 5b2690d6954b8fbb159f19935d64133f12770c00b68422559c65431942d72
|
||||
1ff79d47d7a75906c30b7818ec0f38b7fb2
|
||||
pkSm = 02f00f0f1de81e5d6cf18140d4926ffdc9b1898c48dc49657ae36eb1e45de
|
||||
b8b951aaf1f10c82d2eaa6d02aafa3f10d2b6
|
||||
|
||||
A.4.3.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
|
||||
889d89dbfa691d1cde91517fa222ed7ad364
|
||||
BlindedElement = 03859b36b95e6564faa85cd3801175eda2949707f6aa0640ad0
|
||||
93cbf8ad2f58e762f08b56b2a1b42a64953aaf49cbf1ae3
|
||||
EvaluationElement = 0220710e2e00306453f5b4f574cb6a512453f35c45080d09
|
||||
373e190c19ce5b185914fbf36582d7e0754bb7c8b683205b91
|
||||
Proof = 82a17ef41c8b57f1e3122311b4d5cd39a63df0f67443ef18d961f9b659c1
|
||||
601ced8d3c64b294f604319ca80230380d437a49c7af0d620e22116669c008ebb767
|
||||
d90283d573b49cdb49e3725889620924c2c4b047a2a6225a3ba27e640ebddd33
|
||||
ProofRandomScalar = 803d955f0e073a04aa5d92b3fb739f56f9db001266677f62
|
||||
c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
|
||||
Output = 0188653cfec38119a6c7dd7948b0f0720460b4310e40824e048bf82a165
|
||||
27303ed449a08caf84272c3bbc972ede797df
|
||||
|
||||
A.4.3.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
|
||||
889d89dbfa691d1cde91517fa222ed7ad364
|
||||
BlindedElement = 03f7efcb4aaf000263369d8a0621cb96b81b3206e99876de2a0
|
||||
0699ed4c45acf3969cd6e2319215395955d3f8d8cc1c712
|
||||
EvaluationElement = 034993c818369927e74b77c400376fd1ae29b6ac6c6ddb77
|
||||
6cf10e4fbc487826531b3cf0b7c8ca4d92c7af90c9def85ce6
|
||||
Proof = 693471b5dff0cd6a5c00ea34d7bf127b2795164e3bdb5f39a1e5edfbd13e
|
||||
443bc516061cd5b8449a473c2ceeccada9f3e5b57302e3d7bc5e28d38d6e3a3056e1
|
||||
e73b6cc030f5180f8a1ffa45aa923ee66d2ad0a07b500f2acc7fb99b5506465c
|
||||
ProofRandomScalar = 803d955f0e073a04aa5d92b3fb739f56f9db001266677f62
|
||||
c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
|
||||
Output = ff2a527a21cc43b251a567382677f078c6e356336aec069dea8ba369953
|
||||
43ca3b33bb5d6cf15be4d31a7e6d75b30d3f5
|
||||
|
||||
A.4.3.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
|
||||
889d89dbfa691d1cde91517fa222ed7ad364,803d955f0e073a04aa5d92b3fb739f5
|
||||
6f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
|
||||
BlindedElement = 03859b36b95e6564faa85cd3801175eda2949707f6aa0640ad0
|
||||
93cbf8ad2f58e762f08b56b2a1b42a64953aaf49cbf1ae3,021a65d618d645f1a20b
|
||||
c33b06deaa7e73d6d634c8a56a3d02b53a732b69a5c53c5a207ea33d5afdcde9a22d
|
||||
59726bce51
|
||||
EvaluationElement = 0220710e2e00306453f5b4f574cb6a512453f35c45080d09
|
||||
373e190c19ce5b185914fbf36582d7e0754bb7c8b683205b91,02017657b315ec65e
|
||||
f861505e596c8645d94685dd7602cdd092a8f1c1c0194a5d0485fe47d071d972ab51
|
||||
4370174cc23f5
|
||||
Proof = 4a0b2fe96d5b2a046a0447fe079b77859ef11a39a3520d6ff7c626aad9b4
|
||||
73b724fb0cf188974ec961710a62162a83e97e0baa9eeada73397032d928b3e97b1e
|
||||
a92ad9458208302be3681b8ba78bcc17745bac00f84e0fdc98a6a8cba009c080
|
||||
ProofRandomScalar = a097e722ed2427de86966910acba9f5c350e8040f828bf6c
|
||||
eca27405420cdf3d63cb3aef005f40ba51943c8026877963
|
||||
Output = 0188653cfec38119a6c7dd7948b0f0720460b4310e40824e048bf82a165
|
||||
27303ed449a08caf84272c3bbc972ede797df,ff2a527a21cc43b251a567382677f0
|
||||
78c6e356336aec069dea8ba36995343ca3b33bb5d6cf15be4d31a7e6d75b30d3f5
|
||||
|
||||
A.5. P521-SHA512
|
||||
|
||||
A.5.1. OPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 0153441b8faedb0340439036d6aed06d1217b34c42f17f8db4c5cc610a4a9
|
||||
55d698a688831b16d0dc7713a1aa3611ec60703bffc7dc9c84e3ed673b3dbe1d5fcc
|
||||
ea6
|
||||
|
||||
A.5.1.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
|
||||
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 0300e78bf846b0e1e1a3c320e353d758583cd876df56100a3a1
|
||||
e62bacba470fa6e0991be1be80b721c50c5fd0c672ba764457acc18c6200704e9294
|
||||
fbf28859d916351
|
||||
EvaluationElement = 030166371cf827cb2fb9b581f97907121a16e2dc5d8b10ce
|
||||
9f0ede7f7d76a0d047657735e8ad07bcda824907b3e5479bd72cdef6b839b967ba5c
|
||||
58b118b84d26f2ba07
|
||||
Output = 26232de6fff83f812adadadb6cc05d7bbeee5dca043dbb16b03488abb99
|
||||
81d0a1ef4351fad52dbd7e759649af393348f7b9717566c19a6b8856284d69375c80
|
||||
9
|
||||
|
||||
A.5.1.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
|
||||
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 0300c28e57e74361d87e0c1874e5f7cc1cc796d61f9cad50427
|
||||
cf54655cdb455613368d42b27f94bf66f59f53c816db3e95e68e1b113443d66a99b3
|
||||
693bab88afb556b
|
||||
EvaluationElement = 0301ad453607e12d0cc11a3359332a40c3a254eaa1afc642
|
||||
96528d55bed07ba322e72e22cf3bcb50570fd913cb54f7f09c17aff8787af75f6a7f
|
||||
af5640cbb2d9620a6e
|
||||
Output = ad1f76ef939042175e007738906ac0336bbd1d51e287ebaa66901abdd32
|
||||
4ea3ffa40bfc5a68e7939c2845e0fd37a5a6e76dadb9907c6cc8579629757fd4d04b
|
||||
a
|
||||
|
||||
A.5.2. VOPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 015c7fc1b4a0b1390925bae915bd9f3d72009d44d9241b962428aad5d13f2
|
||||
2803311e7102632a39addc61ea440810222715c9d2f61f03ea424ec9ab1fe5e31cf9
|
||||
238
|
||||
pkSm = 0301505d646f6e4c9102451eb39730c4ba1c4087618641edbdba4a60896b0
|
||||
7fd0c9414ce553cbf25b81dfcca50a8f6724ab7a2bc4d0cf736967a287bb6084cc06
|
||||
78ac0
|
||||
|
||||
A.5.2.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
|
||||
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 0301d6e4fb545e043ddb6aee5d5ceeee1b44102615ab04430c2
|
||||
7dd0f56988dedcb1df32ef384f160e0e76e718605f14f3f582f9357553d153b99679
|
||||
5b4b3628a4f6380
|
||||
EvaluationElement = 03013fdeaf887f3d3d283a79e696a54b66ff0edcb559265e
|
||||
204a958acf840e0930cc147e2a6835148d8199eebc26c03e9394c9762a1c991dde40
|
||||
bca0f8ca003eefb045
|
||||
Proof = 0077fcc8ec6d059d7759b0a61f871e7c1dadc65333502e09a51994328f79
|
||||
e5bda3357b9a4f410a1760a3612c2f8f27cb7cb032951c047cc66da60da583df7b24
|
||||
7edd0188e5eb99c71799af1d80d643af16ffa1545acd9e9233fbb370455b10eb257e
|
||||
a12a1667c1b4ee5b0ab7c93d50ae89602006960f083ca9adc4f6276c0ad60440393c
|
||||
ProofRandomScalar = 015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e07
|
||||
3a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698c
|
||||
e45c405d1348b7b1
|
||||
Output = 5e003d9b2fb540b3d4bab5fedd154912246da1ee5e557afd8f56415faa1
|
||||
a0fadff6517da802ee254437e4f60907b4cda146e7ba19e249eef7be405549f62954
|
||||
b
|
||||
|
||||
A.5.2.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
|
||||
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 03005b05e656cb609ce5ff5faf063bb746d662d67bbd07c0626
|
||||
38396f52f0392180cf2365cabb0ece8e19048961d35eeae5d5fa872328dce98df076
|
||||
ee154dd191c615e
|
||||
EvaluationElement = 0301b19fcf482b1fff04754e282292ed736c5f0aa080d4f4
|
||||
2663cd3a416c6596f03129e8e096d8671fe5b0d19838312c511d2ce08d431e43e3ef
|
||||
06199d8cab7426238d
|
||||
Proof = 01ec9fece444caa6a57032e8963df0e945286f88fbdf233fb5101f0924f7
|
||||
ea89c47023f5f72f240e61991fd33a299b5b38c45a5e2dd1a67b072e59dfe86708a3
|
||||
59c701e38d383c60cf6969463bcf13251bedad47b7941f52e409a3591398e2792441
|
||||
0b18a301c0e19f527cad504fa08388050ac634e1b05c5216d337742f2754e1fc502f
|
||||
ProofRandomScalar = 015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e07
|
||||
3a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698c
|
||||
e45c405d1348b7b1
|
||||
Output = fa15eebba81ecf40954f7135cb76f69ef22c6bae394d1a4362f9b03066b
|
||||
54b6604d39f2e53369ca6762a3d9787e230e832aa85955af40ecb8deebb009a8cf47
|
||||
4
|
||||
|
||||
A.5.2.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
|
||||
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364,015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb7
|
||||
39f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b
|
||||
1
|
||||
BlindedElement = 0301d6e4fb545e043ddb6aee5d5ceeee1b44102615ab04430c2
|
||||
7dd0f56988dedcb1df32ef384f160e0e76e718605f14f3f582f9357553d153b99679
|
||||
5b4b3628a4f6380,0301403b597538b939b450c93586ba275f9711ba07e42364bac1
|
||||
d5769c6824a8b55be6f9a536df46d952b11ab2188363b3d6737635d9543d4dba14a6
|
||||
e19421b9245bf5
|
||||
EvaluationElement = 03013fdeaf887f3d3d283a79e696a54b66ff0edcb559265e
|
||||
204a958acf840e0930cc147e2a6835148d8199eebc26c03e9394c9762a1c991dde40
|
||||
bca0f8ca003eefb045,03001f96424497e38c46c904978c2fa1636c5c3dd2e634a85
|
||||
d8a7265977c5dce1f02c7e6c118479f0751767b91a39cce6561998258591b5d7c1bb
|
||||
02445a9e08e4f3e8d
|
||||
Proof = 00b4d215c8405e57c7a4b53398caf55f1f1623aaeb22408ddb9ea2913090
|
||||
9b3f95dbb1ff366e81e86e918f9f2fd8b80dbb344cd498c9499d112905e585417e00
|
||||
68c600fe5dea18b389ef6c4cc062935607b8ccbbb9a84fba3143868a3e8a58efa0bf
|
||||
6ca642804d09dc06e980f64837811227c4267b217f1099a4e28b0854f4e5ee659796
|
||||
ProofRandomScalar = 01ec21c7bb69b0734cb48dfd68433dd93b0fa097e722ed24
|
||||
27de86966910acba9f5c350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba
|
||||
51943c8026877963
|
||||
Output = 5e003d9b2fb540b3d4bab5fedd154912246da1ee5e557afd8f56415faa1
|
||||
a0fadff6517da802ee254437e4f60907b4cda146e7ba19e249eef7be405549f62954
|
||||
b,fa15eebba81ecf40954f7135cb76f69ef22c6bae394d1a4362f9b03066b54b6604
|
||||
d39f2e53369ca6762a3d9787e230e832aa85955af40ecb8deebb009a8cf474
|
||||
|
||||
A.5.3. POPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 014893130030ce69cf714f536498a02ff6b396888f9bb507985c32928c442
|
||||
7d6d39de10ef509aca4240e8569e3a88debc0d392e3361bcd934cb9bdd59e339dff7
|
||||
b27
|
||||
pkSm = 0301de8ceb9ffe9237b1bba87c320ea0bebcfc3447fe6f278065c6c69886d
|
||||
692d1126b79b6844f829940ace9b52a5e26882cf7cbc9e57503d4cca3cd834584729
|
||||
f812a
|
||||
|
||||
A.5.3.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
|
||||
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 020095cff9d7ecf65bdfee4ea92d6e748d60b02de34ad98094f
|
||||
82e25d33a8bf50138ccc2cc633556f1a97d7ea9438cbb394df612f041c485a515849
|
||||
d5ebb2238f2f0e2
|
||||
EvaluationElement = 0301408e9c5be3ffcc1c16e5ae8f8aa68446223b0804b119
|
||||
62e856af5a6d1c65ebbb5db7278c21db4e8cc06d89a35b6804fb1738a295b691638a
|
||||
f77aa1327253f26d01
|
||||
Proof = 0106a89a61eee9dd2417d2849a8e2167bc5f56e3aed5a3ff23e22511fa1b
|
||||
37a29ed44d1bbfd6907d99cfbc558a56aec709282415a864a281e49dc53792a4a638
|
||||
a0660034306d64be12a94dcea5a6d664cf76681911c8b9a84d49bf12d4893307ec14
|
||||
436bd05f791f82446c0de4be6c582d373627b51886f76c4788256e3da7ec8fa18a86
|
||||
ProofRandomScalar = 015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e07
|
||||
3a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698c
|
||||
e45c405d1348b7b1
|
||||
Output = 808ae5b87662eaaf0b39151dd85991b94c96ef214cb14a68bf5c1439548
|
||||
82d330da8953a80eea20788e552bc8bbbfff3100e89f9d6e341197b122c46a208733
|
||||
b
|
||||
|
||||
A.5.3.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
|
||||
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 030112ea89cf9cf589496189eafc5f9eb13c9f9e170d6ecde7c
|
||||
5b940541cb1a9c5cfeec908b67efe16b81ca00d0ce216e34b3d5f46a658d3fd8573d
|
||||
671bdb6515ed508
|
||||
EvaluationElement = 0200ebc49df1e6fa61f412e6c391e6f074400ecdd2f56c4a
|
||||
8c03fe0f91d9b551f40d4b5258fd891952e8c9b28003bcfa365122e54a5714c8949d
|
||||
5d202767b31b4bf1f6
|
||||
Proof = 0082162c71a7765005cae202d4bd14b84dae63c29067e886b82506992bd9
|
||||
94a1c3aac0c1c5309222fe1af8287b6443ed6df5c2e0b0991faddd3564c73c7597ae
|
||||
cd9a003b1f1e3c65f28e58ab4e767cfb4adbcaf512441645f4c2aed8bf67d132d966
|
||||
006d35fa71a34145414bf3572c1de1a46c266a344dd9e22e7fb1e90ffba1caf556d9
|
||||
ProofRandomScalar = 015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e07
|
||||
3a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698c
|
||||
e45c405d1348b7b1
|
||||
Output = 27032e24b1a52a82ab7f4646f3c5df0f070f499db98b9c5df33972bd5af
|
||||
5762c3638afae7912a6c1acdb1ae2ab2fa670bd5486c645a0e55412e08d33a4a0d6e
|
||||
3
|
||||
|
||||
A.5.3.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
|
||||
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364,015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb7
|
||||
39f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b
|
||||
1
|
||||
BlindedElement = 020095cff9d7ecf65bdfee4ea92d6e748d60b02de34ad98094f
|
||||
82e25d33a8bf50138ccc2cc633556f1a97d7ea9438cbb394df612f041c485a515849
|
||||
d5ebb2238f2f0e2,0201a328cf9f3fdeb86b6db242dd4cbb436b3a488b70b72d2fbb
|
||||
d1e5f50d7b0878b157d6f278c6a95c488f3ad52d6898a421658a82fe7ceb000b01ae
|
||||
dea7967522d525
|
||||
EvaluationElement = 0301408e9c5be3ffcc1c16e5ae8f8aa68446223b0804b119
|
||||
62e856af5a6d1c65ebbb5db7278c21db4e8cc06d89a35b6804fb1738a295b691638a
|
||||
f77aa1327253f26d01,020062ab51ac3aa829e0f5b7ae50688bcf5f63a18a83a6e0d
|
||||
a538666b8d50c7ea2b4ef31f4ac669302318dbebe46660acdda695da30c22cee7ca2
|
||||
1f6984a720504502e
|
||||
Proof = 00731738844f739bca0cca9d1c8bea204bed4fd00285785738b985763741
|
||||
de5cdfa275152d52b6a2fdf7792ef3779f39ba34581e56d62f78ecad5b7f8083f384
|
||||
961501cd4b43713253c022692669cf076b1d382ecd8293c1de69ea569737f37a2477
|
||||
2ab73517983c1e3db5818754ba1f008076267b8058b6481949ae346cdc17a8455fe2
|
||||
ProofRandomScalar = 01ec21c7bb69b0734cb48dfd68433dd93b0fa097e722ed24
|
||||
27de86966910acba9f5c350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba
|
||||
51943c8026877963
|
||||
Output = 808ae5b87662eaaf0b39151dd85991b94c96ef214cb14a68bf5c1439548
|
||||
82d330da8953a80eea20788e552bc8bbbfff3100e89f9d6e341197b122c46a208733
|
||||
b,27032e24b1a52a82ab7f4646f3c5df0f070f499db98b9c5df33972bd5af5762c36
|
||||
38afae7912a6c1acdb1ae2ab2fa670bd5486c645a0e55412e08d33a4a0d6e3
|
||||
"#;
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
// Copyright (c) VexaHub and contributors.
|
||||
|
||||
use core::iter;
|
||||
|
||||
use digest::Output;
|
||||
|
||||
use crate::common::{Dst, Mode, STR_HASH_TO_GROUP, finalize_after_unblind};
|
||||
use crate::{CipherSuite, Group};
|
||||
|
||||
pub(crate) fn prf<CS: CipherSuite>(
|
||||
input: &[u8],
|
||||
key: <CS::Group as Group>::Scalar,
|
||||
mode: Mode,
|
||||
) -> Output<CS::Hash> {
|
||||
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, mode);
|
||||
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
|
||||
|
||||
let res = point * &key;
|
||||
|
||||
finalize_after_unblind::<CS, _, _>(iter::once((input, res)))
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
// Copyright (c) VexaHub and contributors.
|
||||
|
||||
macro_rules! test_all_curves {
|
||||
($($test_fn:ident),+ $(,)?) => {
|
||||
#[test]
|
||||
fn test_functionality() -> $crate::Result<()> {
|
||||
#[cfg(feature = "ristretto255")]
|
||||
{
|
||||
$( $test_fn::<$crate::Ristretto255>(); )+
|
||||
}
|
||||
$( $test_fn::<::p256::NistP256>(); )+
|
||||
$( $test_fn::<::p384::NistP384>(); )+
|
||||
$( $test_fn::<::p521::NistP521>(); )+
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use test_all_curves;
|
||||
+29
-26
@@ -1,27 +1,24 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
// Copyright (c) VexaHub and contributors.
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::cmp::min;
|
||||
use rand_core::{CryptoRng, Error, RngCore};
|
||||
|
||||
use core::convert::Infallible;
|
||||
use rand_core::{TryCryptoRng, TryRng};
|
||||
|
||||
/// A simple implementation of `RngCore` for testing purposes.
|
||||
///
|
||||
/// This generates a cyclic sequence (i.e. cycles over an initial buffer)
|
||||
///
|
||||
///
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CycleRng {
|
||||
v: Vec<u8>,
|
||||
}
|
||||
|
||||
impl CycleRng {
|
||||
/// Create a `CycleRng`, yielding a sequence starting with
|
||||
/// `initial` and looping thereafter
|
||||
/// Create a `CycleRng`, yielding a sequence starting with `initial` and
|
||||
/// looping thereafter
|
||||
pub fn new(initial: Vec<u8>) -> Self {
|
||||
CycleRng { v: initial }
|
||||
}
|
||||
@@ -38,29 +35,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());
|
||||
(&mut dest[..len]).copy_from_slice(&self.v[..len]);
|
||||
rotate_left(&mut self.v, len);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
|
||||
self.fill_bytes(dest);
|
||||
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 {}
|
||||
|
||||
+10
-8
@@ -1,11 +1,13 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
// Copyright (c) VexaHub and contributors.
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
mod cfrg_vectors;
|
||||
mod macros;
|
||||
mod mock_rng;
|
||||
mod parser;
|
||||
mod voprf_test_vectors;
|
||||
mod voprf_vectors;
|
||||
mod test_cfrg_vectors;
|
||||
|
||||
pub(crate) mod helpers;
|
||||
|
||||
pub(crate) use macros::test_all_curves;
|
||||
|
||||
+16
-22
@@ -1,66 +1,60 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
// Copyright (c) VexaHub and contributors.
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
use alloc::string::String;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{format, vec};
|
||||
|
||||
pub(crate) fn rfc_to_json(input: &str) -> String {
|
||||
format!("{{\n{}\n}}", parse_ciphersuites(input))
|
||||
}
|
||||
|
||||
fn parse_ciphersuites(input: &str) -> String {
|
||||
let re = regex::Regex::new(r"## OPRF\((?P<ciphersuite>.+?)\)").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"].to_string(),
|
||||
&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"### (?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"].to_string(),
|
||||
&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"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")
|
||||
@@ -94,7 +88,7 @@ fn parse_params(input: &str) -> String {
|
||||
let key = iter.next().unwrap().split_whitespace().next().unwrap();
|
||||
let val = iter.next().unwrap().split_whitespace().next().unwrap();
|
||||
|
||||
param = format!(" \"{}\": \"{}", key, val);
|
||||
param = format!(" \"{key}\": \"{val}");
|
||||
} else {
|
||||
let s = line.trim().to_string();
|
||||
if s.contains('~') || s.contains('#') {
|
||||
|
||||
@@ -0,0 +1,523 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
// Copyright (c) VexaHub and contributors.
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
use alloc::string::String;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::tests::mock_rng::CycleRng;
|
||||
use crate::tests::parser::*;
|
||||
use crate::{
|
||||
BlindedElement, CipherSuite, EvaluationElement, Group, OprfClient, OprfServer, PoprfClient,
|
||||
PoprfServer, PoprfServerBatchEvaluateFinishResult, PoprfServerBatchEvaluatePrepareResult,
|
||||
Proof, Result, VoprfClient, VoprfServer, VoprfServerBatchEvaluateFinishResult,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct VOPRFTestVectorParameters {
|
||||
seed: Vec<u8>,
|
||||
sksm: Vec<u8>,
|
||||
pksm: Vec<u8>,
|
||||
input: Vec<Vec<u8>>,
|
||||
info: Vec<u8>,
|
||||
key_info: Vec<u8>,
|
||||
blind: Vec<Vec<u8>>,
|
||||
blinded_element: Vec<Vec<u8>>,
|
||||
evaluation_element: Vec<Vec<u8>>,
|
||||
proof: Vec<u8>,
|
||||
proof_random_scalar: Vec<u8>,
|
||||
output: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
fn populate_test_vectors(values: &Value) -> VOPRFTestVectorParameters {
|
||||
VOPRFTestVectorParameters {
|
||||
seed: decode(values, "Seed"),
|
||||
sksm: decode(values, "skSm"),
|
||||
pksm: decode(values, "pkSm"),
|
||||
input: decode_vec(values, "Input"),
|
||||
info: decode(values, "Info"),
|
||||
key_info: decode(values, "KeyInfo"),
|
||||
blind: decode_vec(values, "Blind"),
|
||||
blinded_element: decode_vec(values, "BlindedElement"),
|
||||
evaluation_element: decode_vec(values, "EvaluationElement"),
|
||||
proof: decode(values, "Proof"),
|
||||
proof_random_scalar: decode(values, "ProofRandomScalar"),
|
||||
output: decode_vec(values, "Output"),
|
||||
}
|
||||
}
|
||||
|
||||
fn decode(values: &Value, key: &str) -> Vec<u8> {
|
||||
values[key]
|
||||
.as_str()
|
||||
.and_then(|s| hex::decode(s).ok())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn decode_vec(values: &Value, key: &str) -> Vec<Vec<u8>> {
|
||||
let s = values[key].as_str().unwrap();
|
||||
let res = match s.contains(',') {
|
||||
true => Some(s.split(',').map(|x| hex::decode(x).unwrap()).collect()),
|
||||
false => Some(vec![hex::decode(s).unwrap()]),
|
||||
};
|
||||
res.unwrap()
|
||||
}
|
||||
|
||||
macro_rules! json_to_test_vectors {
|
||||
( $v:ident, $cs:expr, $mode:expr ) => {
|
||||
$v[$cs][$mode]
|
||||
.as_array()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(populate_test_vectors)
|
||||
.collect::<Vec<VOPRFTestVectorParameters>>()
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vectors() -> Result<()> {
|
||||
use p256::NistP256;
|
||||
use p384::NistP384;
|
||||
use p521::NistP521;
|
||||
|
||||
let rfc: Value = serde_json::from_str(rfc_to_json(super::cfrg_vectors::VECTORS).as_str())
|
||||
.expect("Could not parse json");
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
{
|
||||
use crate::Ristretto255;
|
||||
|
||||
let ristretto_oprf_tvs = json_to_test_vectors!(
|
||||
rfc,
|
||||
String::from("ristretto255-SHA512"),
|
||||
String::from("OPRF")
|
||||
);
|
||||
assert_ne!(ristretto_oprf_tvs.len(), 0);
|
||||
test_oprf_seed_to_key::<Ristretto255>(&ristretto_oprf_tvs)?;
|
||||
test_oprf_blind::<Ristretto255>(&ristretto_oprf_tvs)?;
|
||||
test_oprf_blind_evaluate::<Ristretto255>(&ristretto_oprf_tvs)?;
|
||||
test_oprf_finalize::<Ristretto255>(&ristretto_oprf_tvs)?;
|
||||
test_oprf_evaluate::<Ristretto255>(&ristretto_oprf_tvs)?;
|
||||
|
||||
let ristretto_voprf_tvs = json_to_test_vectors!(
|
||||
rfc,
|
||||
String::from("ristretto255-SHA512"),
|
||||
String::from("VOPRF")
|
||||
);
|
||||
assert_ne!(ristretto_voprf_tvs.len(), 0);
|
||||
test_voprf_seed_to_key::<Ristretto255>(&ristretto_voprf_tvs)?;
|
||||
test_voprf_blind::<Ristretto255>(&ristretto_voprf_tvs)?;
|
||||
test_voprf_blind_evaluate::<Ristretto255>(&ristretto_voprf_tvs)?;
|
||||
test_voprf_finalize::<Ristretto255>(&ristretto_voprf_tvs)?;
|
||||
test_voprf_evaluate::<Ristretto255>(&ristretto_voprf_tvs)?;
|
||||
|
||||
let ristretto_poprf_tvs = json_to_test_vectors!(
|
||||
rfc,
|
||||
String::from("ristretto255-SHA512"),
|
||||
String::from("POPRF")
|
||||
);
|
||||
assert_ne!(ristretto_poprf_tvs.len(), 0);
|
||||
test_poprf_seed_to_key::<Ristretto255>(&ristretto_poprf_tvs)?;
|
||||
test_poprf_blind::<Ristretto255>(&ristretto_poprf_tvs)?;
|
||||
test_poprf_blind_evaluate::<Ristretto255>(&ristretto_poprf_tvs)?;
|
||||
test_poprf_finalize::<Ristretto255>(&ristretto_poprf_tvs)?;
|
||||
test_poprf_evaluate::<Ristretto255>(&ristretto_poprf_tvs)?;
|
||||
}
|
||||
|
||||
let p256_oprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P256-SHA256"), String::from("OPRF"));
|
||||
assert_ne!(p256_oprf_tvs.len(), 0);
|
||||
test_oprf_seed_to_key::<NistP256>(&p256_oprf_tvs)?;
|
||||
test_oprf_blind::<NistP256>(&p256_oprf_tvs)?;
|
||||
test_oprf_blind_evaluate::<NistP256>(&p256_oprf_tvs)?;
|
||||
test_oprf_finalize::<NistP256>(&p256_oprf_tvs)?;
|
||||
test_oprf_evaluate::<NistP256>(&p256_oprf_tvs)?;
|
||||
|
||||
let p256_voprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P256-SHA256"), String::from("VOPRF"));
|
||||
assert_ne!(p256_voprf_tvs.len(), 0);
|
||||
test_voprf_seed_to_key::<NistP256>(&p256_voprf_tvs)?;
|
||||
test_voprf_blind::<NistP256>(&p256_voprf_tvs)?;
|
||||
test_voprf_blind_evaluate::<NistP256>(&p256_voprf_tvs)?;
|
||||
test_voprf_finalize::<NistP256>(&p256_voprf_tvs)?;
|
||||
test_voprf_evaluate::<NistP256>(&p256_voprf_tvs)?;
|
||||
|
||||
let p256_poprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P256-SHA256"), String::from("POPRF"));
|
||||
assert_ne!(p256_poprf_tvs.len(), 0);
|
||||
test_poprf_seed_to_key::<NistP256>(&p256_poprf_tvs)?;
|
||||
test_poprf_blind::<NistP256>(&p256_poprf_tvs)?;
|
||||
test_poprf_blind_evaluate::<NistP256>(&p256_poprf_tvs)?;
|
||||
test_poprf_finalize::<NistP256>(&p256_poprf_tvs)?;
|
||||
test_poprf_evaluate::<NistP256>(&p256_poprf_tvs)?;
|
||||
|
||||
let p384_oprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P384-SHA384"), String::from("OPRF"));
|
||||
assert_ne!(p384_oprf_tvs.len(), 0);
|
||||
test_oprf_seed_to_key::<NistP384>(&p384_oprf_tvs)?;
|
||||
test_oprf_blind::<NistP384>(&p384_oprf_tvs)?;
|
||||
test_oprf_blind_evaluate::<NistP384>(&p384_oprf_tvs)?;
|
||||
test_oprf_finalize::<NistP384>(&p384_oprf_tvs)?;
|
||||
test_oprf_evaluate::<NistP384>(&p384_oprf_tvs)?;
|
||||
|
||||
let p384_voprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P384-SHA384"), String::from("VOPRF"));
|
||||
assert_ne!(p384_voprf_tvs.len(), 0);
|
||||
test_voprf_seed_to_key::<NistP384>(&p384_voprf_tvs)?;
|
||||
test_voprf_blind::<NistP384>(&p384_voprf_tvs)?;
|
||||
test_voprf_blind_evaluate::<NistP384>(&p384_voprf_tvs)?;
|
||||
test_voprf_finalize::<NistP384>(&p384_voprf_tvs)?;
|
||||
test_voprf_evaluate::<NistP384>(&p384_voprf_tvs)?;
|
||||
|
||||
let p384_poprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P384-SHA384"), String::from("POPRF"));
|
||||
assert_ne!(p384_poprf_tvs.len(), 0);
|
||||
test_poprf_seed_to_key::<NistP384>(&p384_poprf_tvs)?;
|
||||
test_poprf_blind::<NistP384>(&p384_poprf_tvs)?;
|
||||
test_poprf_blind_evaluate::<NistP384>(&p384_poprf_tvs)?;
|
||||
test_poprf_finalize::<NistP384>(&p384_poprf_tvs)?;
|
||||
test_poprf_evaluate::<NistP384>(&p384_poprf_tvs)?;
|
||||
|
||||
let p521_oprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P521-SHA512"), String::from("OPRF"));
|
||||
assert_ne!(p521_oprf_tvs.len(), 0);
|
||||
test_oprf_seed_to_key::<NistP521>(&p521_oprf_tvs)?;
|
||||
test_oprf_blind::<NistP521>(&p521_oprf_tvs)?;
|
||||
test_oprf_blind_evaluate::<NistP521>(&p521_oprf_tvs)?;
|
||||
test_oprf_finalize::<NistP521>(&p521_oprf_tvs)?;
|
||||
test_oprf_evaluate::<NistP521>(&p521_oprf_tvs)?;
|
||||
|
||||
let p521_voprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P521-SHA512"), String::from("VOPRF"));
|
||||
assert_ne!(p521_voprf_tvs.len(), 0);
|
||||
test_voprf_seed_to_key::<NistP521>(&p521_voprf_tvs)?;
|
||||
test_voprf_blind::<NistP521>(&p521_voprf_tvs)?;
|
||||
test_voprf_blind_evaluate::<NistP521>(&p521_voprf_tvs)?;
|
||||
test_voprf_finalize::<NistP521>(&p521_voprf_tvs)?;
|
||||
test_voprf_evaluate::<NistP521>(&p521_voprf_tvs)?;
|
||||
|
||||
let p521_poprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P521-SHA512"), String::from("POPRF"));
|
||||
assert_ne!(p521_poprf_tvs.len(), 0);
|
||||
test_poprf_seed_to_key::<NistP521>(&p521_poprf_tvs)?;
|
||||
test_poprf_blind::<NistP521>(&p521_poprf_tvs)?;
|
||||
test_poprf_blind_evaluate::<NistP521>(&p521_poprf_tvs)?;
|
||||
test_poprf_finalize::<NistP521>(&p521_poprf_tvs)?;
|
||||
test_poprf_evaluate::<NistP521>(&p521_poprf_tvs)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_oprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
let server = OprfServer::<CS>::new_from_seed(¶meters.seed, ¶meters.key_info)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.sksm,
|
||||
&CS::Group::serialize_scalar(server.get_private_key()).to_vec()
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_voprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
let server = VoprfServer::<CS>::new_from_seed(¶meters.seed, ¶meters.key_info)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.sksm,
|
||||
&CS::Group::serialize_scalar(server.get_private_key()).to_vec()
|
||||
);
|
||||
assert_eq!(
|
||||
¶meters.pksm,
|
||||
CS::Group::serialize_elem(server.get_public_key()).as_slice()
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_poprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
let server = PoprfServer::<CS>::new_from_seed(¶meters.seed, ¶meters.key_info)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.sksm,
|
||||
&CS::Group::serialize_scalar(server.get_private_key()).to_vec()
|
||||
);
|
||||
assert_eq!(
|
||||
¶meters.pksm,
|
||||
CS::Group::serialize_elem(server.get_public_key()).as_slice()
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests input -> blind, blinded_element
|
||||
fn test_oprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||
let client_result =
|
||||
OprfClient::<CS>::deterministic_blind_unchecked(¶meters.input[i], blind)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.blind[i],
|
||||
&CS::Group::serialize_scalar(client_result.state.blind).to_vec()
|
||||
);
|
||||
assert_eq!(
|
||||
parameters.blinded_element[i].as_slice(),
|
||||
client_result.message.serialize().as_slice(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests input -> blind, blinded_element
|
||||
fn test_voprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||
let client_blind_result =
|
||||
VoprfClient::<CS>::deterministic_blind_unchecked(¶meters.input[i], blind)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.blind[i],
|
||||
&CS::Group::serialize_scalar(client_blind_result.state.get_blind()).to_vec()
|
||||
);
|
||||
assert_eq!(
|
||||
parameters.blinded_element[i].as_slice(),
|
||||
client_blind_result.message.serialize().as_slice(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests input -> blind, blinded_element
|
||||
fn test_poprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||
let client_blind_result =
|
||||
PoprfClient::<CS>::deterministic_blind_unchecked(¶meters.input[i], blind)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.blind[i],
|
||||
&CS::Group::serialize_scalar(client_blind_result.state.get_blind()).to_vec()
|
||||
);
|
||||
assert_eq!(
|
||||
parameters.blinded_element[i].as_slice(),
|
||||
client_blind_result.message.serialize().as_slice(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests sksm, blinded_element -> evaluation_element
|
||||
fn test_oprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let server = OprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
let message = server.blind_evaluate(&BlindedElement::deserialize(
|
||||
¶meters.blinded_element[i],
|
||||
)?);
|
||||
|
||||
assert_eq!(
|
||||
¶meters.evaluation_element[i],
|
||||
&message.serialize().as_slice()
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_voprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
|
||||
let server = VoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
|
||||
let mut blinded_elements = vec![];
|
||||
for blinded_element_bytes in ¶meters.blinded_element {
|
||||
blinded_elements.push(BlindedElement::deserialize(blinded_element_bytes)?);
|
||||
}
|
||||
|
||||
let prepared_evaluation_elements =
|
||||
server.batch_blind_evaluate_prepare(blinded_elements.iter());
|
||||
let prepared_elements: Vec<_> = prepared_evaluation_elements.collect();
|
||||
let VoprfServerBatchEvaluateFinishResult { messages, proof } = server
|
||||
.batch_blind_evaluate_finish(&mut rng, blinded_elements.iter(), &prepared_elements)?;
|
||||
let messages: Vec<_> = messages.collect();
|
||||
|
||||
for (parameter, message) in parameters.evaluation_element.iter().zip(messages) {
|
||||
assert_eq!(¶meter, &message.serialize().as_slice());
|
||||
}
|
||||
|
||||
assert_eq!(¶meters.proof, &proof.serialize().to_vec());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_poprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
|
||||
let server = PoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
|
||||
let mut blinded_elements = vec![];
|
||||
for blinded_element_bytes in ¶meters.blinded_element {
|
||||
blinded_elements.push(BlindedElement::deserialize(blinded_element_bytes)?);
|
||||
}
|
||||
|
||||
let PoprfServerBatchEvaluatePrepareResult {
|
||||
prepared_evaluation_elements,
|
||||
prepared_tweak,
|
||||
} = server.batch_blind_evaluate_prepare(blinded_elements.iter(), Some(¶meters.info))?;
|
||||
let prepared_evaluation_elements: Vec<_> = prepared_evaluation_elements.collect();
|
||||
let PoprfServerBatchEvaluateFinishResult { messages, proof } =
|
||||
PoprfServer::batch_blind_evaluate_finish::<_, _, Vec<_>>(
|
||||
&mut rng,
|
||||
blinded_elements.iter(),
|
||||
&prepared_evaluation_elements,
|
||||
&prepared_tweak,
|
||||
)?;
|
||||
|
||||
let messages: Vec<_> = messages.collect();
|
||||
|
||||
for (parameter, message) in parameters.evaluation_element.iter().zip(messages) {
|
||||
assert_eq!(¶meter, &message.serialize().as_slice());
|
||||
}
|
||||
|
||||
assert_eq!(¶meters.proof, &proof.serialize().to_vec());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests input, blind, evaluation_element -> output
|
||||
fn test_oprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let client =
|
||||
OprfClient::<CS>::from_blind(CS::Group::deserialize_scalar(¶meters.blind[i])?);
|
||||
|
||||
let client_finalize_result = client.finalize(
|
||||
¶meters.input[i],
|
||||
&EvaluationElement::deserialize(¶meters.evaluation_element[i])?,
|
||||
)?;
|
||||
|
||||
assert_eq!(¶meters.output[i], &client_finalize_result.to_vec());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_voprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
let mut clients = vec![];
|
||||
for i in 0..parameters.input.len() {
|
||||
let client = VoprfClient::<CS>::from_blind_and_element(
|
||||
CS::Group::deserialize_scalar(¶meters.blind[i])?,
|
||||
CS::Group::deserialize_elem(¶meters.blinded_element[i])?,
|
||||
);
|
||||
clients.push(client.clone());
|
||||
}
|
||||
|
||||
let messages: Vec<_> = parameters
|
||||
.evaluation_element
|
||||
.iter()
|
||||
.map(|x| EvaluationElement::deserialize(x).unwrap())
|
||||
.collect();
|
||||
|
||||
let batch_result = VoprfClient::batch_finalize(
|
||||
¶meters.input,
|
||||
&clients,
|
||||
&messages,
|
||||
&Proof::deserialize(¶meters.proof)?,
|
||||
CS::Group::deserialize_elem(¶meters.pksm)?,
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
parameters.output,
|
||||
batch_result
|
||||
.map(|arr| arr.map(|message| message.to_vec()))
|
||||
.collect::<Result<Vec<_>>>()?
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_poprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
let mut clients = vec![];
|
||||
for i in 0..parameters.input.len() {
|
||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||
let client_blind_result =
|
||||
PoprfClient::<CS>::deterministic_blind_unchecked(¶meters.input[i], blind)?;
|
||||
let client = client_blind_result.state;
|
||||
clients.push(client.clone());
|
||||
}
|
||||
|
||||
let messages: Vec<_> = parameters
|
||||
.evaluation_element
|
||||
.iter()
|
||||
.map(|x| EvaluationElement::deserialize(x).unwrap())
|
||||
.collect();
|
||||
|
||||
let batch_result = PoprfClient::batch_finalize(
|
||||
parameters.input.iter().map(|input| input.as_slice()),
|
||||
&clients,
|
||||
&messages,
|
||||
&Proof::deserialize(¶meters.proof)?,
|
||||
CS::Group::deserialize_elem(¶meters.pksm)?,
|
||||
Some(¶meters.info),
|
||||
)?;
|
||||
|
||||
let result: Vec<Vec<u8>> = batch_result.map(|arr| arr.unwrap().to_vec()).collect();
|
||||
|
||||
assert_eq!(parameters.output, result);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests input, sksm -> output
|
||||
fn test_oprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let server = OprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
|
||||
let server_evaluate_result = server.evaluate(¶meters.input[i])?;
|
||||
|
||||
assert_eq!(¶meters.output[i], &server_evaluate_result.to_vec());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_voprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let server = VoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
|
||||
let server_evaluate_result = server.evaluate(¶meters.input[i])?;
|
||||
|
||||
assert_eq!(¶meters.output[i], &server_evaluate_result.to_vec());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_poprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let server = PoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
|
||||
let server_evaluate_result =
|
||||
server.evaluate(¶meters.input[i], Some(¶meters.info))?;
|
||||
|
||||
assert_eq!(¶meters.output[i], &server_evaluate_result.to_vec());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,335 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
use crate::{
|
||||
errors::InternalError,
|
||||
group::Group,
|
||||
tests::{mock_rng::CycleRng, parser::*},
|
||||
voprf::{
|
||||
BlindedElement, EvaluationElement, NonVerifiableClient, NonVerifiableServer, Proof,
|
||||
VerifiableClient, VerifiableServer,
|
||||
},
|
||||
};
|
||||
use alloc::string::ToString;
|
||||
use alloc::vec::Vec;
|
||||
use digest::{BlockInput, Digest};
|
||||
use generic_array::GenericArray;
|
||||
use json::JsonValue;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct VOPRFTestVectorParameters {
|
||||
seed: Vec<u8>,
|
||||
sksm: Vec<u8>,
|
||||
pksm: Vec<u8>,
|
||||
input: Vec<Vec<u8>>,
|
||||
info: Vec<u8>,
|
||||
blind: Vec<Vec<u8>>,
|
||||
blinded_element: Vec<Vec<u8>>,
|
||||
evaluation_element: Vec<Vec<u8>>,
|
||||
proof: Vec<u8>,
|
||||
proof_random_scalar: Vec<u8>,
|
||||
output: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
fn populate_test_vectors(values: &JsonValue) -> VOPRFTestVectorParameters {
|
||||
VOPRFTestVectorParameters {
|
||||
seed: decode(values, "seed"),
|
||||
sksm: decode(values, "skSm"),
|
||||
pksm: decode(values, "pkSm"),
|
||||
input: decode_vec(values, "Input"),
|
||||
info: decode(values, "Info"),
|
||||
blind: decode_vec(values, "Blind"),
|
||||
blinded_element: decode_vec(values, "BlindedElement"),
|
||||
evaluation_element: decode_vec(values, "EvaluationElement"),
|
||||
proof: decode(values, "Proof"),
|
||||
proof_random_scalar: decode(values, "ProofRandomScalar"),
|
||||
output: decode_vec(values, "Output"),
|
||||
}
|
||||
}
|
||||
|
||||
fn decode(values: &JsonValue, key: &str) -> Vec<u8> {
|
||||
values[key]
|
||||
.as_str()
|
||||
.and_then(|s| hex::decode(&s.to_string()).ok())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn decode_vec(values: &JsonValue, key: &str) -> Vec<Vec<u8>> {
|
||||
let s = values[key].as_str().unwrap();
|
||||
let res = match s.contains(',') {
|
||||
true => Some(
|
||||
s.split(',')
|
||||
.map(|x| hex::decode(&x.to_string()).unwrap())
|
||||
.collect(),
|
||||
),
|
||||
false => Some(vec![hex::decode(&s.to_string()).unwrap()]),
|
||||
};
|
||||
res.unwrap()
|
||||
}
|
||||
|
||||
macro_rules! json_to_test_vectors {
|
||||
( $v:ident, $cs:expr, $mode:expr ) => {
|
||||
$v[$cs][$mode]
|
||||
.members()
|
||||
.map(|x| populate_test_vectors(&x))
|
||||
.collect::<Vec<VOPRFTestVectorParameters>>()
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vectors() -> Result<(), InternalError> {
|
||||
let rfc = json::parse(rfc_to_json(super::voprf_vectors::VECTORS).as_str())
|
||||
.expect("Could not parse json");
|
||||
|
||||
use curve25519_dalek::ristretto::RistrettoPoint;
|
||||
use sha2::Sha512;
|
||||
|
||||
let ristretto_base_tvs = json_to_test_vectors!(
|
||||
rfc,
|
||||
String::from("ristretto255, SHA-512"),
|
||||
String::from("Base")
|
||||
);
|
||||
|
||||
let ristretto_verifiable_tvs = json_to_test_vectors!(
|
||||
rfc,
|
||||
String::from("ristretto255, SHA-512"),
|
||||
String::from("Verifiable")
|
||||
);
|
||||
|
||||
test_base_seed_to_key::<RistrettoPoint, Sha512>(&ristretto_base_tvs)?;
|
||||
test_base_blind::<RistrettoPoint, Sha512>(&ristretto_base_tvs)?;
|
||||
test_base_evaluate::<RistrettoPoint, Sha512>(&ristretto_base_tvs)?;
|
||||
test_base_finalize::<RistrettoPoint, Sha512>(&ristretto_base_tvs)?;
|
||||
|
||||
test_verifiable_seed_to_key::<RistrettoPoint, Sha512>(&ristretto_verifiable_tvs)?;
|
||||
test_verifiable_blind::<RistrettoPoint, Sha512>(&ristretto_verifiable_tvs)?;
|
||||
test_verifiable_evaluate::<RistrettoPoint, Sha512>(&ristretto_verifiable_tvs)?;
|
||||
test_verifiable_finalize::<RistrettoPoint, Sha512>(&ristretto_verifiable_tvs)?;
|
||||
|
||||
#[cfg(feature = "p256")]
|
||||
{
|
||||
use p256_::ProjectivePoint;
|
||||
use sha2::Sha256;
|
||||
|
||||
let p256_base_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P-256, SHA-256"), String::from("Base"));
|
||||
|
||||
let p256_verifiable_tvs = json_to_test_vectors!(
|
||||
rfc,
|
||||
String::from("P-256, SHA-256"),
|
||||
String::from("Verifiable")
|
||||
);
|
||||
|
||||
test_base_seed_to_key::<ProjectivePoint, Sha256>(&p256_base_tvs)?;
|
||||
test_base_blind::<ProjectivePoint, Sha256>(&p256_base_tvs)?;
|
||||
test_base_evaluate::<ProjectivePoint, Sha256>(&p256_base_tvs)?;
|
||||
test_base_finalize::<ProjectivePoint, Sha256>(&p256_base_tvs)?;
|
||||
|
||||
test_verifiable_seed_to_key::<ProjectivePoint, Sha256>(&p256_verifiable_tvs)?;
|
||||
test_verifiable_blind::<ProjectivePoint, Sha256>(&p256_verifiable_tvs)?;
|
||||
test_verifiable_evaluate::<ProjectivePoint, Sha256>(&p256_verifiable_tvs)?;
|
||||
test_verifiable_finalize::<ProjectivePoint, Sha256>(&p256_verifiable_tvs)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_base_seed_to_key<G: Group, H: BlockInput + Digest>(
|
||||
tvs: &[VOPRFTestVectorParameters],
|
||||
) -> Result<(), InternalError> {
|
||||
for parameters in tvs {
|
||||
let server = NonVerifiableServer::<G, H>::new_from_seed(¶meters.seed)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.sksm,
|
||||
&G::scalar_as_bytes(server.get_private_key()).to_vec()
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_verifiable_seed_to_key<G: Group, H: BlockInput + Digest>(
|
||||
tvs: &[VOPRFTestVectorParameters],
|
||||
) -> Result<(), InternalError> {
|
||||
for parameters in tvs {
|
||||
let server = VerifiableServer::<G, H>::new_from_seed(¶meters.seed)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.sksm,
|
||||
&G::scalar_as_bytes(server.get_private_key()).to_vec()
|
||||
);
|
||||
assert_eq!(¶meters.pksm, &server.get_public_key().to_arr().to_vec());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests input -> blind, blinded_element
|
||||
fn test_base_blind<G: Group, H: BlockInput + Digest>(
|
||||
tvs: &[VOPRFTestVectorParameters],
|
||||
) -> Result<(), InternalError> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let blind =
|
||||
G::from_scalar_slice(&GenericArray::clone_from_slice(¶meters.blind[i]))?;
|
||||
let client_result = NonVerifiableClient::<G, H>::deterministic_blind_unchecked(
|
||||
parameters.input[i].clone(),
|
||||
blind,
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.blind[i],
|
||||
&G::scalar_as_bytes(client_result.state.get_blind()).to_vec()
|
||||
);
|
||||
assert_eq!(
|
||||
¶meters.blinded_element[i],
|
||||
&client_result.message.serialize()
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests input -> blind, blinded_element
|
||||
fn test_verifiable_blind<G: Group, H: BlockInput + Digest>(
|
||||
tvs: &[VOPRFTestVectorParameters],
|
||||
) -> Result<(), InternalError> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let blind =
|
||||
G::from_scalar_slice(&GenericArray::clone_from_slice(¶meters.blind[i]))?;
|
||||
let client_blind_result = VerifiableClient::<G, H>::deterministic_blind_unchecked(
|
||||
parameters.input[i].clone(),
|
||||
blind,
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.blind[i],
|
||||
&G::scalar_as_bytes(client_blind_result.state.get_blind()).to_vec()
|
||||
);
|
||||
assert_eq!(
|
||||
¶meters.blinded_element[i],
|
||||
&client_blind_result.message.serialize()
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests sksm, blinded_element -> evaluation_element
|
||||
fn test_base_evaluate<G: Group, H: BlockInput + Digest>(
|
||||
tvs: &[VOPRFTestVectorParameters],
|
||||
) -> Result<(), InternalError> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let server = NonVerifiableServer::<G, H>::new_with_key(¶meters.sksm)?;
|
||||
let server_result = server.evaluate(
|
||||
BlindedElement::deserialize(¶meters.blinded_element[i])?,
|
||||
Some(¶meters.info),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.evaluation_element[i],
|
||||
&server_result.message.serialize()
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_verifiable_evaluate<G: Group, H: BlockInput + Digest>(
|
||||
tvs: &[VOPRFTestVectorParameters],
|
||||
) -> Result<(), InternalError> {
|
||||
for parameters in tvs {
|
||||
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
|
||||
let server = VerifiableServer::<G, H>::new_with_key(¶meters.sksm)?;
|
||||
|
||||
let mut blinded_elements = vec![];
|
||||
for blinded_element_bytes in ¶meters.blinded_element {
|
||||
blinded_elements.push(BlindedElement::deserialize(blinded_element_bytes)?);
|
||||
}
|
||||
|
||||
let batch_evaluate_result =
|
||||
server.batch_evaluate(&mut rng, &blinded_elements, Some(¶meters.info))?;
|
||||
|
||||
for i in 0..parameters.evaluation_element.len() {
|
||||
assert_eq!(
|
||||
¶meters.evaluation_element[i],
|
||||
&batch_evaluate_result.messages[i].serialize(),
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(¶meters.proof, &batch_evaluate_result.proof.serialize());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests input, blind, evaluation_element -> output
|
||||
fn test_base_finalize<G: Group, H: BlockInput + Digest>(
|
||||
tvs: &[VOPRFTestVectorParameters],
|
||||
) -> Result<(), InternalError> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let client = NonVerifiableClient::<G, H>::from_data_and_blind(
|
||||
¶meters.input[i],
|
||||
<G as Group>::from_scalar_slice(&GenericArray::clone_from_slice(
|
||||
¶meters.blind[i],
|
||||
))?,
|
||||
);
|
||||
|
||||
let client_finalize_result = client.finalize(
|
||||
EvaluationElement::deserialize(¶meters.evaluation_element[i])?,
|
||||
Some(¶meters.info),
|
||||
)?;
|
||||
|
||||
assert_eq!(¶meters.output[i], &client_finalize_result.to_vec());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_verifiable_finalize<G: Group, H: BlockInput + Digest>(
|
||||
tvs: &[VOPRFTestVectorParameters],
|
||||
) -> Result<(), InternalError> {
|
||||
for parameters in tvs {
|
||||
let mut clients = vec![];
|
||||
for i in 0..parameters.input.len() {
|
||||
let client = VerifiableClient::<G, H>::from_data_and_blind_and_element(
|
||||
¶meters.input[i],
|
||||
<G as Group>::from_scalar_slice(&GenericArray::clone_from_slice(
|
||||
¶meters.blind[i],
|
||||
))?,
|
||||
<G as Group>::from_element_slice(&GenericArray::clone_from_slice(
|
||||
¶meters.blinded_element[i],
|
||||
))?,
|
||||
);
|
||||
clients.push(client.clone());
|
||||
}
|
||||
|
||||
let messages: Vec<_> = parameters
|
||||
.evaluation_element
|
||||
.iter()
|
||||
.map(|x| EvaluationElement::deserialize(x).unwrap())
|
||||
.collect();
|
||||
|
||||
let batch_result = VerifiableClient::batch_finalize(
|
||||
&clients,
|
||||
&messages,
|
||||
Proof::deserialize(¶meters.proof)?,
|
||||
G::from_element_slice(GenericArray::from_slice(¶meters.pksm))?,
|
||||
Some(¶meters.info),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
parameters.output,
|
||||
batch_result
|
||||
.iter()
|
||||
.map(|arr| arr.to_vec())
|
||||
.collect::<Vec<Vec<u8>>>()
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,637 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
//! The VOPRF test vectors taken from:
|
||||
//! https://github.com/cfrg/draft-irtf-cfrg-voprf/blob/master/draft-irtf-cfrg-voprf.md
|
||||
|
||||
pub(crate) static VECTORS: &str = r#"
|
||||
## OPRF(ristretto255, SHA-512)
|
||||
|
||||
### Base Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
skSm = 74db8e13d2c5148a1181d57cc06debd730da4df1978b72ac18bc48992a0d2
|
||||
c0f
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = c604c785ada70d77a5256ae21767de8c3304115237d262134f5e46e512cf
|
||||
8e03
|
||||
BlindedElement = 744441a5d3ee12571a84d34812443eba2b6521a47265ad655f0
|
||||
1e759b3dd7d35
|
||||
EvaluationElement = 4254c503ee2013262473eec926b109b018d699b8dd954ee8
|
||||
78bc17b159696353
|
||||
Output = 9aef8983b729baacb7ecf1be98d1276ca29e7d62dbf39bc595be018b66b
|
||||
199119f18579a9ae96a39d7d506c9e00f75b433a870d76ba755a3e7196911fff89ff
|
||||
3
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 5ed895206bfc53316d307b23e46ecc6623afb3086da74189a416012be037
|
||||
e50b
|
||||
BlindedElement = f4eeea4e1bcb2ec818ee2d5c1fcec56c24064a9ff4bea5b3dd6
|
||||
877800fc28e4d
|
||||
EvaluationElement = 185dae43b6209dacbc41a62fd4889700d11eeeff4e83ffbc
|
||||
72d54daee7e25659
|
||||
Output = f556e2d83e576b4edc890472572d08f0d90d2ecc52a73b35b2a8416a72f
|
||||
f676549e3a83054fdf4fd16fe03e03bee7bb32cbd83c7ca212ea0d03b8996c2c268b
|
||||
2
|
||||
~~~
|
||||
|
||||
### Verifiable Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
skSm = ad08ad9c7107691d792d346d743e8a79b8f6ae0673d58cbf7389d7003598c
|
||||
903
|
||||
pkSm = 7a5627aec2f2209a2fc62f39f57a8f5ffc4bbfd679d0273e6081b2b621ee3
|
||||
b52
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = ed8366feb6b1d05d1f46acb727061e43aadfafe9c10e5a64e7518d63e326
|
||||
3503
|
||||
BlindedElement = 56c6926e940df23d5dfe6a48949c5a9e5b503df3bff36454ba4
|
||||
821afa1528718
|
||||
EvaluationElement = 523774950001072a4fb1f1f3300f7feb1eeddb5b8304baa9
|
||||
c3d463c11e7f0509
|
||||
Proof = c973c8cfbcdbb12a09e7640e44e45d85d420ed0539a18dc6c67c189b4f28
|
||||
c70dd32f9b13717ee073e1e73333a7cb17545dd42ed8a2008c5dae11a3bd7e70260d
|
||||
ProofRandomScalar = 019cbd1d7420292528f8cdd62f339fdabb602f04a95dac9d
|
||||
bcec831b8c681a09
|
||||
Output = 2d9ed987fdfa623a5b4d5e445b127e86212b7c8f2567c175b424c59602f
|
||||
bba7c36975df5e4ecdf060430c8b1b581fc97e953535fd82089e15afbafcf310b339
|
||||
9
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = e6d0f1d89ad552e383d6c6f4e8598cc3037d6e274d22da3089e7afbd4171
|
||||
ea02
|
||||
BlindedElement = 5cd133d03df2e1ff919ed85501319c2039853dd7dc59da73605
|
||||
fd5791b835d23
|
||||
EvaluationElement = c0ba1012cbfb0338dadb435ef1d910eb179dc18c0d0a341f
|
||||
0249a3a9ff03b06e
|
||||
Proof = 156761aee4eb6a5e1e32bc0adb56ea46d65883777e152d4c607a3a3b8abf
|
||||
3b036ecebae005d3f26222a8da0a3924cceed8a1a7c707ef4ba077456c3e80f8c40f
|
||||
ProofRandomScalar = 74ae06fd50d5f26c2519bd7b184f45dd3ef2cb50197d42df
|
||||
9d013f7d6c312a0b
|
||||
Output = f5da1276b5ca3de4591534cf2d96f7bb49059bd374f40259f42dca89d72
|
||||
3cac69ed3ae567128aaa2dfdf777f333615524aec24bc77b0a38e200e6a07b6c638e
|
||||
b
|
||||
~~~
|
||||
|
||||
#### Test Vector 3, Batch Size 2
|
||||
|
||||
~~~
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 80513e77795feeec6d2c450589b0e1b178febd5c193a9fcba0d27f0a06e0
|
||||
d50f,533c2e6d91c934f919ac218973be55ba0d7b234160a0d4cf3bddafbda99e2e0
|
||||
c
|
||||
BlindedElement = 1c7ee9c1b4145dabeba9ad159531432a20718cb44a86f79dc73
|
||||
f6f8671c9bf5e,7c1ef37881602cb6d3cf995e6ee310ed51e39b80ce0a825a316bc6
|
||||
21d0580a14
|
||||
EvaluationElement = a8a66348d351408cb7e2d26341a1258ba91c1a7d1b380f62
|
||||
15bdfc242500991b,5a4b72bee9d2ca80ea220571690e2f92fadd0c13635b2888bc1
|
||||
ff255f8fee975
|
||||
Proof = caad28bac17ce71d59b43956e8d80f3edde3d0c317144bef3d10d9733ef1
|
||||
cf09fd910c663ea85ad7cfaf641d73314694fe18d3f6b89cfe001b18163ff908d10a
|
||||
ProofRandomScalar = 3af5aec325791592eee4a8860522f8444c8e71ac33af5186
|
||||
a9706137886dce08
|
||||
Output = 2d9ed987fdfa623a5b4d5e445b127e86212b7c8f2567c175b424c59602f
|
||||
bba7c36975df5e4ecdf060430c8b1b581fc97e953535fd82089e15afbafcf310b339
|
||||
9,f5da1276b5ca3de4591534cf2d96f7bb49059bd374f40259f42dca89d723cac69e
|
||||
d3ae567128aaa2dfdf777f333615524aec24bc77b0a38e200e6a07b6c638eb
|
||||
~~~
|
||||
|
||||
## OPRF(decaf448, SHAKE-256)
|
||||
|
||||
### Base Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3
|
||||
skSm = 82c2a6492e1792e6ccdf1d7cff410c717681bd53ad47da7646b14ebd05885
|
||||
53e4c034e02b3ae5e724600a17a638ad528c04f793df56c2618
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = d1080372f0fcf8c5eace50914e7127f576725f215cc7c111673c635ce668
|
||||
bbbb9b50601ad89b358ab8c23ed0b6c9d040365ec9d060868714
|
||||
BlindedElement = 1c354d6d31500c7c5ae6fb10901ac87552ea3af1824e79871e2
|
||||
596ef537f86abac64859cf6f35911ab74f0b09a06ecc757a65a104e9e49fb
|
||||
EvaluationElement = 9e5bbf27b2312a493b2f2f1d051b7cdf3801769ec5dc0724
|
||||
51b68c4d0d4ed9303979ec4798261a01fabd8d25540f48a11dd8342fded95383
|
||||
Output = 5f8c28d5e760786cbd000ac58444bd216141472b9370b058408a714da5e
|
||||
3dd51fc572f96c99a9338bc8569abc991bc1523fa1467cd3a0de3aef7f154bd65d92
|
||||
e
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = aed1ffa44fd8f0ed16373606a3cf7df589cca86d8ea1abbf5768771dbef3
|
||||
d401c74ae55ba1e28b9565e1e4018eb261a14134a4ce60c1c718
|
||||
BlindedElement = e8111f22d50595f68f01a6a9135f50e8702c90794c2637fbe00
|
||||
9046f0c455884cc77ee7a87f3abf494afe780b3620ab0e7fb65c65ba902b2
|
||||
EvaluationElement = 0ec625f99914ba702f0e6bc5d0f837cb4deaf7ab3ac55458
|
||||
7182c3dfe1dad6d1540964f9581d26e8ef0a47b61c5f145109a5fffe04ad528e
|
||||
Output = 7f0e40c08d8220f88c0961925f764ee0e4e08909d497f462a97a2030b40
|
||||
b44986fa76d344efb9b0acab23db81356fc8c380b80701a61a5fa76097a5d2ea7aa9
|
||||
e
|
||||
~~~
|
||||
|
||||
### Verifiable Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3
|
||||
skSm = 5d295b55d1d6e46411bbb4151d154dc61711012ff2390255b3345988f8e3c
|
||||
458089d52e9b1d837049898f9e4e63a4534f0ed3b3a47c7051c
|
||||
pkSm = 8e623ef9b65ef2ce148ce56249ee5e69ed6acd3e504a07905cc4c09312551
|
||||
8d30ae7d6de274438b822d5a55a4365216ac588a4c400fbf6ff
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 4c936db1779a621b6c71475ac3111fd5703a59b713929f36dfd1e892a7fe
|
||||
814479c93d8b4b6e11d1f6fe5351e51457b665fa7b76074e531f
|
||||
BlindedElement = 74bb2406b15a86ba94b0686901545f8ddc23e64918de47c76fa
|
||||
0bf812387021392c73e01068ac9cc07c7647b3d0d4e648c27bb3880ddb8e5
|
||||
EvaluationElement = 90997b495c19f16561a3286a7bcba9a4ee6e12bab4d580d5
|
||||
004ae5064d90a389124e81066f3f1dbf9a729ab46ed674c3292f56d54a0d5641
|
||||
Proof = 668f6ef88b249d51b6c94bfe82f2bec35ab7386bc9f3d14209d0247a5b6e
|
||||
bedec4c333947fff96d322f516f4674cc07638b8e854c52be7045d83d65aff518104
|
||||
60ec43417a6c6efbfb67ba7b0257b1237c64e6792195e338474d09df32b076c0b702
|
||||
ec8c639b34c29878b87aad70d63c
|
||||
ProofRandomScalar = 1b3f5a55b2f18f8c53d4ecf2e1c27e1028f1c345bb504486
|
||||
4aa9dd8439d7520a7ba6183d50ef08bdf6c781aa465660c93e8195a8d231b62f
|
||||
Output = 7db8c49354861f2d71c8175681c9cc930a00251330b2acc5c321f9833fe
|
||||
d4113a1cb3e05a3840082c24e8d49470474dd1c7586f3663f32f66dc3888c63dc0e6
|
||||
e
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 15b3355179392f40c3d5a15f0d5ffc354e340454ec779f575e4573a3886a
|
||||
b5e57e4da2985cea9e32f6d95539ce2c7189e1bd7462e8c5483a
|
||||
BlindedElement = ea3418614d71144ac4ecbd2c63c30ce34718b739ba0a5dd3585
|
||||
efd9800b9debdad4cffc25dcc39b4691aaffba19ead8a425d7d50f016f57e
|
||||
EvaluationElement = 7e12ab491c3787a1f17118f7a0308f8c41f4cd6e850cf7fa
|
||||
ba030b6c1bf1888337149e7c2fc88068626a0107be18e8b9e29f41c8d1510049
|
||||
Proof = 91ed184bf518a155749a99d39bed3f9dc9895054e55fab0ebd0ce4270e84
|
||||
52fcc8da055e8c2f75f2306ecacaa594de592e0d0b059b8eb30e15d5c3132b71ebc4
|
||||
933596c563ee8ce8681e0e40534e92ce487a0e33e341f02a9aaa1f750d9efa7545a0
|
||||
008b2f8dde5047ce68d00c2e962e
|
||||
ProofRandomScalar = 2f2e9955be83a4b25743ebd3618d4fad8b7288477da50bed
|
||||
9befa58af639ddd950fec34205f8a4f166fadcb8fa71a3ffdd2e98f4c8ef5e26
|
||||
Output = 66125718c5d651c88ab57dda67c52a506d436600f1521b7684c869b9a2b
|
||||
3e67d1b41c47593e79fd6b70aaae8d3689536897ae8964ffcd433c0884c12c94929d
|
||||
c
|
||||
~~~
|
||||
|
||||
#### Test Vector 3, Batch Size 2
|
||||
|
||||
~~~
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 614bb578f29cc677ea9e7aea3e4839413997e020f9377b63c13584156a09
|
||||
a46dd2a425c41eac0e313a47e99d05df72c6e1d58e6592577a0d,4c115060bca87db
|
||||
7d73e00cbb8559f84cb7a221b235b0950a0ab553f03f10e1386abe954011b7da62bb
|
||||
6599418ef90b5d4ea98cc28aff517
|
||||
BlindedElement = 909b0b8bcb900bd9e70f27258d7264015c50f3717361afff22d
|
||||
16ad84758d2c6b7a1963263d0d035f63b88df8b473f9365c53abcec34b201,726315
|
||||
ee47e217344da7036a24f806177e221c9f6eae5763f9089b16bada69b85aec56c3ca
|
||||
83b6f5f1091640ea3fe3e9429ff2aa7772efef
|
||||
EvaluationElement = 46d8dec85a27698b4b69a67299eab1da0ec2bbed013a3a59
|
||||
b932e2938e2e2c5bcc8274febf49b7903419c18b895f17c4a9a504737d7a3fdc,fe4
|
||||
7eca9d06b400c80cc2b749284312c6f97c7b5d88055fe56b068c441e053fe909c6c2
|
||||
2bb7cd646a932e2d3838b7b3e2e883cfe0ed1a2a1
|
||||
Proof = 1f63637de4f945f5937ac015a508420f119f7b6a8e001439a1923a1705ce
|
||||
ee704ad17664ff4c72f89566f83ceccee3001d44d849ac4dad2bc05b9bc718ba787f
|
||||
c3c5b09198c4ab244455bac64a9a231b18c4682c0e6e30ae5398f5c041ee2c5b02c6
|
||||
19b7497c5bf070fdb4656353de1d
|
||||
ProofRandomScalar = a614f1894bcf6a1c7cef33909b794fe6e69a642b20f4c911
|
||||
8febffaf6b6a31471fe7794aa77ced123f07e56cc27de60b0ab106c0b8eab127
|
||||
Output = 7db8c49354861f2d71c8175681c9cc930a00251330b2acc5c321f9833fe
|
||||
d4113a1cb3e05a3840082c24e8d49470474dd1c7586f3663f32f66dc3888c63dc0e6
|
||||
e,66125718c5d651c88ab57dda67c52a506d436600f1521b7684c869b9a2b3e67d1b
|
||||
41c47593e79fd6b70aaae8d3689536897ae8964ffcd433c0884c12c94929dc
|
||||
~~~
|
||||
|
||||
## OPRF(P-256, SHA-256)
|
||||
|
||||
### Base Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
skSm = c15d9e9ab36d495d9d62954db6aafe06d3edabf41600d58f9be0737af2719
|
||||
e97
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 5d9e7f6efd3093c32ecceabd57fb03cf760c926d2a7bfa265babf29ec98a
|
||||
f0d0
|
||||
BlindedElement = 03e9097c54d2ea05f99424bdf984ea30ecc3614029bd5f1139e
|
||||
70c4e1ae3bdbd92
|
||||
EvaluationElement = 0202e4d1a338659c211900c39855f30025359928d261e6c9
|
||||
558d667b3fbbc811cd
|
||||
Output = 15b96275d06b85741f491fe0cad5cb835baa6c39066cbea73132dcf95e8
|
||||
58e1c
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 825155ab61f17605af2ae2e935c78d857c9407bcd45128d57d338f1671b5
|
||||
fcbe
|
||||
BlindedElement = 03fa1ea45dd58d6b516c1252f2791610bf5ff1828c93be8af66
|
||||
786f45fb4d14db5
|
||||
EvaluationElement = 02657822553416d91bb3d707040fd0d5a0555f5cbae7519d
|
||||
f3a297747a3ad1dd57
|
||||
Output = e97f3f451f3cfce45a530dec0a0dec934cd78c5b656771549072ee236ce
|
||||
070b9
|
||||
~~~
|
||||
|
||||
### Verifiable Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
skSm = 7f62054fcd598b5e023c08ef0f04e05e26867438d5e355e846c9d8788d5c7
|
||||
a12
|
||||
pkSm = 03d6c3f69cfa683418a533fc52143a377f166e571ae50581abcb97ffd4e71
|
||||
24395
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = cee64d86fd20ab4caa264a26c0e3d42fb773b3173ba76f9588c9b14779bd
|
||||
8d91
|
||||
BlindedElement = 029e103c4003ab9bf4a42e2003dd180922c8517927a68320058
|
||||
178fee56c6ac8a0
|
||||
EvaluationElement = 02856ac0748085d250d842b8b8fff6c1a9f688c961de52c4
|
||||
a1e6c004c48196a123
|
||||
Proof = 2a95bd827cf47873c886967ef6c17fe0e46efddd3b5f639927215cb7592a
|
||||
4bf12a29117174a1af5899d64855352690e416b37f2a95580846a6bec445d82364fc
|
||||
ProofRandomScalar = 70a5204b2b606f5a28328916e1e5ea5a17862d7a261fdd6d
|
||||
959759758d5e34ac
|
||||
Output = 14afc50acf64589445991da5b60add8b3f71205d53a983023d3cdaf8c95
|
||||
c300d
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 5c4b401063eff0bf242b4cd534a79bacfc2e715b2db1e7a3ad4ff8af1b24
|
||||
daa2
|
||||
BlindedElement = 0323aabcfa93e9570524253671b3ce083144b183cecb562ec8f
|
||||
8a8472fc8cf341b
|
||||
EvaluationElement = 03087bc7e00b8ad80b8a27484b91f8bf824a5d896a703135
|
||||
4edfa3269866493d9f
|
||||
Proof = fe55ecc9a92f940d4a56207a58e5554c6976b9425c917d24237b0a35c312
|
||||
bdcdea778a5c56690309ff28f26cc8bc5994e85868e3c870e5a32c0a559d80deccb8
|
||||
ProofRandomScalar = 3b9217801b5d51cef66d9fdbd94a53533e7c5057e09e2200
|
||||
65ea8c257c0dd606
|
||||
Output = 533c79459ee0ffa8844ac37572f3616e10a1074dcbf945ce37b0c651cbb
|
||||
5775f
|
||||
~~~
|
||||
|
||||
#### Test Vector 3, Batch Size 2
|
||||
|
||||
~~~
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = f0c7822ba317fb5e86028c44b92bd3aedcf6744d388ca013ef33edd36930
|
||||
4eda,3b9631be9f8b274d9aaf671bfb6a775229bf435021b89c683259773bc686956
|
||||
b
|
||||
BlindedElement = 021af4563c31cf1513bc5ae0b89c5b527c7ac70614b9d31c44c
|
||||
eb292ab49c91cc4,03f7e7ebe5610710c360df40cbd90dc52c2da500664e879f2afb
|
||||
78e71f815abee1
|
||||
EvaluationElement = 03c8678cdb95e2f0eac027932c51893a20326b774ef23531
|
||||
bcd95def84060d240d,02b68c3891314a9696b5dff5df4b4e5b325938e2c5cb90f5f
|
||||
b9ba6a1133aa4dd14
|
||||
Proof = 6efbde69d36e3f9d53a79a73ce46d5d8ef31f0df2fb3f6f2c882b21fdf0e
|
||||
d76dcd755e42f35f00daaa6e964f48125cf1d642b1cea2e5faa2fb868584a8752bf2
|
||||
ProofRandomScalar = 8306b863276ae74049615162a416d507a6532c99c1ea3f03
|
||||
d05f6e78dc1edabe
|
||||
Output = 14afc50acf64589445991da5b60add8b3f71205d53a983023d3cdaf8c95
|
||||
c300d,533c79459ee0ffa8844ac37572f3616e10a1074dcbf945ce37b0c651cbb577
|
||||
5f
|
||||
~~~
|
||||
|
||||
## OPRF(P-384, SHA-384)
|
||||
|
||||
### Base Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3
|
||||
skSm = b9ff42e68ef6f8eaa3b4d15d15ceb6f3f36b9dc332a3473d64840fc7b4462
|
||||
6c6e70336bdecbe01d9c512b7e7d7e6af21
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 359073c015b92d15450f7fb395bf52c6ea98384c491fe4e4d423b59de7b0
|
||||
df382902c13bdc9993d3717bda68fc080b99
|
||||
BlindedElement = 0285d803c65fda56993a296b99e8f4944e45cccb9b322bbc265
|
||||
c91a21d2c9cd146212aefbf3126ed59d84c32d6ab823b66
|
||||
EvaluationElement = 026061a4ccfe38777e725855c96570fe85303cd70567007e
|
||||
489d0aa8bfced0e47579ecbc290e5150b9e84bf25188294f7e
|
||||
Output = bc2c3c895f96d769703aec18359cbc0e84b41248559f0bd44f1e5467522
|
||||
3c77e00874bbe61c1c320d3c95aee5a8c752f
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 21ece4f9b6ffd01ce82082545413bd9bb5e8f3c63b86ae88d9ce0530b01c
|
||||
b1c23382c7ec9bdd6e75898e4877d8e2bc17
|
||||
BlindedElement = 0211dd06e40b902006c33a92dc476a7c708b6b46c990656239c
|
||||
d6867ff0be5867d859517eaf7ea9bad10702b80a9dc6bdc
|
||||
EvaluationElement = 03a1d34b657f6267b29338592e3c769db5d3fc8713bf2eb7
|
||||
238efb8138d5af8c56f9437315a5c58761b35cbfc0e1d2511d
|
||||
Output = ee37530d0d7b20635fbc476317343b257750ffb3e83a2865ce2a46e5959
|
||||
1f854b8301d6ca7d063322314a33b953c8bd5
|
||||
~~~
|
||||
|
||||
### Verifiable Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3
|
||||
skSm = 42c4d1c15d27be015844404088967afe48c8ae96d4f00ce48e4d38ecabfb8
|
||||
feb5b748de625cdf81ab076745d6211be95
|
||||
pkSm = 0389ad5e50eebf9617ae3a5778e4f0665b56aa9066919e1fa5580d8dd781d
|
||||
560824e0e78aae816af6eff8abe2ad0585a0d
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 102f6338df84c9602bfa9e7d690b1f7a173d07e6d54a419db4a6308f8b09
|
||||
589e4283efb9cd1ee4061c6bf884e60a8774
|
||||
BlindedElement = 02ae8990d580dcd52b6bc273bc6d0fd25be50b057511b953d9c
|
||||
c95bb27cb3e1fd3249ae19744ed496c6e4104ebc1ed48f1
|
||||
EvaluationElement = 024cffdae0cae5fa4d6a68246ae797dbe06508284b65e0f0
|
||||
9046977ab5d52a8b38f0245607db74979e5276fc636332cdee
|
||||
Proof = 128ad4f987ce1e3a9aab1e487df15d8c8000d5c4c9f14bd7fd699fabdb8d
|
||||
a3f577d91625fabb0d9cf6069f8af6d9cc232dd63cd161be84a1e146e0110dc741e6
|
||||
26a082193aa0a26e03118b662f1b903667f6e6fba51d69a2d65982a3b64ecb35
|
||||
ProofRandomScalar = 90f67cafc0ffaa7a1e1d1ced3c477fea691e696032c8709c
|
||||
86cbcda2b184ad0029d29abeabede9788d11782429bff297
|
||||
Output = 8a0b4829bc8422b1a2301d5471256892883c5e3fe27b998d1010225a706
|
||||
545637336a20a76f842d8a22e591d382c77e4
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 8aec1d0c3d16afd032da7ba961449a56cec6fb918e932b06d5778ac7f67b
|
||||
ecfb3e3869237f74106241777f230582e84a
|
||||
BlindedElement = 02a384f2d9635adffcc5482344c519036c019f3cc0918ec737c
|
||||
67cdda10ac0f73a9fe348835531f1900ea2c1f06dacdce4
|
||||
EvaluationElement = 0306f0f71b58d53ae0973538a7bf2ce8fba7143efc88d2ef
|
||||
ca6cf1f98fb8399b16840d1fbbe7897807db930f67916418ae
|
||||
Proof = 2c47297ee0093061ca2c87b430b2851a860aaae76c2bdba48779ba4294e7
|
||||
de0556ede3e6b881a04970b68a6126e2fa197d69e6784fbbd173604501c0edd21696
|
||||
628f0fd7cb13be28f94e5e15c042ffccadd780b2448d7d9d528e9615e4e70539
|
||||
ProofRandomScalar = bb1876a7f7165ac7ec79bfd5213ea2e374252f29a6e19915
|
||||
f81b0c7dcea93ce6580e089ede31c1b6b5b33494581b4868
|
||||
Output = 8c52d40c1f6cc80208bd610178a5034d6c4a05584e19b69617f846b09a8
|
||||
545443c63c8aa4d85bf0aad368e0591b1216a
|
||||
~~~
|
||||
|
||||
#### Test Vector 3, Batch Size 2
|
||||
|
||||
~~~
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 41fabd4722d92472d858051ce9ad1a533176a862c697b2c392aff2aeb77e
|
||||
b20c2ae6ba52fe31e13e03bf1d9f39878b23,51171628f1d28bb7402ca4aea6465e2
|
||||
67b7f977a1fb71593281099ef2625644aee0b6c5f5e6e01a2b052b3bd4caf539b
|
||||
BlindedElement = 02d4e6186c9ffa92565055f43f27bb1e2c4103c3325bba0b499
|
||||
adb99a157987d20fb374096814e438a6b483efa8f2a3307,033a3b052416a8a6d842
|
||||
a0baea6f5fab99d36645a70c89897a536970d34038eca35afac24906294cb7925b1b
|
||||
05e4327c8f
|
||||
EvaluationElement = 037bf8e28a0607b1f8aa59363380b5a7450b66b98017cf03
|
||||
3797f6c6c74e7625a445f71ace1bea7836ea5baa75d54eb5bd,03b793a9cb2d76991
|
||||
f1d6cd822abfbfa89fdfa1a06ef42b0bc8ade161e1996ed08c288a08366d4140c762
|
||||
7bba4e3472bcf
|
||||
Proof = 27240901b6855d2b58ce84afefa91dd11819d7d5df73f94865a9d7e19020
|
||||
41200eb732b60b57fa0daf6e456402bb1ccb1aed901af35d3d790cd7c618604b766b
|
||||
b9271010354da9e4e5507e0468adf177977143db2ddb94d9b70e837ad7578275
|
||||
ProofRandomScalar = 1b538ff23749be19e92df82df1acd3f606cc9faa9dc7ab25
|
||||
1997738a3a232f352c2059c25684e6ccea420f8d0c793fa0
|
||||
Output = 8a0b4829bc8422b1a2301d5471256892883c5e3fe27b998d1010225a706
|
||||
545637336a20a76f842d8a22e591d382c77e4,8c52d40c1f6cc80208bd610178a503
|
||||
4d6c4a05584e19b69617f846b09a8545443c63c8aa4d85bf0aad368e0591b1216a
|
||||
~~~
|
||||
|
||||
## OPRF(P-521, SHA-512)
|
||||
|
||||
### Base Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
skSm = 00a2f8572ee764d2ec34363fb62ef9e8ff48883b5357b6802f43fffe5c5fd
|
||||
0d11f766bf7086aab33e2dce02cc71d77250ef6ed360a3fd56244abb6bdbc3aa6534
|
||||
da1
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 01b983705fcc9a39607288b935b0797ac6b3c4b2e848823ac9ae16b3a3b5
|
||||
816be03432370deb7c3c17d9fc7cb4e0ce646e04e42d638e0fa7a434ed340772a8b5
|
||||
d626
|
||||
BlindedElement = 03006ce4a27e778a624d943cf4db48f9d393d3d4dd9cd44b78a
|
||||
cf2d5b668a12f0ca587962de8c82b5aaa1f0166eb60d511f060aaab895fc6c519332
|
||||
77bc945add6d74a
|
||||
EvaluationElement = 030055f7cd3ee3b1734e73ad8bbd4baca72ae8d051160c27
|
||||
7ee329f23fa2365f9f138b38e6e2c59cc287242eeca01fae83d0c7cc3bb19724ac59
|
||||
8a188816e7cfe1ca88
|
||||
Output = aa59060a41ec8ca7b6c47f9c5a31883a44ffd95869a09dbe845ea8ce20c
|
||||
b290dba0b57c505824a0dcf6f961a2baeb8e6b49df8c158761a3fdb46f39e8e7fcb8
|
||||
b
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 01a03b1096b0316bc8567c89bd70267d35c8ddcb2be2cdc867089a2eb5cf
|
||||
471b1e6eb4b043b9644c8539857abe3a2022e9c9fd6a1695bbabe8add48bcd149ff3
|
||||
b841
|
||||
BlindedElement = 0201459ba64ad0e0f9f689f0ad5ab29ca5b960f5c9da3aef412
|
||||
6d2d547b871e754b17971fd45e0d64bdcfc8d256c342a141f04e2640705c38936c8c
|
||||
f53c22ea6b13966
|
||||
EvaluationElement = 030094036457e8e5bf77719b11f01dd4aa2959efdb3329c3
|
||||
e3b25493efc3ab572c2e7db104cd5922645320ef51bbb282f84e5f6b08e9b49354f9
|
||||
d6a9f3a4327a1de6e4
|
||||
Output = 5efe6f00f45ec4e87e4c9b89aeaec61313c15c0a0a21ee2e41362d6af54
|
||||
536adf2f68d23c729b92b6fa8d5611764b0272be6cc153d47a0256c8cb44bd740037
|
||||
a
|
||||
~~~
|
||||
|
||||
### Verifiable Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
skSm = 0064799c2f9c0f9e6b9ac2aca5c42687cf15742fb73e086c4954aa0bdc8b8
|
||||
25911ff03712e8d308c0a6ff5435375036f189391234bf21aac57fa73df155d70da4
|
||||
7bd
|
||||
pkSm = 03013e587a7750213bb7c2b338a4507635f1ba60ece346de32ad975373e56
|
||||
fbabd878f9956996aac83a550ed5f5ba98fcc56817f6230cc7e84cb7eb2a1e1db51d
|
||||
bfc1b
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 00bbb82117c88bbd91b8954e16c0b9ceed3ce992b198be1ebfba9ba970db
|
||||
d75beefbfc6d056b7f7ba1ef79f4facbf2d912c26ce2ecc5bb8d66419b379952e96b
|
||||
d6f5
|
||||
BlindedElement = 02002ff3ef3f2411aa0358936f852be710af790c9affbced8c3
|
||||
9b018fd97de0a45d80c66cbf0dbda690ee4f594e0795627e6c6f37a500f223c30f31
|
||||
c24e73501532e7c
|
||||
EvaluationElement = 0300769fd56c5174c4e3922900fcefdd5a89c9592f4d8e8f
|
||||
2396678fa72c01d4f8551ec92d4b5287ca673dc29d8db9bb05d2396121a6b8732b68
|
||||
ebf310fc2620059d67
|
||||
Proof = 011fd92f54f6a955a333648d843807bd88f644d235a7d592189da42d721e
|
||||
a6f7b55ec813146f35982487910aa15bbf5ce90653edb6a1b48c0bfd15758e9358aa
|
||||
731601baa67a3a59db301f41caa020986ae9e93a80d6c06d92e8c5eef6056fa6f342
|
||||
6b6054d118dc9fecb77fdcb4fc86b9857ada6de18394ff7d6c574cbd08d746b9dde0
|
||||
ProofRandomScalar = 00ce4f0d824939827888f4c28773466f3c0a05741260040b
|
||||
c9f302a4fea13f1d8f2f6b92a02a32d5eb06f81de7960470f06169bee12cf47965b7
|
||||
2a59946ca3879670
|
||||
Output = a647c5a940aa19d767ab0e163d1357ca068206b2b78f9e8e1021c0bb0f3
|
||||
27d20cb8fadf996199d86d4cc0a08ac314493319979e1c2a98a96085b8fabff9f0d0
|
||||
7
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 009055c99bf9591cb0eab2a72d044c05ca2cc2ef9b609a38546f74b6d688
|
||||
f70cf205f782fa11a0d61b2f5a8a2a1143368327f3077c68a1545e9aafbba6a90dc0
|
||||
d40a
|
||||
BlindedElement = 0301e2ecf7313820e9d47763e12633ce6acf9b3dec89928c83b
|
||||
de1ede2180dc73553af1317408846af5c53ebfed00d19a4125f4ffb7df9f4260ccc0
|
||||
84a6f7482414a9d
|
||||
EvaluationElement = 02000e69591ab605652cb3310e774edf79417e102cf89005
|
||||
c2c7f2bd3a06060d740817802f2cf484748d93df5b281a4bd835617a97ec9809519d
|
||||
474ca53bba15cdf014
|
||||
Proof = 0076fa4275414acb9f87dc9e4f20971d51fcd0d38a980854ac2ad1bd5737
|
||||
eec23bfb4599d021881f7b3872d2e90d9b47e4219f490cf7f0235b2f0859cb2ef15d
|
||||
dfd401acb6b0844edf066a5767b4b85536bfee69bdf472acf7a59254cf6578f9f35e
|
||||
ba51bb58c6428d6b7c9e5c9af97edc66d98886fda9544048bf9ceea6fc745bf970da
|
||||
ProofRandomScalar = 00b5dfc19eb96faba6382ec845097904db87240b9dd47b1e
|
||||
487ec625f11a7ba2cc3de74c5078a81806f74dd65065273c5bd886c7f87ff8c5f39f
|
||||
90320718eff747e3
|
||||
Output = 8d109503ccced41cbec087dab86c607763020be93bdd5ec8508cb078607
|
||||
1a2b22a7b06150242bcaf6ea1b555a994e0266647eb72914caf73cabe53ddfb0f940
|
||||
d
|
||||
~~~
|
||||
|
||||
#### Test Vector 3, Batch Size 2
|
||||
|
||||
~~~
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 01c6cf092d80c7cf2cb55388d899515238094c800bdd9c65f71780ba85f5
|
||||
ae9b4703e17e559ca3ccd1944f9a70536c175f11a827452672b60d4e9f89eba28104
|
||||
6e29,00cba1ba1a337759061965a423d9d3d6e1e1006dc8984ad28a4c93ecfc36fc2
|
||||
171046b3c4284855cfa2434ed98db9e68a597db2c14728fade716a6a82d600444b26
|
||||
e
|
||||
BlindedElement = 0201e22c01df5ac0502842fad603f7a1e1183bcc79a5cb04bb7
|
||||
befdea870a9a6ea96fbccd752ea9927a9e1e28438098f693461e81832a3f690616bf
|
||||
983fced079f3a33,0300b49216dd8ba5ba1275d8345679f70fbc6baf4f4b32a03e91
|
||||
7165a18afa9fad849c48eecb4bae965057ef7c215b52b42ca53c8d5f650633e0bb70
|
||||
97f2bd809d09ea
|
||||
EvaluationElement = 03002949c2478249b918a0cf2cd870226541a81d2f3e88c4
|
||||
7119f732301e749c3dea317c11174a18b89d1b9d2aa4f6ae92ae724e03a4800a26b7
|
||||
c827b00199f1114bcd,0300924ab017ea6e6328a0b0f341bbeb7d209c67ac169fa4e
|
||||
f7b04055c66b92aa9657f5d83b0b1ee9c79f3f0198519c97fef07dbecf3f6d477755
|
||||
0242a1c87953f9461
|
||||
Proof = 01f5d3c3f835d91aa88202f0fe8728180eeffe7fbc66ffe3f7a7dd958696
|
||||
a7cd3d47b3c0ec6cd59e9ee23090137293e6f42269923f3d4a1659bc706fd9762070
|
||||
7d230028cd4b0aa237b91a352fce81248936826ba99e7bd5103a871715126014b8d4
|
||||
7447e5f20192ed377a7431516fbd82763098ba23f9d15b84fe24fb1126beb0d46f03
|
||||
ProofRandomScalar = 00d47b0d4ca4c64825ba085de242042b84d9ebe3b2e9de07
|
||||
678ff96713dfe16f40f2c662a56ed2db95e1e7bf2dea02bd1fa76e953a630772f68b
|
||||
53baade9962d1646
|
||||
Output = a647c5a940aa19d767ab0e163d1357ca068206b2b78f9e8e1021c0bb0f3
|
||||
27d20cb8fadf996199d86d4cc0a08ac314493319979e1c2a98a96085b8fabff9f0d0
|
||||
7,8d109503ccced41cbec087dab86c607763020be93bdd5ec8508cb0786071a2b22a
|
||||
7b06150242bcaf6ea1b555a994e0266647eb72914caf73cabe53ddfb0f940d
|
||||
~~~
|
||||
"#;
|
||||
-180
@@ -1,180 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
//! Helper functions
|
||||
|
||||
use crate::errors::InternalError;
|
||||
use core::array::IntoIter;
|
||||
use generic_array::{typenum::U0, ArrayLength, GenericArray};
|
||||
|
||||
// Corresponds to the I2OSP() function from RFC8017
|
||||
pub(crate) fn i2osp<L: ArrayLength<u8>>(
|
||||
input: usize,
|
||||
) -> Result<GenericArray<u8, L>, InternalError> {
|
||||
const SIZEOF_USIZE: usize = core::mem::size_of::<usize>();
|
||||
|
||||
// Check if input >= 256^length
|
||||
if (SIZEOF_USIZE as u32 - input.leading_zeros() / 8) > L::U32 {
|
||||
return Err(InternalError::SerializationError);
|
||||
}
|
||||
|
||||
if L::USIZE <= SIZEOF_USIZE {
|
||||
return Ok(GenericArray::clone_from_slice(
|
||||
&input.to_be_bytes()[SIZEOF_USIZE - L::USIZE..],
|
||||
));
|
||||
}
|
||||
|
||||
let mut output = GenericArray::default();
|
||||
output[L::USIZE - SIZEOF_USIZE..L::USIZE].copy_from_slice(&input.to_be_bytes());
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// Simplifies handling of [`serialize()`] output and implements [`Iterator`].
|
||||
pub(crate) struct Serialized<'a, L1: ArrayLength<u8>, L2: ArrayLength<u8>> {
|
||||
octet: GenericArray<u8, L1>,
|
||||
input: Input<'a, L2>,
|
||||
}
|
||||
|
||||
enum Input<'a, L: ArrayLength<u8>> {
|
||||
Owned(GenericArray<u8, L>),
|
||||
Borrowed(&'a [u8]),
|
||||
}
|
||||
|
||||
impl<'a, L1: ArrayLength<u8>, L2: ArrayLength<u8>> IntoIterator for &'a Serialized<'a, L1, L2> {
|
||||
type Item = &'a [u8];
|
||||
|
||||
type IntoIter = IntoIter<&'a [u8], 2>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
IntoIter::new([
|
||||
&self.octet,
|
||||
match self.input {
|
||||
Input::Owned(ref bytes) => bytes,
|
||||
Input::Borrowed(bytes) => bytes,
|
||||
},
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
// Computes I2OSP(len(input), max_bytes) || input
|
||||
pub(crate) fn serialize<L: ArrayLength<u8>>(
|
||||
input: &[u8],
|
||||
) -> Result<Serialized<L, U0>, InternalError> {
|
||||
Ok(Serialized {
|
||||
octet: i2osp::<L>(input.len())?,
|
||||
input: Input::Borrowed(input),
|
||||
})
|
||||
}
|
||||
|
||||
// Variation of `serialize` that takes an owned `input`
|
||||
pub(crate) fn serialize_owned<L1: ArrayLength<u8>, L2: ArrayLength<u8>>(
|
||||
input: GenericArray<u8, L2>,
|
||||
) -> Result<Serialized<'static, L1, L2>, InternalError> {
|
||||
Ok(Serialized {
|
||||
octet: i2osp::<L1>(input.len())?,
|
||||
input: Input::Owned(input),
|
||||
})
|
||||
}
|
||||
|
||||
macro_rules! chain_name {
|
||||
($var:ident, $mod:ident) => {
|
||||
$mod
|
||||
};
|
||||
($var:ident) => {
|
||||
$var
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! chain_skip {
|
||||
($var:ident, $feed:expr) => {
|
||||
$feed
|
||||
};
|
||||
($var:ident) => {
|
||||
&$var
|
||||
};
|
||||
}
|
||||
|
||||
/// The purpose of this macro is to simplify [`concat`](alloc::slice::Concat::concat)ing
|
||||
/// slices into an [`Iterator`] to avoid allocation
|
||||
macro_rules! chain {
|
||||
(
|
||||
$var:ident,
|
||||
$item1:expr $(=> |$mod1:ident| $feed1:expr)?,
|
||||
$($item2:expr $(=> |$mod2:ident| $feed2:expr)?),+$(,)?
|
||||
) => {
|
||||
let chain_name!(__temp$(, $mod1)?) = $item1;
|
||||
let $var = (chain_skip!(__temp$(, $feed1)?)).into_iter();
|
||||
$(
|
||||
let chain_name!(__temp$(, $mod2)?) = $item2;
|
||||
let $var = $var.chain(chain_skip!(__temp$(, $feed2)?));
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_tests {
|
||||
use super::*;
|
||||
use crate::voprf::{
|
||||
BlindedElement, EvaluationElement, NonVerifiableClient, NonVerifiableServer, Proof,
|
||||
VerifiableClient, VerifiableServer,
|
||||
};
|
||||
use curve25519_dalek::ristretto::RistrettoPoint;
|
||||
use generic_array::typenum::{U1, U2};
|
||||
use proptest::{collection::vec, prelude::*};
|
||||
use sha2::Sha512;
|
||||
|
||||
// Test the error condition for I2OSP
|
||||
#[test]
|
||||
fn test_i2osp_err_check() {
|
||||
assert!(i2osp::<U1>(0).is_ok());
|
||||
|
||||
assert!(i2osp::<U1>(255).is_ok());
|
||||
assert!(i2osp::<U1>(256).is_err());
|
||||
assert!(i2osp::<U1>(257).is_err());
|
||||
|
||||
assert!(i2osp::<U2>(256 * 256 - 1).is_ok());
|
||||
assert!(i2osp::<U2>(256 * 256).is_err());
|
||||
assert!(i2osp::<U2>(256 * 256 + 1).is_err());
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_nocrash_nonverifiable_client(bytes in vec(any::<u8>(), 0..200)) {
|
||||
NonVerifiableClient::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_verifiable_client(bytes in vec(any::<u8>(), 0..200)) {
|
||||
VerifiableClient::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_nonverifiable_server(bytes in vec(any::<u8>(), 0..200)) {
|
||||
NonVerifiableServer::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_verifiable_server(bytes in vec(any::<u8>(), 0..200)) {
|
||||
VerifiableServer::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_blinded_element(bytes in vec(any::<u8>(), 0..200)) {
|
||||
BlindedElement::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_evaluation_element(bytes in vec(any::<u8>(), 0..200)) {
|
||||
EvaluationElement::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_proof(bytes in vec(any::<u8>(), 0..200)) {
|
||||
Proof::<RistrettoPoint, Sha512>::deserialize(&bytes[..]).map_or(true, |_| true);
|
||||
}
|
||||
}
|
||||
}
|
||||
+503
-999
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
[formatting]
|
||||
allowed_blank_lines = 1
|
||||
reorder_keys = true
|
||||
Reference in New Issue
Block a user