Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a30d5b7fa8 | ||
|
|
8f20115075 |
@@ -1,102 +0,0 @@
|
||||
name: Rust CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ gitea.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
fmt:
|
||||
name: cargo fmt
|
||||
runs-on: linux_amd64
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
- name: Run cargo fmt
|
||||
run: cargo fmt --all -- --check
|
||||
|
||||
clippy:
|
||||
name: cargo clippy
|
||||
runs-on: linux_amd64
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy
|
||||
- name: Run cargo clippy
|
||||
run: cargo clippy --all-features --all-targets -- -D warnings
|
||||
- name: Run cargo doc
|
||||
run: cargo doc --no-deps --document-private-items --features danger,std
|
||||
env:
|
||||
RUSTDOCFLAGS: -D warnings
|
||||
|
||||
test:
|
||||
name: test (${{ matrix.toolchain }} / ${{ matrix.backend_feature || 'no backend' }} / ${{ matrix.frontend_feature || 'no frontend' }})
|
||||
runs-on: linux_amd64
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
backend_feature:
|
||||
- --features ristretto255-ciphersuite
|
||||
- ""
|
||||
frontend_feature:
|
||||
- ""
|
||||
- --features danger
|
||||
- --features serde
|
||||
toolchain:
|
||||
- stable
|
||||
- "1.87.0"
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
- uses: dtolnay/rust-toolchain@${{ matrix.toolchain }}
|
||||
- name: Run cargo test
|
||||
run: cargo test --no-default-features --lib --tests ${{ matrix.backend_feature }}
|
||||
- name: Run cargo test with alloc
|
||||
run: cargo test --no-default-features --lib --tests ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }} --features alloc
|
||||
- name: Run cargo test with std
|
||||
run: cargo test --no-default-features --lib --tests ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }} --features std
|
||||
- name: Run cargo test with all features
|
||||
run: cargo test --all-features
|
||||
|
||||
build-no-std:
|
||||
name: no-std (${{ matrix.target }} / ${{ matrix.backend_feature || 'no backend' }})
|
||||
runs-on: linux_amd64
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target:
|
||||
- wasm32-unknown-unknown
|
||||
- thumbv6m-none-eabi
|
||||
backend_feature:
|
||||
- ""
|
||||
- --features ristretto255-ciphersuite
|
||||
frontend_feature:
|
||||
- ""
|
||||
- --features danger
|
||||
- --features serde
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
- name: Build no-std
|
||||
run: cargo build --verbose --target=${{ matrix.target }} --no-default-features ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }}
|
||||
|
||||
audit:
|
||||
name: cargo audit
|
||||
runs-on: linux_amd64
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Install cargo-audit
|
||||
run: cargo install cargo-audit
|
||||
- name: Run cargo audit
|
||||
run: cargo audit -D warnings
|
||||
@@ -1,26 +0,0 @@
|
||||
name: Publish
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [ published ]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: linux_amd64
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Login to crates.io
|
||||
run: cargo login $CRATES_IO_TOKEN
|
||||
env:
|
||||
CRATES_IO_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
|
||||
|
||||
- name: Dry run publish
|
||||
run: cargo publish --dry-run --manifest-path Cargo.toml
|
||||
|
||||
- name: Publish
|
||||
run: cargo publish --manifest-path Cargo.toml
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
|
||||
@@ -0,0 +1,12 @@
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
- package-ecosystem: cargo
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
@@ -0,0 +1,180 @@
|
||||
name: Rust CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v0.4
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
|
||||
jobs:
|
||||
cargo-audit:
|
||||
name: Audit
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Cache cargo-audit
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/.crates.toml
|
||||
~/.cargo/.crates2.json
|
||||
~/.cargo/bin/cargo-audit
|
||||
key: cargo-audit
|
||||
|
||||
- name: Install cargo-audit
|
||||
run: cargo install cargo-audit
|
||||
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run cargo audit
|
||||
run: cargo audit -D warnings
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
backend_feature:
|
||||
- --features ristretto255-ciphersuite
|
||||
-
|
||||
frontend_feature:
|
||||
-
|
||||
- --features danger
|
||||
- --features serde
|
||||
toolchain:
|
||||
- stable
|
||||
- 1.65.0
|
||||
name: test
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- 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 ${{ matrix.backend_feature }}
|
||||
|
||||
- name: Run cargo test with alloc
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --no-default-features ${{ matrix.frontend_feature }},alloc ${{ matrix.backend_feature }}
|
||||
|
||||
- name: Run cargo test with std
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --no-default-features ${{ matrix.frontend_feature }},std ${{ matrix.backend_feature }}
|
||||
|
||||
- name: Run cargo test with all features enabled
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --all-features
|
||||
|
||||
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-ciphersuite
|
||||
frontend_feature:
|
||||
-
|
||||
- --features danger
|
||||
- --features serde
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: hecrj/setup-rust-action@v2
|
||||
- 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@v4
|
||||
|
||||
- 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-features --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 danger,std
|
||||
|
||||
|
||||
rustfmt:
|
||||
name: cargo fmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install nightly toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly
|
||||
override: true
|
||||
components: rustfmt
|
||||
|
||||
- name: Run cargo fmt
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
taplo:
|
||||
name: Taplo
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/.crates.toml
|
||||
~/.cargo/.crates2.json
|
||||
~/.cargo/bin/taplo
|
||||
key: taplo
|
||||
|
||||
- name: Install Taplo
|
||||
run: cargo install taplo-cli --locked
|
||||
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run Taplo
|
||||
run: taplo fmt --check
|
||||
@@ -0,0 +1,29 @@
|
||||
name: Publish
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
rust: [stable]
|
||||
|
||||
steps:
|
||||
- uses: hecrj/setup-rust-action@v2
|
||||
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 }}
|
||||
@@ -8,3 +8,7 @@ Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# Editors
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
+4
-73
@@ -1,80 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## 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.1 (September 24, 2024)
|
||||
* Backport all non-protocol-breaking changes from versions 0.5+
|
||||
* Fixes Rust 1.81+ compatibility, compatible with 0.4.0 (draft 11), incompatible with 0.5+ (final RFC)
|
||||
* Updated dependencies
|
||||
|
||||
## 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
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# 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
|
||||
+20
-1
@@ -2,11 +2,30 @@
|
||||
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-vx, you agree that your contributions will be
|
||||
By contributing to voprf, you agree that your contributions will be
|
||||
licensed under both the LICENSE-MIT and LICENSE-APACHE files in the root
|
||||
directory of this source tree.
|
||||
|
||||
+39
-37
@@ -1,67 +1,69 @@
|
||||
[package]
|
||||
authors = ["VexaHub Developers", "Kevin Lewi <[email protected]>"]
|
||||
authors = ["Kevin Lewi <[email protected]>"]
|
||||
categories = ["no-std", "algorithms", "cryptography"]
|
||||
description = "An implementation of a verifiable oblivious pseudorandom function (VOPRF)"
|
||||
edition = "2024"
|
||||
keywords = ["oprf", "voprf", "cryptography", "oblivious-prf"]
|
||||
edition = "2021"
|
||||
keywords = ["oprf"]
|
||||
license = "MIT"
|
||||
name = "voprf-vx"
|
||||
name = "voprf"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/vexahub/voprf-vx/"
|
||||
rust-version = "1.87"
|
||||
version = "1.0.0-pre.1"
|
||||
repository = "https://github.com/facebook/voprf/"
|
||||
rust-version = "1.65"
|
||||
version = "0.4.1"
|
||||
|
||||
[features]
|
||||
alloc = []
|
||||
danger = []
|
||||
doctest = ["dep:p256", "dep:sha2"]
|
||||
default = ["ristretto255-ciphersuite", "dep:serde"]
|
||||
ristretto255 = ["dep:curve25519-dalek"]
|
||||
ristretto255 = ["dep:curve25519-dalek", "generic-array/more_lengths"]
|
||||
ristretto255-ciphersuite = ["ristretto255", "dep:sha2"]
|
||||
serde = ["curve25519-dalek?/serde", "hybrid-array/serde", "dep:serde"]
|
||||
serde = ["generic-array/serde", "dep:serde"]
|
||||
std = ["alloc"]
|
||||
|
||||
[dependencies]
|
||||
curve25519-dalek = { version = "5.0.0-rc", default-features = false, features = ["rand_core", "zeroize"], optional = true }
|
||||
derive-where = { version = "1", features = ["zeroize-on-drop"] }
|
||||
digest = "0.11"
|
||||
displaydoc = { version = "0.2", default-features = false }
|
||||
elliptic-curve = { version = "0.14", features = [
|
||||
"sec1",
|
||||
] }
|
||||
hash2curve = "0.14"
|
||||
hybrid-array = "0.4"
|
||||
rand_core = { version = "0.10", default-features = false, features = [] }
|
||||
serde = { version = "1", default-features = false, features = [
|
||||
"derive",
|
||||
curve25519-dalek = { version = "4", default-features = false, features = [
|
||||
"rand_core",
|
||||
"zeroize",
|
||||
], optional = true }
|
||||
sha2 = { version = "0.11", default-features = false, optional = true }
|
||||
p256 = { version = "0.14.0-rc", default-features = false, features = ["hash2curve", "oprf"], optional = true }
|
||||
derive-where = { version = "1", features = ["zeroize-on-drop"] }
|
||||
digest = "0.10"
|
||||
displaydoc = { version = "0.2", default-features = false }
|
||||
elliptic-curve = { version = "0.13", features = [
|
||||
"hash2curve",
|
||||
"sec1",
|
||||
"voprf",
|
||||
] }
|
||||
generic-array = "0.14"
|
||||
rand_core = { version = "0.6", default-features = false }
|
||||
serde = { version = "1", default-features = false, features = [
|
||||
"derive",
|
||||
], optional = true }
|
||||
sha2 = { version = "0.10", default-features = false, optional = true }
|
||||
subtle = { version = "2.6", default-features = false }
|
||||
zeroize = { version = "1.5", default-features = false }
|
||||
zeroize = { version = "1.8", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
generic-array = { version = "0.14", features = ["more_lengths"] }
|
||||
hex = "0.4"
|
||||
p256 = { version = "0.14.0-rc", default-features = false, features = [
|
||||
"hash2curve",
|
||||
"oprf",
|
||||
p256 = { version = "0.13", default-features = false, features = [
|
||||
"hash2curve",
|
||||
"voprf",
|
||||
] }
|
||||
p384 = { version = "0.14.0-rc", default-features = false, features = [
|
||||
"hash2curve",
|
||||
"oprf",
|
||||
p384 = { version = "0.13", default-features = false, features = [
|
||||
"hash2curve",
|
||||
"voprf",
|
||||
] }
|
||||
p521 = { version = "0.14.0-rc", default-features = false, features = [
|
||||
"hash2curve",
|
||||
"oprf",
|
||||
p521 = { version = "0.13.3", default-features = false, features = [
|
||||
"hash2curve",
|
||||
"voprf",
|
||||
] }
|
||||
proptest = "1"
|
||||
rand = "0.10"
|
||||
rand = "0.8"
|
||||
regex = "1"
|
||||
serde_json = "1"
|
||||
sha2 = "0.11"
|
||||
sha2 = "0.10"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
targets = []
|
||||
features = ["doctest"]
|
||||
|
||||
@@ -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 [RFC 9497](https://www.rfc-editor.org/rfc/rfc9497).
|
||||
This implementation is based on the [Internet Draft for VOPRF](https://github.com/cfrg/draft-irtf-cfrg-voprf).
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
The API can be found [here](https://docs.rs/voprf-vx/) along with an example for usage.
|
||||
The API can be found [here](https://docs.rs/voprf/) along with an example for usage.
|
||||
|
||||
Installation
|
||||
------------
|
||||
@@ -16,19 +16,17 @@ Installation
|
||||
Add the following line to the dependencies of your `Cargo.toml`:
|
||||
|
||||
```
|
||||
voprf = { package = "voprf-vx", version = "1.0.0-pre.0" }
|
||||
voprf = "0.4.1"
|
||||
```
|
||||
|
||||
### Minimum Supported Rust Version
|
||||
|
||||
Rust **1.87** or higher.
|
||||
Rust **1.65** or higher.
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
This is a fork of [facebook/voprf](https://github.com/facebook/voprf/) maintained by [VexaHub](https://github.com/vexahub).
|
||||
|
||||
The original author is Kevin Lewi ([@kevinlewi](https://github.com/kevinlewi)).
|
||||
The author of this code is Kevin Lewi ([@kevinlewi](https://github.com/kevinlewi)).
|
||||
To learn more about contributing to this project, [see this document](./CONTRIBUTING.md).
|
||||
|
||||
License
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended"
|
||||
],
|
||||
"dependencyDashboard": true,
|
||||
"osvVulnerabilityAlerts": true,
|
||||
"rangeStrategy": "auto",
|
||||
"packageRules": [
|
||||
{
|
||||
"matchManagers": [
|
||||
"cargo"
|
||||
],
|
||||
"groupName": "rust deps"
|
||||
},
|
||||
{
|
||||
"matchManagers": [
|
||||
"cargo"
|
||||
],
|
||||
"matchUpdateTypes": [
|
||||
"major"
|
||||
],
|
||||
"automerge": false
|
||||
}
|
||||
],
|
||||
"lockFileMaintenance": {
|
||||
"enabled": true
|
||||
},
|
||||
"configMigration": true
|
||||
}
|
||||
@@ -1 +1,8 @@
|
||||
format_code_in_doc_comments = true
|
||||
format_strings = true
|
||||
group_imports = "StdExternalCrate"
|
||||
imports_granularity = "Module"
|
||||
license_template_path = ".cargo/license.rs"
|
||||
newline_style = "Unix"
|
||||
unstable_features = true
|
||||
wrap_comments = true
|
||||
|
||||
+17
-24
@@ -8,25 +8,22 @@
|
||||
|
||||
//! 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::core_api::BlockSizeUser;
|
||||
use digest::{FixedOutput, HashMarker, OutputSizeUser};
|
||||
use hash2curve::{ExpandMsg, GroupDigest, MapToCurve, OprfParameters};
|
||||
use hybrid_array::ArraySize;
|
||||
use hybrid_array::typenum::{IsGreaterOrEqual, Prod, True, U2};
|
||||
use elliptic_curve::VoprfParameters;
|
||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
|
||||
|
||||
use crate::Group;
|
||||
|
||||
/// 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>,
|
||||
<Self::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<Self::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// The ciphersuite identifier as dictated by
|
||||
/// <https://www.rfc-editor.org/rfc/rfc9497>
|
||||
const ID: &'static [u8];
|
||||
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-voprf/>
|
||||
const ID: &'static str;
|
||||
|
||||
/// 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`].
|
||||
@@ -37,20 +34,16 @@ where
|
||||
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
|
||||
impl<T: VoprfParameters> 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>,
|
||||
T::Hash: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||
<T::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<T::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
const ID: &'static [u8] = T::ID;
|
||||
const ID: &'static str = T::ID;
|
||||
|
||||
type Group = T;
|
||||
type Hash = OprfHash<T>;
|
||||
|
||||
type Hash = T::Hash;
|
||||
}
|
||||
|
||||
+115
-53
@@ -12,10 +12,12 @@ use core::convert::TryFrom;
|
||||
use core::ops::Add;
|
||||
|
||||
use derive_where::derive_where;
|
||||
use digest::core_api::BlockSizeUser;
|
||||
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 generic_array::sequence::Concat;
|
||||
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U2, U256, U9};
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use subtle::ConstantTimeEq;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
@@ -28,14 +30,14 @@ use crate::{CipherSuite, Error, Group, InternalError, Result};
|
||||
///////////////
|
||||
|
||||
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_SEED: [u8; 5] = *b"Seed-";
|
||||
pub(crate) const STR_DERIVE_KEYPAIR: [u8; 13] = *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-");
|
||||
pub(crate) const STR_VOPRF: [u8; 8] = *b"VOPRF10-";
|
||||
pub(crate) const STR_HASH_TO_SCALAR: [u8; 13] = *b"HashToScalar-";
|
||||
pub(crate) const STR_HASH_TO_GROUP: [u8; 12] = *b"HashToGroup-";
|
||||
|
||||
/// Determines the mode of operation (either base mode or verifiable mode). This
|
||||
/// is only used for custom implementations for [`Group`].
|
||||
@@ -77,7 +79,10 @@ impl Mode {
|
||||
pub struct BlindedElement<CS: CipherSuite>(
|
||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||
pub(crate) <CS::Group as Group>::Elem,
|
||||
);
|
||||
)
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>;
|
||||
|
||||
/// The server's response to the [BlindedElement] message from a client (either
|
||||
/// verifiable or not) to a server (either verifiable or not).
|
||||
@@ -91,7 +96,10 @@ pub struct BlindedElement<CS: CipherSuite>(
|
||||
pub struct EvaluationElement<CS: CipherSuite>(
|
||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||
pub(crate) <CS::Group as Group>::Elem,
|
||||
);
|
||||
)
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>;
|
||||
|
||||
/// Contains prepared [`EvaluationElement`]s by a server batch evaluate
|
||||
/// preparation.
|
||||
@@ -102,7 +110,10 @@ pub struct EvaluationElement<CS: CipherSuite>(
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct PreparedEvaluationElement<CS: CipherSuite>(pub(crate) EvaluationElement<CS>);
|
||||
pub struct PreparedEvaluationElement<CS: CipherSuite>(pub(crate) EvaluationElement<CS>)
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>;
|
||||
|
||||
/// A proof produced by a server that the OPRF output matches against a server
|
||||
/// public key.
|
||||
@@ -113,7 +124,11 @@ pub struct PreparedEvaluationElement<CS: CipherSuite>(pub(crate) EvaluationEleme
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct Proof<CS: CipherSuite> {
|
||||
pub struct Proof<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
#[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>"))]
|
||||
@@ -127,7 +142,7 @@ pub struct Proof<CS: CipherSuite> {
|
||||
|
||||
/// Can only fail with [`Error::Batch`].
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
pub(crate) fn generate_proof<CS: CipherSuite, R: TryRng + TryCryptoRng>(
|
||||
pub(crate) fn generate_proof<CS: CipherSuite, R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
k: <CS::Group as Group>::Scalar,
|
||||
a: <CS::Group as Group>::Elem,
|
||||
@@ -135,12 +150,16 @@ pub(crate) fn generate_proof<CS: CipherSuite, R: TryRng + TryCryptoRng>(
|
||||
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
|
||||
) -> Result<Proof<CS>>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html#section-2.2.1
|
||||
|
||||
let (m, z) = compute_composites::<CS, _, _>(Some(k), b, cs, ds, mode)?;
|
||||
|
||||
let r = CS::Group::random_scalar(rng)?;
|
||||
let r = CS::Group::random_scalar(rng);
|
||||
let t2 = a * &r;
|
||||
let t3 = m * &r;
|
||||
|
||||
@@ -177,7 +196,7 @@ pub(crate) fn generate_proof<CS: CipherSuite, R: TryRng + TryCryptoRng>(
|
||||
&STR_CHALLENGE,
|
||||
];
|
||||
|
||||
let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, mode);
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, mode);
|
||||
// This can't fail, the size of the `input` is known.
|
||||
let c_scalar = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
|
||||
let s_scalar = r - &(c_scalar * &k);
|
||||
@@ -194,8 +213,12 @@ pub(crate) fn verify_proof<CS: CipherSuite>(
|
||||
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
|
||||
) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html#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);
|
||||
@@ -233,7 +256,7 @@ pub(crate) fn verify_proof<CS: CipherSuite>(
|
||||
&STR_CHALLENGE,
|
||||
];
|
||||
|
||||
let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, mode);
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, mode);
|
||||
// This can't fail, the size of the `input` is known.
|
||||
let c = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
|
||||
|
||||
@@ -259,8 +282,12 @@ fn compute_composites<
|
||||
c_slice: IC,
|
||||
d_slice: ID,
|
||||
mode: Mode,
|
||||
) -> Result<ComputeCompositesResult<CS>> {
|
||||
// https://www.rfc-editor.org/rfc/rfc9497#section-2.2.1
|
||||
) -> Result<ComputeCompositesResult<CS>>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html#section-2.2.1
|
||||
|
||||
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
|
||||
|
||||
@@ -271,7 +298,7 @@ fn compute_composites<
|
||||
let len = u16::try_from(c_slice.len()).map_err(|_| Error::Batch)?;
|
||||
|
||||
// seedDST = "Seed-" || contextString
|
||||
let seed_dst = Dst::new::<CS, _>(STR_SEED, mode);
|
||||
let seed_dst = Dst::new::<CS, _, _>(STR_SEED, mode);
|
||||
|
||||
// h1Input = I2OSP(len(Bm), 2) || Bm ||
|
||||
// I2OSP(len(seedDST), 2) || seedDST
|
||||
@@ -282,7 +309,7 @@ fn compute_composites<
|
||||
.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 seed_len = i2osp_2_array(&seed);
|
||||
|
||||
let mut m = CS::Group::identity_elem();
|
||||
let mut z = CS::Group::identity_elem();
|
||||
@@ -307,7 +334,7 @@ fn compute_composites<
|
||||
&STR_COMPOSITE,
|
||||
];
|
||||
|
||||
let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, mode);
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, mode);
|
||||
// This can't fail, the size of the `input` is known.
|
||||
let di = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
|
||||
m = c * &di + &m;
|
||||
@@ -335,8 +362,12 @@ 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);
|
||||
) -> Result<<CS::Group as Group>::Scalar, Error>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let dst = Dst::new::<CS, _, _>(STR_DERIVE_KEYPAIR, mode);
|
||||
|
||||
let info_len = i2osp_2(info.len()).map_err(|_| Error::DeriveKeyPair)?;
|
||||
|
||||
@@ -369,7 +400,11 @@ pub fn derive_key<CS: CipherSuite>(
|
||||
seed: &[u8],
|
||||
info: &[u8],
|
||||
mode: Mode,
|
||||
) -> Result<<CS::Group as Group>::Scalar, Error> {
|
||||
) -> Result<<CS::Group as Group>::Scalar, Error>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
derive_key_internal::<CS>(seed, info, mode)
|
||||
}
|
||||
|
||||
@@ -383,7 +418,11 @@ pub(crate) fn derive_keypair<CS: CipherSuite>(
|
||||
seed: &[u8],
|
||||
info: &[u8],
|
||||
mode: Mode,
|
||||
) -> Result<DeriveKeypairResult<CS>, Error> {
|
||||
) -> Result<DeriveKeypairResult<CS>, Error>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let sk_s = derive_key_internal::<CS>(seed, info, mode)?;
|
||||
let pk_s = CS::Group::base_elem() * &sk_s;
|
||||
|
||||
@@ -399,7 +438,11 @@ pub(crate) fn deterministic_blind_unchecked<CS: CipherSuite>(
|
||||
input: &[u8],
|
||||
blind: &<CS::Group as Group>::Scalar,
|
||||
mode: Mode,
|
||||
) -> Result<<CS::Group as Group>::Elem> {
|
||||
) -> Result<<CS::Group as Group>::Elem>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let hashed_point = hash_to_group::<CS>(input, mode)?;
|
||||
Ok(hashed_point * blind)
|
||||
}
|
||||
@@ -408,8 +451,12 @@ pub(crate) fn deterministic_blind_unchecked<CS: CipherSuite>(
|
||||
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);
|
||||
) -> Result<<CS::Group as Group>::Elem>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -418,8 +465,12 @@ pub(crate) fn hash_to_group<CS: CipherSuite>(
|
||||
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>> {
|
||||
issued_element: GenericArray<u8, <<CS as CipherSuite>::Group as Group>::ElemLen>,
|
||||
) -> Result<Output<CS::Hash>>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
// OPRF & VOPRF
|
||||
// hashInput = I2OSP(len(input), 2) || input ||
|
||||
// I2OSP(len(issuedElement), 2) || issuedElement ||
|
||||
@@ -441,34 +492,45 @@ pub(crate) fn server_evaluate_hash_input<CS: CipherSuite>(
|
||||
.chain_update(info.as_ref());
|
||||
}
|
||||
Ok(hash
|
||||
.chain_update(i2osp_2(issued_element.as_slice().len()).map_err(|_| Error::Input)?)
|
||||
.chain_update(i2osp_2(issued_element.as_ref().len()).map_err(|_| Error::Input)?)
|
||||
.chain_update(issued_element)
|
||||
.chain_update(STR_FINALIZE)
|
||||
.finalize())
|
||||
}
|
||||
|
||||
pub(crate) struct Dst<L: ArraySize> {
|
||||
dst_1: Array<u8, L>,
|
||||
dst_2: &'static [u8],
|
||||
pub(crate) struct Dst<L: ArrayLength<u8>> {
|
||||
dst_1: GenericArray<u8, L>,
|
||||
dst_2: [u8; 2],
|
||||
}
|
||||
|
||||
impl<L: ArraySize> Dst<L> {
|
||||
pub(crate) fn new<CS, TL>(par_1: Array<u8, TL>, mode: Mode) -> Self
|
||||
impl<L: ArrayLength<u8>> Dst<L> {
|
||||
pub(crate) fn new<CS: CipherSuite, T, TL>(par_1: T, mode: Mode) -> Self
|
||||
where
|
||||
CS: CipherSuite,
|
||||
TL: ArraySize + Add<U9, Output = L>,
|
||||
T: Into<GenericArray<u8, TL>>,
|
||||
TL: ArrayLength<u8> + Add<U9, Output = L>,
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let par_1 = par_1.into();
|
||||
// Generates the contextString parameter as defined in
|
||||
// <https://www.rfc-editor.org/rfc/rfc9497#section-3.1>
|
||||
let par_2 = ArrayN::<u8, 7>::from(STR_OPRF)
|
||||
.concat(ArrayN::<u8, 1>::from([mode.to_u8()]))
|
||||
.concat(ArrayN::<u8, 1>::from([b'-']));
|
||||
// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-voprf/>
|
||||
let par_2 = GenericArray::from(STR_VOPRF).concat([mode.to_u8()].into());
|
||||
|
||||
// See <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html#section-4.1>
|
||||
let cs_id_u16: u16 = match CS::ID {
|
||||
"ristretto255-SHA512" => 0x0001,
|
||||
"decaf448-SHAKE256" => 0x0002,
|
||||
"P256-SHA256" => 0x0003,
|
||||
"P384-SHA384" => 0x0004,
|
||||
"P521-SHA512" => 0x0005,
|
||||
_ => panic!("Incompatible ciphersuite: {}", CS::ID),
|
||||
};
|
||||
|
||||
let dst_1 = par_1.concat(par_2);
|
||||
let dst_2 = CS::ID;
|
||||
let dst_2 = cs_id_u16.to_be_bytes();
|
||||
|
||||
assert!(
|
||||
L::USIZE + dst_2.len() <= u16::MAX.into(),
|
||||
L::USIZE + 2 <= u16::MAX.into(),
|
||||
"constructed DST longer then {}",
|
||||
u16::MAX
|
||||
);
|
||||
@@ -477,13 +539,11 @@ impl<L: ArraySize> Dst<L> {
|
||||
}
|
||||
|
||||
pub(crate) fn as_dst(&self) -> [&[u8]; 2] {
|
||||
[&self.dst_1, self.dst_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()
|
||||
u16::try_from(L::USIZE + 2).unwrap().to_be_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,6 +575,8 @@ pub(crate) fn i2osp_2(input: usize) -> Result<[u8; 2], InternalError> {
|
||||
.map_err(|_| InternalError::I2osp)
|
||||
}
|
||||
|
||||
pub(crate) fn i2osp_2_array<L: ArraySize + IsLess<U256>>() -> Array<u8, U2> {
|
||||
pub(crate) fn i2osp_2_array<L: ArrayLength<u8> + IsLess<U256>>(
|
||||
_: &GenericArray<u8, L>,
|
||||
) -> GenericArray<u8, U2> {
|
||||
L::U16.to_be_bytes().into()
|
||||
}
|
||||
|
||||
+5
-4
@@ -8,11 +8,13 @@
|
||||
|
||||
//! Errors which are produced during an execution of the protocol
|
||||
|
||||
use displaydoc::Display;
|
||||
|
||||
/// [`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)]
|
||||
#[derive(Clone, Copy, Debug, Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub enum Error {
|
||||
/// Size of info is longer then [`u16::MAX`].
|
||||
Info,
|
||||
@@ -28,8 +30,6 @@ pub enum Error {
|
||||
ProofVerification,
|
||||
/// The protocol has failed and can't be completed.
|
||||
Protocol,
|
||||
/// Random number generator failure.
|
||||
Rng,
|
||||
}
|
||||
|
||||
/// Only used to implement [`Group`](crate::Group).
|
||||
@@ -41,4 +41,5 @@ pub enum InternalError {
|
||||
I2osp,
|
||||
}
|
||||
|
||||
impl core::error::Error for Error {}
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
+29
-58
@@ -6,72 +6,54 @@
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
|
||||
use core::ops::{Add, Mul};
|
||||
use digest::block_api::BlockSizeUser;
|
||||
use digest::typenum::{IsLess, IsLessOrEqual, U256};
|
||||
use digest::core_api::BlockSizeUser;
|
||||
use digest::{FixedOutput, HashMarker};
|
||||
use elliptic_curve::group::cofactor::CofactorGroup;
|
||||
use elliptic_curve::sec1::{FromSec1Point, ModulusSize, ToSec1Point};
|
||||
use elliptic_curve::hash2curve::{ExpandMsgXmd, FromOkm, GroupDigest};
|
||||
use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint};
|
||||
use elliptic_curve::{
|
||||
AffinePoint, Field, FieldBytes, FieldBytesSize, Group as _, ProjectivePoint, PublicKey, Scalar,
|
||||
SecretKey,
|
||||
AffinePoint, Field, 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 generic_array::typenum::{IsLess, IsLessOrEqual, U256};
|
||||
use generic_array::GenericArray;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
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,
|
||||
ProjectivePoint<Self>: CofactorGroup + ToEncodedPoint<Self>,
|
||||
FieldBytesSize<Self>: ModulusSize,
|
||||
AffinePoint<Self>: FromEncodedPoint<Self> + ToEncodedPoint<Self>,
|
||||
Scalar<Self>: FromOkm,
|
||||
{
|
||||
type Elem = ProjectivePoint<Self>;
|
||||
|
||||
type ElemLen = ElemLen<Self>;
|
||||
type ElemLen = <FieldBytesSize<Self> as ModulusSize>::CompressedPointSize;
|
||||
|
||||
type Scalar = Scalar<Self>;
|
||||
|
||||
type ScalarLen = ScalarLen<Self>;
|
||||
|
||||
type SecurityLevel = C::SecurityLevel;
|
||||
type ScalarLen = FieldBytesSize<Self>;
|
||||
|
||||
// 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)
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
||||
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError>
|
||||
where
|
||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
||||
{
|
||||
Self::hash_from_bytes::<ExpandMsgXmd<H>>(input, dst).map_err(|_| InternalError::Input)
|
||||
}
|
||||
|
||||
// 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>,
|
||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
||||
{
|
||||
hash_to_scalar::<C, ExpandMsgXmd<H>, <C as MapToCurve>::Length>(input, dst)
|
||||
<Self as GroupDigest>::hash_to_scalar::<ExpandMsgXmd<H>>(input, dst)
|
||||
.map_err(|_| InternalError::Input)
|
||||
}
|
||||
|
||||
@@ -83,10 +65,10 @@ where
|
||||
ProjectivePoint::<Self>::identity()
|
||||
}
|
||||
|
||||
fn serialize_elem(elem: Self::Elem) -> Array<u8, Self::ElemLen> {
|
||||
let bytes = elem.to_sec1_point(true);
|
||||
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> {
|
||||
let bytes = elem.to_encoded_point(true);
|
||||
let bytes = bytes.as_bytes();
|
||||
let mut result = Array::default();
|
||||
let mut result = GenericArray::default();
|
||||
result[..bytes.len()].copy_from_slice(bytes);
|
||||
result
|
||||
}
|
||||
@@ -97,16 +79,8 @@ where
|
||||
.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 random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
|
||||
*SecretKey::<Self>::random(rng).to_nonzero_scalar()
|
||||
}
|
||||
|
||||
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar {
|
||||
@@ -122,11 +96,8 @@ where
|
||||
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 serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
||||
scalar.into()
|
||||
}
|
||||
|
||||
fn deserialize_scalar(scalar_bits: &[u8]) -> Result<Self::Scalar> {
|
||||
|
||||
+14
-37
@@ -14,11 +14,11 @@ mod ristretto;
|
||||
|
||||
use core::ops::{Add, Mul, Sub};
|
||||
|
||||
use digest::block_api::BlockSizeUser;
|
||||
use digest::core_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};
|
||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
#[cfg(feature = "ristretto255")]
|
||||
pub use ristretto::Ristretto255;
|
||||
use subtle::{Choice, ConstantTimeEq};
|
||||
@@ -27,16 +27,8 @@ 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 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,
|
||||
{
|
||||
/// subgroup is noted additively — as in the draft RFC — in this trait.
|
||||
pub trait Group {
|
||||
/// The type of group elements
|
||||
type Elem: ConstantTimeEq
|
||||
+ Copy
|
||||
@@ -45,7 +37,7 @@ where
|
||||
+ for<'a> Mul<&'a Self::Scalar, Output = Self::Elem>;
|
||||
|
||||
/// The byte length necessary to represent group elements
|
||||
type ElemLen: ArraySize + 'static;
|
||||
type ElemLen: ArrayLength<u8> + 'static;
|
||||
|
||||
/// The type of base field scalars
|
||||
type Scalar: ConstantTimeEq
|
||||
@@ -56,15 +48,7 @@ where
|
||||
+ for<'a> Sub<&'a Self::Scalar, Output = Self::Scalar>;
|
||||
|
||||
/// The byte length necessary to represent scalars
|
||||
type ScalarLen: ArraySize + 'static;
|
||||
|
||||
/// Security parameter `k` in bytes (i.e. `k / 8`), as defined in
|
||||
/// [RFC 9380 §8](https://www.rfc-editor.org/rfc/rfc9380#section-8).
|
||||
///
|
||||
/// Used to enforce `H::OutputSize >= 2 * SecurityLevel` in
|
||||
/// `hash_to_curve` and `hash_to_scalar`, which corresponds to the
|
||||
/// `expand_message` requirement `len_in_bytes = 2 * k / 8`.
|
||||
type SecurityLevel: ArraySize + Mul<U2>;
|
||||
type ScalarLen: ArrayLength<u8> + 'static;
|
||||
|
||||
/// Transforms a password and domain separation tag (DST) into a curve point
|
||||
///
|
||||
@@ -74,9 +58,7 @@ where
|
||||
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError>
|
||||
where
|
||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||
H::OutputSize: IsLess<U256>
|
||||
+ IsLessOrEqual<H::BlockSize, Output = True>
|
||||
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>;
|
||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>;
|
||||
|
||||
/// Hashes a slice of pseudo-random bytes to a scalar
|
||||
///
|
||||
@@ -86,9 +68,7 @@ where
|
||||
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
|
||||
where
|
||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||
H::OutputSize: IsLess<U256>
|
||||
+ IsLessOrEqual<H::BlockSize, Output = True>
|
||||
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>;
|
||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>;
|
||||
|
||||
/// Get the base point for the group
|
||||
fn base_elem() -> Self::Elem;
|
||||
@@ -102,7 +82,7 @@ where
|
||||
}
|
||||
|
||||
/// Serializes the `self` group element
|
||||
fn serialize_elem(elem: Self::Elem) -> Array<u8, Self::ElemLen>;
|
||||
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen>;
|
||||
|
||||
/// Return an element from its fixed-length bytes representation. If the
|
||||
/// element is the identity element, return an error.
|
||||
@@ -112,11 +92,8 @@ where
|
||||
/// is not a valid point on the group or the identity element.
|
||||
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem>;
|
||||
|
||||
/// 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>;
|
||||
/// picks a scalar at random
|
||||
fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar;
|
||||
|
||||
/// The multiplicative inverse of this scalar
|
||||
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar;
|
||||
@@ -129,7 +106,7 @@ where
|
||||
fn zero_scalar() -> Self::Scalar;
|
||||
|
||||
/// Serializes a scalar to bytes
|
||||
fn serialize_scalar(scalar: Self::Scalar) -> Array<u8, Self::ScalarLen>;
|
||||
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen>;
|
||||
|
||||
/// Return a scalar from its fixed-length bytes representation. If the
|
||||
/// scalar is zero or invalid, then return an error.
|
||||
|
||||
+26
-50
@@ -6,20 +6,16 @@
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
|
||||
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::core_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 elliptic_curve::hash2curve::{ExpandMsg, ExpandMsgXmd, Expander};
|
||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256, U32, U64};
|
||||
use generic_array::GenericArray;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use subtle::ConstantTimeEq;
|
||||
|
||||
use super::Group;
|
||||
@@ -31,7 +27,7 @@ pub struct Ristretto255;
|
||||
|
||||
#[cfg(feature = "ristretto255-ciphersuite")]
|
||||
impl crate::CipherSuite for Ristretto255 {
|
||||
const ID: &'static [u8] = b"ristretto255-SHA512";
|
||||
const ID: &'static str = "ristretto255-SHA512";
|
||||
|
||||
type Group = Ristretto255;
|
||||
|
||||
@@ -47,52 +43,34 @@ impl Group for Ristretto255 {
|
||||
|
||||
type ScalarLen = U32;
|
||||
|
||||
type SecurityLevel = U16;
|
||||
|
||||
// Implements the `hash_to_ristretto255()` function from
|
||||
// https://www.rfc-editor.org/rfc/rfc9380.html#appendix-B
|
||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt
|
||||
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError>
|
||||
where
|
||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||
H::OutputSize: IsLess<U256>
|
||||
+ IsLessOrEqual<H::BlockSize, Output = True>
|
||||
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>,
|
||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
||||
{
|
||||
let mut uniform_bytes = [0u8; 64];
|
||||
let mut uniform_bytes = GenericArray::<_, U64>::default();
|
||||
ExpandMsgXmd::<H>::expand_message(input, dst, 64)
|
||||
.map_err(|_| InternalError::Input)?
|
||||
.fill_bytes(&mut uniform_bytes);
|
||||
|
||||
<ExpandMsgXmd<H> as ExpandMsg<U16>>::expand_message(
|
||||
input,
|
||||
dst,
|
||||
NonZeroU16::new(64).unwrap(),
|
||||
)
|
||||
.map_err(|_| InternalError::Input)?
|
||||
.fill_bytes(&mut uniform_bytes)
|
||||
.map_err(|_| InternalError::Input)?;
|
||||
|
||||
Ok(RistrettoPoint::from_uniform_bytes(&uniform_bytes))
|
||||
Ok(RistrettoPoint::from_uniform_bytes(&uniform_bytes.into()))
|
||||
}
|
||||
|
||||
// Implements the `HashToScalar()` function from
|
||||
// https://www.rfc-editor.org/rfc/rfc9497#section-4.1
|
||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.1
|
||||
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
|
||||
where
|
||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||
H::OutputSize: IsLess<U256>
|
||||
+ IsLessOrEqual<H::BlockSize, Output = True>
|
||||
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>,
|
||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
||||
{
|
||||
let mut uniform_bytes = [0u8; 64];
|
||||
let mut uniform_bytes = GenericArray::<_, U64>::default();
|
||||
ExpandMsgXmd::<H>::expand_message(input, dst, 64)
|
||||
.map_err(|_| InternalError::Input)?
|
||||
.fill_bytes(&mut uniform_bytes);
|
||||
|
||||
<ExpandMsgXmd<H> as ExpandMsg<U16>>::expand_message(
|
||||
input,
|
||||
dst,
|
||||
NonZeroU16::new(64).unwrap(),
|
||||
)
|
||||
.map_err(|_| InternalError::Input)?
|
||||
.fill_bytes(&mut uniform_bytes)
|
||||
.map_err(|_| InternalError::Input)?;
|
||||
|
||||
Ok(Scalar::from_bytes_mod_order_wide(&uniform_bytes))
|
||||
Ok(Scalar::from_bytes_mod_order_wide(&uniform_bytes.into()))
|
||||
}
|
||||
|
||||
fn base_elem() -> Self::Elem {
|
||||
@@ -104,7 +82,7 @@ impl Group for Ristretto255 {
|
||||
}
|
||||
|
||||
// serialization of a group element
|
||||
fn serialize_elem(elem: Self::Elem) -> Array<u8, Self::ElemLen> {
|
||||
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> {
|
||||
elem.compress().to_bytes().into()
|
||||
}
|
||||
|
||||
@@ -116,14 +94,12 @@ impl Group for Ristretto255 {
|
||||
.ok_or(Error::Deserialization)
|
||||
}
|
||||
|
||||
fn random_scalar<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self::Scalar> {
|
||||
fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
|
||||
loop {
|
||||
let mut scalar_bytes = [0u8; 32];
|
||||
rng.try_fill_bytes(&mut scalar_bytes)
|
||||
.map_err(|_| Error::Rng)?;
|
||||
let scalar = Scalar::random(rng);
|
||||
|
||||
if let Ok(scalar) = Self::deserialize_scalar(&scalar_bytes) {
|
||||
break Ok(scalar);
|
||||
if scalar != Scalar::ZERO {
|
||||
break scalar;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,7 +117,7 @@ impl Group for Ristretto255 {
|
||||
Scalar::ZERO
|
||||
}
|
||||
|
||||
fn serialize_scalar(scalar: Self::Scalar) -> Array<u8, Self::ScalarLen> {
|
||||
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
||||
scalar.to_bytes().into()
|
||||
}
|
||||
|
||||
|
||||
+84
-82
@@ -9,7 +9,9 @@
|
||||
//! An implementation of a verifiable oblivious pseudorandom function (VOPRF)
|
||||
//!
|
||||
//! Note: This implementation is in sync with
|
||||
//! [RFC 9497](https://www.rfc-editor.org/rfc/rfc9497).
|
||||
//! [draft-irtf-cfrg-voprf-11](https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html),
|
||||
//! but this specification is subject to change, until the final version
|
||||
//! published by the IETF.
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
@@ -20,7 +22,7 @@
|
||||
//! We will use the following choice in this example:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! type CipherSuite = voprf::Ristretto255;
|
||||
//! ```
|
||||
//!
|
||||
//! ## Modes of Operation
|
||||
@@ -51,14 +53,14 @@
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! use rand::rngs::SysRng;
|
||||
//! use rand::Rng;
|
||||
//! use voprf_vx::OprfServer;
|
||||
//! use rand::rngs::OsRng;
|
||||
//! use rand::RngCore;
|
||||
//! use voprf::OprfServer;
|
||||
//!
|
||||
//! let mut server_rng = SysRng;
|
||||
//! let mut server_rng = OsRng;
|
||||
//! let server = OprfServer::<CipherSuite>::new(&mut server_rng);
|
||||
//! ```
|
||||
//!
|
||||
@@ -71,14 +73,14 @@
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! use rand::rngs::SysRng;
|
||||
//! use rand::Rng;
|
||||
//! use voprf_vx::OprfClient;
|
||||
//! use rand::rngs::OsRng;
|
||||
//! use rand::RngCore;
|
||||
//! use voprf::OprfClient;
|
||||
//!
|
||||
//! let mut client_rng = SysRng;
|
||||
//! let mut client_rng = OsRng;
|
||||
//! let client_blind_result = OprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
|
||||
//! .expect("Unable to construct client");
|
||||
//! ```
|
||||
@@ -92,19 +94,19 @@
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::OprfClient;
|
||||
//! # use rand::{rngs::SysRng, Rng};
|
||||
//! # use voprf::OprfClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = SysRng;
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # 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;
|
||||
//! # use voprf::OprfServer;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! let server_evaluate_result = server.blind_evaluate(&client_blind_result.message);
|
||||
//! ```
|
||||
@@ -117,19 +119,19 @@
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::OprfClient;
|
||||
//! # use rand::{rngs::SysRng, Rng};
|
||||
//! # use voprf::OprfClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = SysRng;
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # 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;
|
||||
//! # use voprf::OprfServer;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! # let message = server.blind_evaluate(&client_blind_result.message);
|
||||
//! let client_finalize_result = client_blind_result
|
||||
@@ -150,19 +152,19 @@
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::OprfClient;
|
||||
//! # use rand::{rngs::SysRng, Rng};
|
||||
//! # use voprf::OprfClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = SysRng;
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # 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;
|
||||
//! # use voprf::OprfServer;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! # let message = server.blind_evaluate(&client_blind_result.message);
|
||||
//! let client_finalize_result = client_blind_result
|
||||
@@ -197,14 +199,14 @@
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! use rand::rngs::SysRng;
|
||||
//! use rand::Rng;
|
||||
//! use voprf_vx::VoprfServer;
|
||||
//! use rand::rngs::OsRng;
|
||||
//! use rand::RngCore;
|
||||
//! use voprf::VoprfServer;
|
||||
//!
|
||||
//! let mut server_rng = SysRng;
|
||||
//! let mut server_rng = OsRng;
|
||||
//! let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//!
|
||||
//! // To be sent to the client
|
||||
@@ -224,14 +226,14 @@
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! use rand::rngs::SysRng;
|
||||
//! use rand::Rng;
|
||||
//! use voprf_vx::VoprfClient;
|
||||
//! use rand::rngs::OsRng;
|
||||
//! use rand::RngCore;
|
||||
//! use voprf::VoprfClient;
|
||||
//!
|
||||
//! let mut client_rng = SysRng;
|
||||
//! let mut client_rng = OsRng;
|
||||
//! let client_blind_result = VoprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
|
||||
//! .expect("Unable to construct client");
|
||||
//! ```
|
||||
@@ -246,19 +248,19 @@
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::{VoprfServerEvaluateResult, VoprfClient};
|
||||
//! # use rand::{rngs::SysRng, Rng};
|
||||
//! # use voprf::{VoprfServerEvaluateResult, VoprfClient};
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = SysRng;
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # 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;
|
||||
//! # use voprf::VoprfServer;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! let VoprfServerEvaluateResult { message, proof } =
|
||||
//! server.blind_evaluate(&mut server_rng, &client_blind_result.message);
|
||||
@@ -273,19 +275,19 @@
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::VoprfClient;
|
||||
//! # use rand::{rngs::SysRng, Rng};
|
||||
//! # use voprf::VoprfClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = SysRng;
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # 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;
|
||||
//! # use voprf::VoprfServer;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! # let server_evaluate_result = server.blind_evaluate(
|
||||
//! # &mut server_rng,
|
||||
@@ -314,19 +316,19 @@
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::VoprfClient;
|
||||
//! # use rand::{rngs::SysRng, Rng};
|
||||
//! # use voprf::VoprfClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = SysRng;
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # 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;
|
||||
//! # use voprf::VoprfServer;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! # let server_evaluate_result = server.blind_evaluate(
|
||||
//! # &mut server_rng,
|
||||
@@ -368,13 +370,13 @@
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::VoprfClient;
|
||||
//! # use rand::{rngs::SysRng, Rng};
|
||||
//! # use voprf::VoprfClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! let mut client_rng = SysRng;
|
||||
//! let mut client_rng = OsRng;
|
||||
//! let mut client_states = vec![];
|
||||
//! let mut client_messages = vec![];
|
||||
//! for _ in 0..10 {
|
||||
@@ -392,13 +394,13 @@
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::{VoprfServerBatchEvaluateFinishResult, VoprfClient};
|
||||
//! # use rand::{rngs::SysRng, Rng};
|
||||
//! # use voprf::{VoprfServerBatchEvaluateFinishResult, VoprfClient};
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = SysRng;
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let mut client_states = vec![];
|
||||
//! # let mut client_messages = vec![];
|
||||
//! # for _ in 0..10 {
|
||||
@@ -409,8 +411,8 @@
|
||||
//! # client_states.push(client_blind_result.state);
|
||||
//! # client_messages.push(client_blind_result.message);
|
||||
//! # }
|
||||
//! # use voprf_vx::VoprfServer;
|
||||
//! let mut server_rng = SysRng;
|
||||
//! # use voprf::VoprfServer;
|
||||
//! let mut server_rng = OsRng;
|
||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! let prepared_evaluation_elements = server.batch_blind_evaluate_prepare(client_messages.iter());
|
||||
//! let prepared_elements: Vec<_> = prepared_evaluation_elements.collect();
|
||||
@@ -426,13 +428,13 @@
|
||||
//! ```
|
||||
//! # #[cfg(feature = "alloc")] {
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::{VoprfServerBatchEvaluateResult, VoprfClient};
|
||||
//! # use rand::{rngs::SysRng, Rng};
|
||||
//! # use voprf::{VoprfServerBatchEvaluateResult, VoprfClient};
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = SysRng;
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let mut client_states = vec![];
|
||||
//! # let mut client_messages = vec![];
|
||||
//! # for _ in 0..10 {
|
||||
@@ -443,8 +445,8 @@
|
||||
//! # client_states.push(client_blind_result.state);
|
||||
//! # client_messages.push(client_blind_result.message);
|
||||
//! # }
|
||||
//! # use voprf_vx::VoprfServer;
|
||||
//! let mut server_rng = SysRng;
|
||||
//! # use voprf::VoprfServer;
|
||||
//! let mut server_rng = OsRng;
|
||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! let VoprfServerBatchEvaluateResult { messages, proof } = server
|
||||
//! .batch_blind_evaluate(&mut server_rng, &client_messages)
|
||||
@@ -460,13 +462,13 @@
|
||||
//! ```
|
||||
//! # #[cfg(feature = "alloc")] {
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf_vx::{VoprfServerBatchEvaluateResult, VoprfClient};
|
||||
//! # use rand::{rngs::SysRng, Rng};
|
||||
//! # use voprf::{VoprfServerBatchEvaluateResult, VoprfClient};
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = SysRng;
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let mut client_states = vec![];
|
||||
//! # let mut client_messages = vec![];
|
||||
//! # for _ in 0..10 {
|
||||
@@ -477,8 +479,8 @@
|
||||
//! # client_states.push(client_blind_result.state);
|
||||
//! # client_messages.push(client_blind_result.message);
|
||||
//! # }
|
||||
//! # use voprf_vx::VoprfServer;
|
||||
//! # let mut server_rng = SysRng;
|
||||
//! # use voprf::VoprfServer;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! # let VoprfServerBatchEvaluateResult { messages, proof } = server
|
||||
//! # .batch_blind_evaluate(&mut server_rng, &client_messages)
|
||||
@@ -510,7 +512,7 @@
|
||||
//! 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>
|
||||
//! <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html#name-poprf-public-input>
|
||||
//! for more detailed information on how this public input should be used.
|
||||
//!
|
||||
//! # Features
|
||||
@@ -538,7 +540,7 @@
|
||||
//! (https://docs.rs/curve25519-dalek/4.0.0-pre.5/curve25519_dalek/index.html#backends)
|
||||
|
||||
#![no_std]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(not(test), deny(unsafe_code))]
|
||||
#![warn(
|
||||
clippy::cargo,
|
||||
|
||||
+83
-35
@@ -11,14 +11,15 @@
|
||||
use core::iter::{self, Map};
|
||||
|
||||
use derive_where::derive_where;
|
||||
use digest::{Digest, Output};
|
||||
use hybrid_array::Array;
|
||||
use hybrid_array::typenum::Unsigned;
|
||||
use rand_core::{TryCryptoRng, TryRng};
|
||||
use digest::core_api::BlockSizeUser;
|
||||
use digest::{Digest, Output, OutputSizeUser};
|
||||
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256};
|
||||
use generic_array::GenericArray;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use crate::common::{
|
||||
BlindedElement, EvaluationElement, Mode, STR_FINALIZE, derive_key_internal,
|
||||
deterministic_blind_unchecked, hash_to_group, i2osp_2, server_evaluate_hash_input,
|
||||
derive_key_internal, deterministic_blind_unchecked, hash_to_group, i2osp_2,
|
||||
server_evaluate_hash_input, BlindedElement, EvaluationElement, Mode, STR_FINALIZE,
|
||||
};
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::serialization::serde::Scalar;
|
||||
@@ -43,7 +44,11 @@ use crate::{CipherSuite, Error, Group, Result};
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct OprfClient<CS: CipherSuite> {
|
||||
pub struct OprfClient<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) blind: <CS::Group as Group>::Scalar,
|
||||
}
|
||||
@@ -57,7 +62,11 @@ pub struct OprfClient<CS: CipherSuite> {
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct OprfServer<CS: CipherSuite> {
|
||||
pub struct OprfServer<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) sk: <CS::Group as Group>::Scalar,
|
||||
}
|
||||
@@ -67,17 +76,21 @@ pub struct OprfServer<CS: CipherSuite> {
|
||||
// =================== //
|
||||
/////////////////////////
|
||||
|
||||
impl<CS: CipherSuite> OprfClient<CS> {
|
||||
impl<CS: CipherSuite> OprfClient<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// 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>(
|
||||
pub fn blind<R: RngCore + CryptoRng>(
|
||||
input: &[u8],
|
||||
blinding_factor_rng: &mut R,
|
||||
) -> Result<OprfClientBlindResult<CS>> {
|
||||
let blind = CS::Group::random_scalar(blinding_factor_rng)?;
|
||||
let blind = CS::Group::random_scalar(blinding_factor_rng);
|
||||
Self::deterministic_blind_unchecked_inner(input, blind)
|
||||
}
|
||||
|
||||
@@ -141,14 +154,18 @@ impl<CS: CipherSuite> OprfClient<CS> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> OprfServer<CS> {
|
||||
impl<CS: CipherSuite> OprfServer<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// 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)?;
|
||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self> {
|
||||
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
|
||||
rng.fill_bytes(&mut seed);
|
||||
Self::new_from_seed(&seed, &[])
|
||||
}
|
||||
|
||||
@@ -177,7 +194,7 @@ impl<CS: CipherSuite> OprfServer<CS> {
|
||||
Ok(Self { sk })
|
||||
}
|
||||
|
||||
/// Only used for tests
|
||||
// Only used for tests
|
||||
#[cfg(test)]
|
||||
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
|
||||
self.sk
|
||||
@@ -214,7 +231,11 @@ impl<CS: CipherSuite> OprfServer<CS> {
|
||||
|
||||
/// 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> {
|
||||
pub struct OprfClientBlindResult<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// The state to be persisted on the client
|
||||
pub state: OprfClient<CS>,
|
||||
/// The message to send to the server
|
||||
@@ -240,7 +261,11 @@ fn finalize_after_unblind<
|
||||
>(
|
||||
inputs_and_unblinded_elements: IE,
|
||||
_unused: &'a [u8],
|
||||
) -> FinalizeAfterUnblindResult<'a, CS, I, IE> {
|
||||
) -> FinalizeAfterUnblindResult<CS, I, IE>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
inputs_and_unblinded_elements.map(|(input, unblinded_element)| {
|
||||
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
|
||||
|
||||
@@ -267,20 +292,23 @@ fn finalize_after_unblind<
|
||||
mod tests {
|
||||
use core::ptr;
|
||||
|
||||
use rand::TryRng;
|
||||
use rand::rngs::SysRng;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use super::*;
|
||||
use crate::Group;
|
||||
use crate::common::{Dst, STR_HASH_TO_GROUP};
|
||||
use crate::Group;
|
||||
|
||||
fn prf<CS: CipherSuite>(
|
||||
input: &[u8],
|
||||
key: <CS::Group as Group>::Scalar,
|
||||
info: &[u8],
|
||||
mode: Mode,
|
||||
) -> Output<CS::Hash> {
|
||||
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, mode);
|
||||
) -> Output<CS::Hash>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
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;
|
||||
@@ -291,9 +319,13 @@ mod tests {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn base_retrieval<CS: CipherSuite>() {
|
||||
fn base_retrieval<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let input = b"input";
|
||||
let mut rng = SysRng;
|
||||
let mut rng = OsRng;
|
||||
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);
|
||||
@@ -302,17 +334,21 @@ mod tests {
|
||||
assert_eq!(client_finalize_result, res2);
|
||||
}
|
||||
|
||||
fn base_inversion_unsalted<CS: CipherSuite>() {
|
||||
let mut rng = SysRng;
|
||||
fn base_inversion_unsalted<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let mut rng = OsRng;
|
||||
let mut input = [0u8; 64];
|
||||
rng.try_fill_bytes(&mut input).unwrap();
|
||||
rng.fill_bytes(&mut input);
|
||||
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 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()
|
||||
@@ -322,9 +358,13 @@ mod tests {
|
||||
assert_eq!(client_finalize_result, res2);
|
||||
}
|
||||
|
||||
fn server_evaluate<CS: CipherSuite>() {
|
||||
fn server_evaluate<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let input = b"input";
|
||||
let mut rng = SysRng;
|
||||
let mut rng = OsRng;
|
||||
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);
|
||||
@@ -346,9 +386,13 @@ mod tests {
|
||||
assert!(client_finalize != server_evaluate);
|
||||
}
|
||||
|
||||
fn zeroize_oprf_client<CS: CipherSuite>() {
|
||||
fn zeroize_oprf_client<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let input = b"input";
|
||||
let mut rng = SysRng;
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
|
||||
let mut state = client_blind_result.state;
|
||||
@@ -360,9 +404,13 @@ mod tests {
|
||||
assert!(message.serialize().iter().all(|&x| x == 0));
|
||||
}
|
||||
|
||||
fn zeroize_oprf_server<CS: CipherSuite>() {
|
||||
fn zeroize_oprf_server<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let input = b"input";
|
||||
let mut rng = SysRng;
|
||||
let mut rng = OsRng;
|
||||
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);
|
||||
|
||||
+140
-54
@@ -13,15 +13,16 @@ use alloc::vec::Vec;
|
||||
use core::iter::{self, Map, Repeat, Zip};
|
||||
|
||||
use derive_where::derive_where;
|
||||
use digest::core_api::BlockSizeUser;
|
||||
use digest::{Digest, Output, OutputSizeUser};
|
||||
use hybrid_array::typenum::Unsigned;
|
||||
use hybrid_array::{Array, ArraySize};
|
||||
use rand_core::{TryCryptoRng, TryRng};
|
||||
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256};
|
||||
use generic_array::GenericArray;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use crate::common::{
|
||||
BlindedElement, Dst, EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE,
|
||||
STR_HASH_TO_SCALAR, STR_INFO, derive_keypair, deterministic_blind_unchecked, generate_proof,
|
||||
hash_to_group, i2osp_2, server_evaluate_hash_input, verify_proof,
|
||||
derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2,
|
||||
server_evaluate_hash_input, verify_proof, BlindedElement, Dst, EvaluationElement, Mode,
|
||||
PreparedEvaluationElement, Proof, STR_FINALIZE, STR_HASH_TO_SCALAR, STR_INFO,
|
||||
};
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::serialization::serde::{Element, Scalar};
|
||||
@@ -41,7 +42,11 @@ use crate::{CipherSuite, Error, Group, Result};
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct PoprfClient<CS: CipherSuite> {
|
||||
pub struct PoprfClient<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
#[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>"))]
|
||||
@@ -57,7 +62,11 @@ pub struct PoprfClient<CS: CipherSuite> {
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct PoprfServer<CS: CipherSuite> {
|
||||
pub struct PoprfServer<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
#[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>"))]
|
||||
@@ -69,17 +78,21 @@ pub struct PoprfServer<CS: CipherSuite> {
|
||||
// =================== //
|
||||
/////////////////////////
|
||||
|
||||
impl<CS: CipherSuite> PoprfClient<CS> {
|
||||
impl<CS: CipherSuite> PoprfClient<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// 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>(
|
||||
pub fn blind<R: RngCore + CryptoRng>(
|
||||
input: &[u8],
|
||||
blinding_factor_rng: &mut R,
|
||||
) -> Result<PoprfClientBlindResult<CS>> {
|
||||
let blind = CS::Group::random_scalar(blinding_factor_rng)?;
|
||||
let blind = CS::Group::random_scalar(blinding_factor_rng);
|
||||
Self::deterministic_blind_unchecked_inner(input, blind)
|
||||
}
|
||||
|
||||
@@ -132,10 +145,7 @@ impl<CS: CipherSuite> PoprfClient<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,
|
||||
{
|
||||
) -> Result<Output<CS::Hash>> {
|
||||
let clients = core::array::from_ref(self);
|
||||
let messages = core::array::from_ref(evaluation_element);
|
||||
|
||||
@@ -170,7 +180,6 @@ impl<CS: CipherSuite> PoprfClient<CS> {
|
||||
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
|
||||
<&'a IM as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArraySize,
|
||||
{
|
||||
let unblinded_elements = poprf_unblind(clients, messages, pk, proof, info)?;
|
||||
|
||||
@@ -184,14 +193,18 @@ impl<CS: CipherSuite> PoprfClient<CS> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> PoprfServer<CS> {
|
||||
impl<CS: CipherSuite> PoprfServer<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// 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)?;
|
||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self> {
|
||||
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
|
||||
rng.fill_bytes(&mut seed);
|
||||
|
||||
Self::new_from_seed(&seed, &[])
|
||||
}
|
||||
@@ -222,7 +235,7 @@ impl<CS: CipherSuite> PoprfServer<CS> {
|
||||
Ok(Self { sk, pk })
|
||||
}
|
||||
|
||||
/// Only used for tests
|
||||
// Only used for tests
|
||||
#[cfg(test)]
|
||||
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
|
||||
self.sk
|
||||
@@ -235,7 +248,7 @@ impl<CS: CipherSuite> PoprfServer<CS> {
|
||||
/// # Errors
|
||||
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
pub fn blind_evaluate<R: TryRng + TryCryptoRng>(
|
||||
pub fn blind_evaluate<R: RngCore + CryptoRng>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
blinded_element: &BlindedElement<CS>,
|
||||
@@ -273,7 +286,7 @@ impl<CS: CipherSuite> PoprfServer<CS> {
|
||||
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
#[cfg(feature = "alloc")]
|
||||
pub fn batch_blind_evaluate<'a, R: TryRng + TryCryptoRng, IE>(
|
||||
pub fn batch_blind_evaluate<'a, R: RngCore + CryptoRng, IE>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
blinded_elements: &'a IE,
|
||||
@@ -346,7 +359,7 @@ impl<CS: CipherSuite> PoprfServer<CS> {
|
||||
pub fn batch_blind_evaluate_finish<
|
||||
'a,
|
||||
'b,
|
||||
R: TryRng + TryCryptoRng,
|
||||
R: RngCore + CryptoRng,
|
||||
IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator,
|
||||
IE,
|
||||
>(
|
||||
@@ -371,7 +384,7 @@ impl<CS: CipherSuite> PoprfServer<CS> {
|
||||
tweaked_key,
|
||||
prepared_evaluation_elements
|
||||
.into_iter()
|
||||
.map(|element| element.0.0),
|
||||
.map(|element| element.0 .0),
|
||||
blinded_elements.map(|element| element.0),
|
||||
Mode::Poprf,
|
||||
)?;
|
||||
@@ -379,7 +392,7 @@ impl<CS: CipherSuite> PoprfServer<CS> {
|
||||
let messages = prepared_evaluation_elements.into_iter().map(<fn(
|
||||
&PreparedEvaluationElement<CS>,
|
||||
) -> _>::from(
|
||||
|element| EvaluationElement(element.0.0),
|
||||
|element| EvaluationElement(element.0 .0),
|
||||
));
|
||||
|
||||
Ok(PoprfServerBatchEvaluateFinishResult { messages, proof })
|
||||
@@ -414,7 +427,11 @@ impl<CS: CipherSuite> PoprfServer<CS> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> BlindedElement<CS> {
|
||||
impl<CS: CipherSuite> BlindedElement<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Creates a [BlindedElement] from a raw group element.
|
||||
///
|
||||
/// # Caution
|
||||
@@ -433,7 +450,11 @@ impl<CS: CipherSuite> BlindedElement<CS> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> EvaluationElement<CS> {
|
||||
impl<CS: CipherSuite> EvaluationElement<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Creates an [EvaluationElement] from a raw group element.
|
||||
///
|
||||
/// # Caution
|
||||
@@ -459,7 +480,11 @@ impl<CS: CipherSuite> EvaluationElement<CS> {
|
||||
|
||||
/// 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> {
|
||||
pub struct PoprfClientBlindResult<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// The state to be persisted on the client
|
||||
pub state: PoprfClient<CS>,
|
||||
/// The message to send to the server
|
||||
@@ -472,7 +497,11 @@ pub type PoprfClientBatchFinalizeResult<'a, CS, II, IC, IM> =
|
||||
|
||||
/// 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> {
|
||||
pub struct PoprfServerEvaluateResult<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// The message to send to the client
|
||||
pub message: EvaluationElement<CS>,
|
||||
/// The proof for the client to verify
|
||||
@@ -482,7 +511,11 @@ pub struct PoprfServerEvaluateResult<CS: CipherSuite> {
|
||||
/// 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> {
|
||||
pub struct PoprfServerBatchEvaluateResult<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// The messages to send to the client
|
||||
pub messages: Vec<EvaluationElement<CS>>,
|
||||
/// The proof for the client to verify
|
||||
@@ -512,12 +545,19 @@ pub type PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I> = Map<
|
||||
pub struct PoprfPreparedTweak<CS: CipherSuite>(
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
<CS::Group as Group>::Scalar,
|
||||
);
|
||||
)
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>;
|
||||
|
||||
/// 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> {
|
||||
pub struct PoprfServerBatchEvaluatePrepareResult<CS: CipherSuite, I>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Prepared [`EvaluationElement`].
|
||||
pub prepared_evaluation_elements: PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I>,
|
||||
/// Prepared tweak.
|
||||
@@ -536,6 +576,8 @@ pub type PoprfServerBatchEvaluateFinishedMessages<'a, CS, I> = Map<
|
||||
#[derive_where(Debug; <&'a I as IntoIterator>::IntoIter, <CS::Group as Group>::Scalar)]
|
||||
pub struct PoprfServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
&'a I: IntoIterator<Item = &'a PreparedEvaluationElement<CS>>,
|
||||
{
|
||||
/// The [`EvaluationElement`]s to send to the client
|
||||
@@ -556,7 +598,11 @@ where
|
||||
fn compute_tweaked_key<CS: CipherSuite>(
|
||||
pk: <CS::Group as Group>::Elem,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<<CS::Group as Group>::Elem> {
|
||||
) -> Result<<CS::Group as Group>::Elem>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
// None for info is treated the same as empty bytes
|
||||
let info = info.unwrap_or_default();
|
||||
|
||||
@@ -569,7 +615,7 @@ fn compute_tweaked_key<CS: CipherSuite>(
|
||||
let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
|
||||
let framed_info = [STR_INFO.as_slice(), &info_len, info];
|
||||
|
||||
let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, Mode::Poprf);
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, Mode::Poprf);
|
||||
// This can't fail, the size of the `input` is known.
|
||||
let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst.as_dst()).unwrap();
|
||||
|
||||
@@ -590,7 +636,11 @@ fn compute_tweaked_key<CS: CipherSuite>(
|
||||
fn compute_tweak<CS: CipherSuite>(
|
||||
sk: <CS::Group as Group>::Scalar,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<<CS::Group as Group>::Scalar> {
|
||||
) -> Result<<CS::Group as Group>::Scalar>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
// None for info is treated the same as empty bytes
|
||||
let info = info.unwrap_or_default();
|
||||
|
||||
@@ -602,7 +652,7 @@ fn compute_tweak<CS: CipherSuite>(
|
||||
let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
|
||||
let framed_info = [STR_INFO.as_slice(), &info_len, info];
|
||||
|
||||
let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, Mode::Poprf);
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, Mode::Poprf);
|
||||
// This can't fail, the size of the `input` is known.
|
||||
let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst.as_dst()).unwrap();
|
||||
|
||||
@@ -641,6 +691,8 @@ fn poprf_unblind<'a, CS: 'a + CipherSuite, IC, IM>(
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<PoprfUnblindResult<'a, CS, IC, IM>>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
&'a IC: 'a + IntoIterator<Item = &'a PoprfClient<CS>>,
|
||||
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
|
||||
@@ -676,7 +728,7 @@ 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>>,
|
||||
) -> Result<GenericArray<u8, <<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize>>,
|
||||
>;
|
||||
|
||||
/// Can only fail with [`Error::Batch`] and returned values can only fail with
|
||||
@@ -692,7 +744,8 @@ fn finalize_after_unblind<
|
||||
info: Option<&'a [u8]>,
|
||||
) -> Result<FinalizeAfterUnblindResult<'a, CS, IE, II>>
|
||||
where
|
||||
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArraySize,
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
if unblinded_elements.len() != inputs.len() {
|
||||
return Err(Error::Batch);
|
||||
@@ -731,23 +784,30 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::ops::Add;
|
||||
use core::ptr;
|
||||
|
||||
use rand::rngs::SysRng;
|
||||
use generic_array::typenum::Sum;
|
||||
use generic_array::ArrayLength;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use super::*;
|
||||
use crate::Group;
|
||||
use crate::common::STR_HASH_TO_GROUP;
|
||||
use crate::Group;
|
||||
|
||||
fn prf<CS: CipherSuite>(
|
||||
input: &[u8],
|
||||
key: <CS::Group as Group>::Scalar,
|
||||
info: &[u8],
|
||||
mode: Mode,
|
||||
) -> Output<CS::Hash> {
|
||||
) -> Output<CS::Hash>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let t = compute_tweak::<CS>(key, Some(info)).unwrap();
|
||||
|
||||
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, mode);
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, mode);
|
||||
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
|
||||
|
||||
// evaluatedElement = G.ScalarInverse(t) * blindedElement
|
||||
@@ -760,10 +820,14 @@ mod tests {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn verifiable_retrieval<CS: CipherSuite>() {
|
||||
fn verifiable_retrieval<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let input = b"input";
|
||||
let info = b"info";
|
||||
let mut rng = SysRng;
|
||||
let mut rng = OsRng;
|
||||
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
let server_result = server
|
||||
@@ -783,17 +847,21 @@ mod tests {
|
||||
assert_eq!(client_finalize_result, res2);
|
||||
}
|
||||
|
||||
fn verifiable_bad_public_key<CS: CipherSuite>() {
|
||||
fn verifiable_bad_public_key<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let input = b"input";
|
||||
let info = b"info";
|
||||
let mut rng = SysRng;
|
||||
let mut rng = OsRng;
|
||||
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
let server_result = server
|
||||
.blind_evaluate(&mut rng, &client_blind_result.message, Some(info))
|
||||
.unwrap();
|
||||
let wrong_pk = {
|
||||
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, Mode::Oprf);
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, Mode::Oprf);
|
||||
// Choose a group element that is unlikely to be the right public key
|
||||
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
|
||||
};
|
||||
@@ -807,10 +875,14 @@ mod tests {
|
||||
assert!(client_finalize_result.is_err());
|
||||
}
|
||||
|
||||
fn verifiable_server_evaluate<CS: CipherSuite>() {
|
||||
fn verifiable_server_evaluate<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let input = b"input";
|
||||
let info = Some(b"info".as_slice());
|
||||
let mut rng = SysRng;
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let server_result = server
|
||||
@@ -840,9 +912,15 @@ mod tests {
|
||||
assert!(client_finalize != server_evaluate);
|
||||
}
|
||||
|
||||
fn zeroize_verifiable_client<CS: CipherSuite>() {
|
||||
fn zeroize_verifiable_client<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
|
||||
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ElemLen>: ArrayLength<u8>,
|
||||
{
|
||||
let input = b"input";
|
||||
let mut rng = SysRng;
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
|
||||
let mut state = client_blind_result.state;
|
||||
@@ -854,10 +932,18 @@ mod tests {
|
||||
assert!(message.serialize().iter().all(|&x| x == 0));
|
||||
}
|
||||
|
||||
fn zeroize_verifiable_server<CS: CipherSuite>() {
|
||||
fn zeroize_verifiable_server<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
|
||||
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ElemLen>: ArrayLength<u8>,
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ScalarLen>,
|
||||
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ScalarLen>: ArrayLength<u8>,
|
||||
{
|
||||
let input = b"input";
|
||||
let info = b"info";
|
||||
let mut rng = SysRng;
|
||||
let mut rng = OsRng;
|
||||
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
let server_result = server
|
||||
|
||||
+84
-23
@@ -9,8 +9,13 @@
|
||||
//! Handles the serialization of each of the components used in the VOPRF
|
||||
//! protocol
|
||||
|
||||
use hybrid_array::Array;
|
||||
use hybrid_array::typenum::{Sum, Unsigned};
|
||||
use core::ops::Add;
|
||||
|
||||
use digest::core_api::BlockSizeUser;
|
||||
use digest::OutputSizeUser;
|
||||
use generic_array::sequence::Concat;
|
||||
use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, Unsigned, U256};
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
|
||||
use crate::{
|
||||
BlindedElement, CipherSuite, Error, EvaluationElement, Group, OprfClient, OprfServer,
|
||||
@@ -25,9 +30,13 @@ use crate::{
|
||||
/// Length of [`OprfClient`] in bytes for serialization.
|
||||
pub type OprfClientLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
|
||||
|
||||
impl<CS: CipherSuite> OprfClient<CS> {
|
||||
impl<CS: CipherSuite> OprfClient<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Array<u8, OprfClientLen<CS>> {
|
||||
pub fn serialize(&self) -> GenericArray<u8, OprfClientLen<CS>> {
|
||||
CS::Group::serialize_scalar(self.blind)
|
||||
}
|
||||
|
||||
@@ -48,9 +57,17 @@ pub type VoprfClientLen<CS> = Sum<
|
||||
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
||||
>;
|
||||
|
||||
impl<CS: CipherSuite> VoprfClient<CS> {
|
||||
impl<CS: CipherSuite> VoprfClient<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Array<u8, VoprfClientLen<CS>> {
|
||||
pub fn serialize(&self) -> GenericArray<u8, VoprfClientLen<CS>>
|
||||
where
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
|
||||
VoprfClientLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
<CS::Group as Group>::serialize_scalar(self.blind)
|
||||
.concat(<CS::Group as Group>::serialize_elem(self.blinded_element))
|
||||
}
|
||||
@@ -76,9 +93,17 @@ pub type PoprfClientLen<CS> = Sum<
|
||||
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
||||
>;
|
||||
|
||||
impl<CS: CipherSuite> PoprfClient<CS> {
|
||||
impl<CS: CipherSuite> PoprfClient<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Array<u8, PoprfClientLen<CS>> {
|
||||
pub fn serialize(&self) -> GenericArray<u8, PoprfClientLen<CS>>
|
||||
where
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
|
||||
PoprfClientLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
<CS::Group as Group>::serialize_scalar(self.blind)
|
||||
.concat(<CS::Group as Group>::serialize_elem(self.blinded_element))
|
||||
}
|
||||
@@ -101,9 +126,13 @@ impl<CS: CipherSuite> PoprfClient<CS> {
|
||||
/// Length of [`OprfServer`] in bytes for serialization.
|
||||
pub type OprfServerLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
|
||||
|
||||
impl<CS: CipherSuite> OprfServer<CS> {
|
||||
impl<CS: CipherSuite> OprfServer<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Array<u8, OprfServerLen<CS>> {
|
||||
pub fn serialize(&self) -> GenericArray<u8, OprfServerLen<CS>> {
|
||||
CS::Group::serialize_scalar(self.sk)
|
||||
}
|
||||
|
||||
@@ -124,9 +153,17 @@ pub type VoprfServerLen<CS> = Sum<
|
||||
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
||||
>;
|
||||
|
||||
impl<CS: CipherSuite> VoprfServer<CS> {
|
||||
impl<CS: CipherSuite> VoprfServer<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Array<u8, VoprfServerLen<CS>> {
|
||||
pub fn serialize(&self) -> GenericArray<u8, VoprfServerLen<CS>>
|
||||
where
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
|
||||
VoprfServerLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
CS::Group::serialize_scalar(self.sk).concat(CS::Group::serialize_elem(self.pk))
|
||||
}
|
||||
|
||||
@@ -148,9 +185,17 @@ pub type PoprfServerLen<CS> = Sum<
|
||||
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
||||
>;
|
||||
|
||||
impl<CS: CipherSuite> PoprfServer<CS> {
|
||||
impl<CS: CipherSuite> PoprfServer<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Array<u8, PoprfServerLen<CS>> {
|
||||
pub fn serialize(&self) -> GenericArray<u8, PoprfServerLen<CS>>
|
||||
where
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
|
||||
PoprfServerLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
CS::Group::serialize_scalar(self.sk).concat(CS::Group::serialize_elem(self.pk))
|
||||
}
|
||||
|
||||
@@ -172,9 +217,17 @@ pub type ProofLen<CS> = Sum<
|
||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
||||
>;
|
||||
|
||||
impl<CS: CipherSuite> Proof<CS> {
|
||||
impl<CS: CipherSuite> Proof<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Array<u8, ProofLen<CS>> {
|
||||
pub fn serialize(&self) -> GenericArray<u8, ProofLen<CS>>
|
||||
where
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ScalarLen>,
|
||||
ProofLen<CS>: ArrayLength<u8>,
|
||||
{
|
||||
CS::Group::serialize_scalar(self.c_scalar)
|
||||
.concat(CS::Group::serialize_scalar(self.s_scalar))
|
||||
}
|
||||
@@ -194,9 +247,13 @@ impl<CS: CipherSuite> Proof<CS> {
|
||||
/// Length of [`BlindedElement`] in bytes for serialization.
|
||||
pub type BlindedElementLen<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen;
|
||||
|
||||
impl<CS: CipherSuite> BlindedElement<CS> {
|
||||
impl<CS: CipherSuite> BlindedElement<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Array<u8, BlindedElementLen<CS>> {
|
||||
pub fn serialize(&self) -> GenericArray<u8, BlindedElementLen<CS>> {
|
||||
CS::Group::serialize_elem(self.0)
|
||||
}
|
||||
|
||||
@@ -214,9 +271,13 @@ impl<CS: CipherSuite> BlindedElement<CS> {
|
||||
/// Length of [`EvaluationElement`] in bytes for serialization.
|
||||
pub type EvaluationElementLen<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen;
|
||||
|
||||
impl<CS: CipherSuite> EvaluationElement<CS> {
|
||||
impl<CS: CipherSuite> EvaluationElement<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Array<u8, EvaluationElementLen<CS>> {
|
||||
pub fn serialize(&self) -> GenericArray<u8, EvaluationElementLen<CS>> {
|
||||
CS::Group::serialize_elem(self.0)
|
||||
}
|
||||
|
||||
@@ -265,7 +326,7 @@ impl<T> SliceExt for [T] {
|
||||
pub(crate) mod serde {
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use hybrid_array::Array;
|
||||
use generic_array::GenericArray;
|
||||
use serde::de::{Deserializer, Error};
|
||||
use serde::ser::Serializer;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -279,7 +340,7 @@ pub(crate) mod serde {
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Array::<_, G::ElemLen>::deserialize(deserializer)
|
||||
GenericArray::<_, G::ElemLen>::deserialize(deserializer)
|
||||
.and_then(|bytes| G::deserialize_elem(&bytes).map_err(D::Error::custom))
|
||||
}
|
||||
|
||||
@@ -298,7 +359,7 @@ pub(crate) mod serde {
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Array::<_, G::ScalarLen>::deserialize(deserializer)
|
||||
GenericArray::<_, G::ScalarLen>::deserialize(deserializer)
|
||||
.and_then(|bytes| G::deserialize_scalar(&bytes).map_err(D::Error::custom))
|
||||
}
|
||||
|
||||
|
||||
+1023
-910
File diff suppressed because it is too large
Load Diff
+14
-21
@@ -9,8 +9,7 @@
|
||||
use alloc::vec::Vec;
|
||||
use core::cmp::min;
|
||||
|
||||
use core::convert::Infallible;
|
||||
use rand_core::{TryCryptoRng, TryRng};
|
||||
use rand_core::{CryptoRng, Error, RngCore};
|
||||
|
||||
/// A simple implementation of `RngCore` for testing purposes.
|
||||
///
|
||||
@@ -39,35 +38,29 @@ fn rotate_left<T>(data: &mut [T], steps: usize) {
|
||||
data.reverse();
|
||||
}
|
||||
|
||||
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))
|
||||
impl RngCore for CycleRng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
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 next_u64(&mut self) -> u64 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
|
||||
#[inline]
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
let len = min(self.v.len(), dest.len());
|
||||
|
||||
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);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// This is meant for testing only
|
||||
impl TryCryptoRng for CycleRng {}
|
||||
impl CryptoRng for CycleRng {}
|
||||
|
||||
+12
-7
@@ -15,50 +15,55 @@ pub(crate) fn rfc_to_json(input: &str) -> String {
|
||||
}
|
||||
|
||||
fn parse_ciphersuites(input: &str) -> String {
|
||||
let re = regex::Regex::new(r"\nA\.\d\. {2}(?P<ciphersuite>.+?)\n\n").unwrap();
|
||||
let re = regex::Regex::new(r"## OPRF\((?P<ciphersuite>.+?)\)").unwrap();
|
||||
let mut ciphersuites = vec![];
|
||||
|
||||
let chunks: Vec<&str> = re.split(input).collect();
|
||||
for (count, caps) in (1..).zip(re.captures_iter(input)) {
|
||||
let mut count = 1;
|
||||
for caps in re.captures_iter(input) {
|
||||
let ciphersuite = format!(
|
||||
"\"{}\": {{ {} }}",
|
||||
&caps["ciphersuite"],
|
||||
parse_modes(chunks[count])
|
||||
);
|
||||
|
||||
ciphersuites.push(ciphersuite);
|
||||
count += 1;
|
||||
}
|
||||
|
||||
ciphersuites.join(",\n")
|
||||
}
|
||||
|
||||
fn parse_modes(input: &str) -> String {
|
||||
let re = regex::Regex::new(r"A\.\d.\d\. {2}(?P<mode>.*?) Mode").unwrap();
|
||||
let re = regex::Regex::new(r"### (?P<mode>.*+) Mode").unwrap();
|
||||
let mut modes = vec![];
|
||||
|
||||
let chunks: Vec<&str> = re.split(input).collect();
|
||||
for (count, caps) in (1..).zip(re.captures_iter(input)) {
|
||||
let mut count = 1;
|
||||
for caps in re.captures_iter(input) {
|
||||
let mode = format!(
|
||||
"\"{}\": [\n {} \n]",
|
||||
&caps["mode"],
|
||||
parse_vectors(chunks[count])
|
||||
);
|
||||
modes.push(mode);
|
||||
count += 1;
|
||||
}
|
||||
|
||||
modes.join(",\n")
|
||||
}
|
||||
|
||||
fn parse_vectors(input: &str) -> String {
|
||||
let re = regex::Regex::new(r"A\.\d.\d\.\d\. {2}Test Vector.*+\n").unwrap();
|
||||
let re = regex::Regex::new(r"Test Vector.*+\n").unwrap();
|
||||
let mut vectors = vec![];
|
||||
|
||||
let chunks: Vec<&str> = re.split(input).collect();
|
||||
let init_params = parse_params(chunks[0]);
|
||||
|
||||
for (count, _) in (1..).zip(re.captures_iter(input)) {
|
||||
let mut count = 1;
|
||||
for _ in re.captures_iter(input) {
|
||||
let params = format!("{{\n{},\n{}\n}}", init_params, parse_params(chunks[count]));
|
||||
vectors.push(params);
|
||||
count += 1;
|
||||
}
|
||||
|
||||
vectors.join(",\n")
|
||||
|
||||
+100
-30
@@ -9,7 +9,12 @@
|
||||
use alloc::string::String;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::ops::Add;
|
||||
|
||||
use digest::core_api::BlockSizeUser;
|
||||
use digest::OutputSizeUser;
|
||||
use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256};
|
||||
use generic_array::ArrayLength;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::tests::mock_rng::CycleRng;
|
||||
@@ -95,7 +100,7 @@ fn test_vectors() -> Result<()> {
|
||||
|
||||
let ristretto_oprf_tvs = json_to_test_vectors!(
|
||||
rfc,
|
||||
String::from("ristretto255-SHA512"),
|
||||
String::from("ristretto255, SHA-512"),
|
||||
String::from("OPRF")
|
||||
);
|
||||
assert_ne!(ristretto_oprf_tvs.len(), 0);
|
||||
@@ -107,7 +112,7 @@ fn test_vectors() -> Result<()> {
|
||||
|
||||
let ristretto_voprf_tvs = json_to_test_vectors!(
|
||||
rfc,
|
||||
String::from("ristretto255-SHA512"),
|
||||
String::from("ristretto255, SHA-512"),
|
||||
String::from("VOPRF")
|
||||
);
|
||||
assert_ne!(ristretto_voprf_tvs.len(), 0);
|
||||
@@ -119,7 +124,7 @@ fn test_vectors() -> Result<()> {
|
||||
|
||||
let ristretto_poprf_tvs = json_to_test_vectors!(
|
||||
rfc,
|
||||
String::from("ristretto255-SHA512"),
|
||||
String::from("ristretto255, SHA-512"),
|
||||
String::from("POPRF")
|
||||
);
|
||||
assert_ne!(ristretto_poprf_tvs.len(), 0);
|
||||
@@ -131,7 +136,7 @@ fn test_vectors() -> Result<()> {
|
||||
}
|
||||
|
||||
let p256_oprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P256-SHA256"), String::from("OPRF"));
|
||||
json_to_test_vectors!(rfc, String::from("P-256, SHA-256"), 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)?;
|
||||
@@ -140,7 +145,7 @@ fn test_vectors() -> Result<()> {
|
||||
test_oprf_evaluate::<NistP256>(&p256_oprf_tvs)?;
|
||||
|
||||
let p256_voprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P256-SHA256"), String::from("VOPRF"));
|
||||
json_to_test_vectors!(rfc, String::from("P-256, SHA-256"), 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)?;
|
||||
@@ -149,7 +154,7 @@ fn test_vectors() -> Result<()> {
|
||||
test_voprf_evaluate::<NistP256>(&p256_voprf_tvs)?;
|
||||
|
||||
let p256_poprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P256-SHA256"), String::from("POPRF"));
|
||||
json_to_test_vectors!(rfc, String::from("P-256, SHA-256"), 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)?;
|
||||
@@ -158,7 +163,7 @@ fn test_vectors() -> Result<()> {
|
||||
test_poprf_evaluate::<NistP256>(&p256_poprf_tvs)?;
|
||||
|
||||
let p384_oprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P384-SHA384"), String::from("OPRF"));
|
||||
json_to_test_vectors!(rfc, String::from("P-384, SHA-384"), 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)?;
|
||||
@@ -167,7 +172,7 @@ fn test_vectors() -> Result<()> {
|
||||
test_oprf_evaluate::<NistP384>(&p384_oprf_tvs)?;
|
||||
|
||||
let p384_voprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P384-SHA384"), String::from("VOPRF"));
|
||||
json_to_test_vectors!(rfc, String::from("P-384, SHA-384"), 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)?;
|
||||
@@ -176,7 +181,7 @@ fn test_vectors() -> Result<()> {
|
||||
test_voprf_evaluate::<NistP384>(&p384_voprf_tvs)?;
|
||||
|
||||
let p384_poprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P384-SHA384"), String::from("POPRF"));
|
||||
json_to_test_vectors!(rfc, String::from("P-384, SHA-384"), 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)?;
|
||||
@@ -185,7 +190,7 @@ fn test_vectors() -> Result<()> {
|
||||
test_poprf_evaluate::<NistP384>(&p384_poprf_tvs)?;
|
||||
|
||||
let p521_oprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P521-SHA512"), String::from("OPRF"));
|
||||
json_to_test_vectors!(rfc, String::from("P-521, SHA-512"), 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)?;
|
||||
@@ -194,7 +199,7 @@ fn test_vectors() -> Result<()> {
|
||||
test_oprf_evaluate::<NistP521>(&p521_oprf_tvs)?;
|
||||
|
||||
let p521_voprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P521-SHA512"), String::from("VOPRF"));
|
||||
json_to_test_vectors!(rfc, String::from("P-521, SHA-512"), 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)?;
|
||||
@@ -203,7 +208,7 @@ fn test_vectors() -> Result<()> {
|
||||
test_voprf_evaluate::<NistP521>(&p521_voprf_tvs)?;
|
||||
|
||||
let p521_poprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P521-SHA512"), String::from("POPRF"));
|
||||
json_to_test_vectors!(rfc, String::from("P-521, SHA-512"), 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)?;
|
||||
@@ -214,7 +219,11 @@ fn test_vectors() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_oprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
fn test_oprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let server = OprfServer::<CS>::new_from_seed(¶meters.seed, ¶meters.key_info)?;
|
||||
|
||||
@@ -226,7 +235,11 @@ fn test_oprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) ->
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_voprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
fn test_voprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let server = VoprfServer::<CS>::new_from_seed(¶meters.seed, ¶meters.key_info)?;
|
||||
|
||||
@@ -242,7 +255,11 @@ fn test_voprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) ->
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_poprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
fn test_poprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let server = PoprfServer::<CS>::new_from_seed(¶meters.seed, ¶meters.key_info)?;
|
||||
|
||||
@@ -259,7 +276,11 @@ fn test_poprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) ->
|
||||
}
|
||||
|
||||
// Tests input -> blind, blinded_element
|
||||
fn test_oprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
fn test_oprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||
@@ -280,7 +301,11 @@ fn test_oprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result
|
||||
}
|
||||
|
||||
// Tests input -> blind, blinded_element
|
||||
fn test_voprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
fn test_voprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||
@@ -301,7 +326,11 @@ fn test_voprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Resul
|
||||
}
|
||||
|
||||
// Tests input -> blind, blinded_element
|
||||
fn test_poprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
fn test_poprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||
@@ -322,7 +351,11 @@ fn test_poprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Resul
|
||||
}
|
||||
|
||||
// Tests sksm, blinded_element -> evaluation_element
|
||||
fn test_oprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
fn test_oprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let server = OprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
@@ -339,7 +372,13 @@ fn test_oprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters])
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_voprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
fn test_voprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ScalarLen>,
|
||||
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ScalarLen>: ArrayLength<u8>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
|
||||
let server = VoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
@@ -360,12 +399,18 @@ fn test_voprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters])
|
||||
assert_eq!(¶meter, &message.serialize().as_slice());
|
||||
}
|
||||
|
||||
assert_eq!(¶meters.proof, &proof.serialize().to_vec());
|
||||
assert_eq!(¶meters.proof, &proof.serialize().as_slice());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_poprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
fn test_poprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ScalarLen>,
|
||||
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ScalarLen>: ArrayLength<u8>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
|
||||
let server = PoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
@@ -386,7 +431,8 @@ fn test_poprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters])
|
||||
blinded_elements.iter(),
|
||||
&prepared_evaluation_elements,
|
||||
&prepared_tweak,
|
||||
)?;
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let messages: Vec<_> = messages.collect();
|
||||
|
||||
@@ -394,13 +440,17 @@ fn test_poprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters])
|
||||
assert_eq!(¶meter, &message.serialize().as_slice());
|
||||
}
|
||||
|
||||
assert_eq!(¶meters.proof, &proof.serialize().to_vec());
|
||||
assert_eq!(¶meters.proof, &proof.serialize().as_slice());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests input, blind, evaluation_element -> output
|
||||
fn test_oprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
fn test_oprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let client =
|
||||
@@ -417,7 +467,11 @@ fn test_oprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Res
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_voprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
fn test_voprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let mut clients = vec![];
|
||||
for i in 0..parameters.input.len() {
|
||||
@@ -452,7 +506,11 @@ fn test_voprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Re
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_poprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
fn test_poprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
let mut clients = vec![];
|
||||
for i in 0..parameters.input.len() {
|
||||
@@ -486,7 +544,11 @@ fn test_poprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Re
|
||||
}
|
||||
|
||||
// Tests input, sksm -> output
|
||||
fn test_oprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
fn test_oprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let server = OprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
@@ -499,7 +561,11 @@ fn test_oprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Res
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_voprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
fn test_voprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let server = VoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
@@ -512,7 +578,11 @@ fn test_voprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Re
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_poprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
fn test_poprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let server = PoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
|
||||
+126
-48
@@ -13,15 +13,16 @@ use alloc::vec::Vec;
|
||||
use core::iter::{self, Map, Repeat, Zip};
|
||||
|
||||
use derive_where::derive_where;
|
||||
use digest::{Digest, Output};
|
||||
use hybrid_array::Array;
|
||||
use hybrid_array::typenum::Unsigned;
|
||||
use rand_core::{TryCryptoRng, TryRng};
|
||||
use digest::core_api::BlockSizeUser;
|
||||
use digest::{Digest, Output, OutputSizeUser};
|
||||
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256};
|
||||
use generic_array::GenericArray;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use crate::common::{
|
||||
BlindedElement, EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE,
|
||||
derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2,
|
||||
server_evaluate_hash_input, verify_proof,
|
||||
server_evaluate_hash_input, verify_proof, BlindedElement, EvaluationElement, Mode,
|
||||
PreparedEvaluationElement, Proof, STR_FINALIZE,
|
||||
};
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::serialization::serde::{Element, Scalar};
|
||||
@@ -41,7 +42,11 @@ use crate::{CipherSuite, Error, Group, Result};
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct VoprfClient<CS: CipherSuite> {
|
||||
pub struct VoprfClient<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
#[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>"))]
|
||||
@@ -57,7 +62,11 @@ pub struct VoprfClient<CS: CipherSuite> {
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct VoprfServer<CS: CipherSuite> {
|
||||
pub struct VoprfServer<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
#[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>"))]
|
||||
@@ -69,17 +78,21 @@ pub struct VoprfServer<CS: CipherSuite> {
|
||||
// =================== //
|
||||
/////////////////////////
|
||||
|
||||
impl<CS: CipherSuite> VoprfClient<CS> {
|
||||
impl<CS: CipherSuite> VoprfClient<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// 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>(
|
||||
pub fn blind<R: RngCore + CryptoRng>(
|
||||
input: &[u8],
|
||||
blinding_factor_rng: &mut R,
|
||||
) -> Result<VoprfClientBlindResult<CS>> {
|
||||
let blind = CS::Group::random_scalar(blinding_factor_rng)?;
|
||||
let blind = CS::Group::random_scalar(blinding_factor_rng);
|
||||
Self::deterministic_blind_unchecked_inner(input, blind)
|
||||
}
|
||||
|
||||
@@ -184,21 +197,25 @@ impl<CS: CipherSuite> VoprfClient<CS> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Only used for test functions
|
||||
// Only used for test functions
|
||||
#[cfg(test)]
|
||||
pub fn get_blind(&self) -> <CS::Group as Group>::Scalar {
|
||||
self.blind
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> VoprfServer<CS> {
|
||||
impl<CS: CipherSuite> VoprfServer<CS>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// Produces a new instance of a [VoprfServer] 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)?;
|
||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self> {
|
||||
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
|
||||
rng.fill_bytes(&mut seed);
|
||||
// This can't fail as the hash output is type constrained.
|
||||
Self::new_from_seed(&seed, &[])
|
||||
}
|
||||
@@ -229,7 +246,7 @@ impl<CS: CipherSuite> VoprfServer<CS> {
|
||||
Ok(Self { sk, pk })
|
||||
}
|
||||
|
||||
/// Only used for tests
|
||||
// Only used for tests
|
||||
#[cfg(test)]
|
||||
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
|
||||
self.sk
|
||||
@@ -238,7 +255,7 @@ impl<CS: CipherSuite> VoprfServer<CS> {
|
||||
/// Computes the second step for the multiplicative blinding version of
|
||||
/// DH-OPRF. This message is sent from the server (who holds the OPRF key)
|
||||
/// to the client.
|
||||
pub fn blind_evaluate<R: TryRng + TryCryptoRng>(
|
||||
pub fn blind_evaluate<R: RngCore + CryptoRng>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
blinded_element: &BlindedElement<CS>,
|
||||
@@ -271,7 +288,7 @@ impl<CS: CipherSuite> VoprfServer<CS> {
|
||||
/// [`Error::Batch`] if the number of `blinded_elements` and
|
||||
/// `evaluation_elements` don't match or is longer then [`u16::MAX`]
|
||||
#[cfg(feature = "alloc")]
|
||||
pub fn batch_blind_evaluate<'a, R: TryRng + TryCryptoRng, I>(
|
||||
pub fn batch_blind_evaluate<'a, R: RngCore + CryptoRng, I>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
blinded_elements: &'a I,
|
||||
@@ -322,7 +339,7 @@ impl<CS: CipherSuite> VoprfServer<CS> {
|
||||
pub fn batch_blind_evaluate_finish<
|
||||
'a,
|
||||
'b,
|
||||
R: TryRng + TryCryptoRng,
|
||||
R: RngCore + CryptoRng,
|
||||
IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator,
|
||||
IE,
|
||||
>(
|
||||
@@ -343,14 +360,14 @@ impl<CS: CipherSuite> VoprfServer<CS> {
|
||||
g,
|
||||
self.pk,
|
||||
blinded_elements.map(|element| element.0),
|
||||
evaluation_elements.into_iter().map(|element| element.0.0),
|
||||
evaluation_elements.into_iter().map(|element| element.0 .0),
|
||||
Mode::Voprf,
|
||||
)?;
|
||||
|
||||
let messages = evaluation_elements.into_iter().map(<fn(
|
||||
&PreparedEvaluationElement<CS>,
|
||||
) -> EvaluationElement<CS>>::from(
|
||||
|element| EvaluationElement(element.0.0),
|
||||
|element| EvaluationElement(element.0 .0),
|
||||
));
|
||||
|
||||
Ok(VoprfServerBatchEvaluateFinishResult { messages, proof })
|
||||
@@ -385,7 +402,11 @@ impl<CS: CipherSuite> VoprfServer<CS> {
|
||||
|
||||
/// 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 VoprfClientBlindResult<CS: CipherSuite> {
|
||||
pub struct VoprfClientBlindResult<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// The state to be persisted on the client
|
||||
pub state: VoprfClient<CS>,
|
||||
/// The message to send to the server
|
||||
@@ -402,7 +423,11 @@ pub type VoprfClientBatchFinalizeResult<'a, C, I, II, IC, IM> = FinalizeAfterUnb
|
||||
|
||||
/// 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 VoprfServerEvaluateResult<CS: CipherSuite> {
|
||||
pub struct VoprfServerEvaluateResult<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// The message to send to the client
|
||||
pub message: EvaluationElement<CS>,
|
||||
/// The proof for the client to verify
|
||||
@@ -412,7 +437,11 @@ pub struct VoprfServerEvaluateResult<CS: CipherSuite> {
|
||||
/// 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 VoprfServerBatchEvaluateResult<CS: CipherSuite> {
|
||||
pub struct VoprfServerBatchEvaluateResult<CS: CipherSuite>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// The messages to send to the client
|
||||
pub messages: Vec<EvaluationElement<CS>>,
|
||||
/// The proof for the client to verify
|
||||
@@ -443,6 +472,8 @@ pub type VoprfServerBatchEvaluateFinishedMessages<'a, CS, I> = Map<
|
||||
#[derive_where(Debug; <&'a I as IntoIterator>::IntoIter, <CS::Group as Group>::Scalar)]
|
||||
pub struct VoprfServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
&'a I: IntoIterator<Item = &'a PreparedEvaluationElement<CS>>,
|
||||
{
|
||||
/// The [`EvaluationElement`]s to send to the client
|
||||
@@ -480,6 +511,8 @@ fn verifiable_unblind<'a, CS: 'a + CipherSuite, IC, IM>(
|
||||
proof: &Proof<CS>,
|
||||
) -> Result<VoprfUnblindResult<'a, CS, IC, IM>>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
&'a IC: 'a + IntoIterator<Item = &'a VoprfClient<CS>>,
|
||||
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
|
||||
@@ -521,7 +554,11 @@ fn finalize_after_unblind<
|
||||
IE: 'a + Iterator<Item = (I, <CS::Group as Group>::Elem)>,
|
||||
>(
|
||||
inputs_and_unblinded_elements: IE,
|
||||
) -> FinalizeAfterUnblindResult<'a, CS, I, IE> {
|
||||
) -> FinalizeAfterUnblindResult<'a, CS, I, IE>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
inputs_and_unblinded_elements.map(|(input, unblinded_element)| {
|
||||
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
|
||||
|
||||
@@ -546,22 +583,29 @@ fn finalize_after_unblind<
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::ops::Add;
|
||||
use core::ptr;
|
||||
|
||||
use ::alloc::vec;
|
||||
use ::alloc::vec::Vec;
|
||||
use rand::rngs::SysRng;
|
||||
use generic_array::typenum::Sum;
|
||||
use generic_array::ArrayLength;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use super::*;
|
||||
use crate::Group;
|
||||
use crate::common::{Dst, STR_HASH_TO_GROUP};
|
||||
use crate::Group;
|
||||
|
||||
fn prf<CS: CipherSuite>(
|
||||
input: &[u8],
|
||||
key: <CS::Group as Group>::Scalar,
|
||||
mode: Mode,
|
||||
) -> Output<CS::Hash> {
|
||||
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, mode);
|
||||
) -> Output<CS::Hash>
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
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;
|
||||
@@ -572,9 +616,13 @@ mod tests {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn verifiable_retrieval<CS: CipherSuite>() {
|
||||
fn verifiable_retrieval<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let input = b"input";
|
||||
let mut rng = SysRng;
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
|
||||
@@ -591,15 +639,19 @@ mod tests {
|
||||
assert_eq!(client_finalize_result, res2);
|
||||
}
|
||||
|
||||
fn verifiable_batch_retrieval<CS: CipherSuite>() {
|
||||
let mut rng = SysRng;
|
||||
fn verifiable_batch_retrieval<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let mut rng = OsRng;
|
||||
let mut inputs = vec![];
|
||||
let mut client_states = vec![];
|
||||
let mut client_messages = vec![];
|
||||
let num_iterations = 10;
|
||||
for _ in 0..num_iterations {
|
||||
let mut input = [0u8; 32];
|
||||
rng.try_fill_bytes(&mut input).unwrap();
|
||||
rng.fill_bytes(&mut input);
|
||||
let client_blind_result = VoprfClient::<CS>::blind(&input, &mut rng).unwrap();
|
||||
inputs.push(input);
|
||||
client_states.push(client_blind_result.state);
|
||||
@@ -635,15 +687,19 @@ mod tests {
|
||||
assert_eq!(client_finalize_result, res2);
|
||||
}
|
||||
|
||||
fn verifiable_batch_bad_public_key<CS: CipherSuite>() {
|
||||
let mut rng = SysRng;
|
||||
fn verifiable_batch_bad_public_key<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let mut rng = OsRng;
|
||||
let mut inputs = vec![];
|
||||
let mut client_states = vec![];
|
||||
let mut client_messages = vec![];
|
||||
let num_iterations = 10;
|
||||
for _ in 0..num_iterations {
|
||||
let mut input = [0u8; 32];
|
||||
rng.try_fill_bytes(&mut input).unwrap();
|
||||
rng.fill_bytes(&mut input);
|
||||
let client_blind_result = VoprfClient::<CS>::blind(&input, &mut rng).unwrap();
|
||||
inputs.push(input);
|
||||
client_states.push(client_blind_result.state);
|
||||
@@ -662,7 +718,7 @@ mod tests {
|
||||
.unwrap();
|
||||
let messages: Vec<_> = messages.collect();
|
||||
let wrong_pk = {
|
||||
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, Mode::Oprf);
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, Mode::Oprf);
|
||||
// Choose a group element that is unlikely to be the right public key
|
||||
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
|
||||
};
|
||||
@@ -671,14 +727,18 @@ mod tests {
|
||||
assert!(client_finalize_result.is_err());
|
||||
}
|
||||
|
||||
fn verifiable_bad_public_key<CS: CipherSuite>() {
|
||||
fn verifiable_bad_public_key<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let input = b"input";
|
||||
let mut rng = SysRng;
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
|
||||
let wrong_pk = {
|
||||
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, Mode::Oprf);
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, Mode::Oprf);
|
||||
// Choose a group element that is unlikely to be the right public key
|
||||
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
|
||||
};
|
||||
@@ -691,9 +751,13 @@ mod tests {
|
||||
assert!(client_finalize_result.is_err());
|
||||
}
|
||||
|
||||
fn verifiable_server_evaluate<CS: CipherSuite>() {
|
||||
fn verifiable_server_evaluate<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
let input = b"input";
|
||||
let mut rng = SysRng;
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
|
||||
@@ -720,9 +784,15 @@ mod tests {
|
||||
assert!(client_finalize != server_evaluate);
|
||||
}
|
||||
|
||||
fn zeroize_voprf_client<CS: CipherSuite>() {
|
||||
fn zeroize_voprf_client<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
|
||||
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ElemLen>: ArrayLength<u8>,
|
||||
{
|
||||
let input = b"input";
|
||||
let mut rng = SysRng;
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
|
||||
let mut state = client_blind_result.state;
|
||||
@@ -734,9 +804,17 @@ mod tests {
|
||||
assert!(message.serialize().iter().all(|&x| x == 0));
|
||||
}
|
||||
|
||||
fn zeroize_voprf_server<CS: CipherSuite>() {
|
||||
fn zeroize_voprf_server<CS: CipherSuite>()
|
||||
where
|
||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
|
||||
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ElemLen>: ArrayLength<u8>,
|
||||
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ScalarLen>,
|
||||
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ScalarLen>: ArrayLength<u8>,
|
||||
{
|
||||
let input = b"input";
|
||||
let mut rng = SysRng;
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
|
||||
|
||||
Reference in New Issue
Block a user