Compare commits
50
Commits
v0.5.0-pre.1
...
v1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
201fb57d9f
|
||
|
|
ef59638de0
|
||
|
|
313f1b66bb
|
||
|
|
0bbf8fbfb5
|
||
|
|
1451f937ca
|
||
|
|
eb00b86000
|
||
|
|
385ee9b1cc
|
||
|
|
51157080fc
|
||
|
|
617fc0241c
|
||
|
|
9f9dc23aa1
|
||
|
|
405c1901ef
|
||
|
|
3ce3ac8817
|
||
|
|
d42e7948a1
|
||
|
|
0a7dc184ca | ||
|
|
f5b7e689e7 | ||
|
|
a22d46fd96 | ||
|
|
f23cdfab2d | ||
|
|
256ab7bc52 | ||
|
|
eb55e9f5b5 | ||
|
|
e944f9db3b | ||
|
|
f3f4fef0e9 | ||
|
|
23aa7813e7 | ||
|
|
0473d9db68 | ||
|
|
f0531f0812 | ||
|
|
c93884aca3 | ||
|
|
40769f7eca | ||
|
|
1b67086028 | ||
|
|
68cc7d3709 | ||
|
|
59e3fedb21 | ||
|
|
8da56845b8 | ||
|
|
c0162ec8d9 | ||
|
|
ee91c9776c | ||
|
|
0fdfdfdaee | ||
|
|
eafa134c94 | ||
|
|
209b957ae4 | ||
|
|
f79ebf9844 | ||
|
|
20a35da7ba | ||
|
|
4bd2cf466e | ||
|
|
829c6add0f | ||
|
|
8b895cc631 | ||
|
|
83eb78b232 | ||
|
|
5badeff8d2 | ||
|
|
40d81294db | ||
|
|
8363d26f6f | ||
|
|
5bce3e3206 | ||
|
|
2787151e1d | ||
|
|
0409db6f40 | ||
|
|
74eaebe446 | ||
|
|
c8de51672b | ||
|
|
daa8dc048f |
+5
-4
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
//
|
//
|
||||||
// This source code is licensed under both the MIT license found in the
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
name: Rust CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
types: [opened, reopened, synchronize]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ci-${{ gitea.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
fmt:
|
||||||
|
name: cargo fmt
|
||||||
|
runs-on: linux_amd64
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
components: rustfmt
|
||||||
|
- name: Run cargo fmt
|
||||||
|
run: cargo fmt --all -- --check
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
name: cargo clippy
|
||||||
|
runs-on: linux_amd64
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
components: clippy
|
||||||
|
- name: Run cargo clippy
|
||||||
|
run: cargo clippy --all-features --all-targets -- -D warnings
|
||||||
|
- name: Run cargo doc
|
||||||
|
run: cargo doc --no-deps --document-private-items --features danger,std
|
||||||
|
env:
|
||||||
|
RUSTDOCFLAGS: -D warnings
|
||||||
|
|
||||||
|
test:
|
||||||
|
name: test (${{ matrix.toolchain }} / ${{ matrix.backend_feature || 'no backend' }} / ${{ matrix.frontend_feature || 'no frontend' }})
|
||||||
|
runs-on: linux_amd64
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
backend_feature:
|
||||||
|
- --features ristretto255-ciphersuite
|
||||||
|
- ""
|
||||||
|
frontend_feature:
|
||||||
|
- ""
|
||||||
|
- --features danger
|
||||||
|
- --features serde
|
||||||
|
toolchain:
|
||||||
|
- stable
|
||||||
|
- "1.87.0"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
|
- uses: dtolnay/rust-toolchain@${{ matrix.toolchain }}
|
||||||
|
- name: Run cargo test
|
||||||
|
run: cargo test --no-default-features --lib --tests ${{ matrix.backend_feature }}
|
||||||
|
- name: Run cargo test with alloc
|
||||||
|
run: cargo test --no-default-features --lib --tests ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }} --features alloc
|
||||||
|
- name: Run cargo test with std
|
||||||
|
run: cargo test --no-default-features --lib --tests ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }} --features std
|
||||||
|
- name: Run cargo test with all features
|
||||||
|
run: cargo test --all-features
|
||||||
|
|
||||||
|
build-no-std:
|
||||||
|
name: no-std (${{ matrix.target }} / ${{ matrix.backend_feature || 'no backend' }})
|
||||||
|
runs-on: linux_amd64
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target:
|
||||||
|
- wasm32-unknown-unknown
|
||||||
|
- thumbv6m-none-eabi
|
||||||
|
backend_feature:
|
||||||
|
- ""
|
||||||
|
- --features ristretto255-ciphersuite
|
||||||
|
frontend_feature:
|
||||||
|
- ""
|
||||||
|
- --features danger
|
||||||
|
- --features serde
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: ${{ matrix.target }}
|
||||||
|
- name: Build no-std
|
||||||
|
run: cargo build --verbose --target=${{ matrix.target }} --no-default-features ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }}
|
||||||
|
|
||||||
|
audit:
|
||||||
|
name: cargo audit
|
||||||
|
runs-on: linux_amd64
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: Install cargo-audit
|
||||||
|
run: cargo install cargo-audit
|
||||||
|
- name: Run cargo audit
|
||||||
|
run: cargo audit -D warnings
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
name: Publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [ published ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: linux_amd64
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
|
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Login to crates.io
|
||||||
|
run: cargo login $CRATES_IO_TOKEN
|
||||||
|
env:
|
||||||
|
CRATES_IO_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
|
||||||
|
|
||||||
|
- name: Dry run publish
|
||||||
|
run: cargo publish --dry-run --manifest-path Cargo.toml
|
||||||
|
|
||||||
|
- name: Publish
|
||||||
|
run: cargo publish --manifest-path Cargo.toml
|
||||||
|
env:
|
||||||
|
CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
version: 2
|
|
||||||
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: cargo
|
|
||||||
directory: /
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
|
|
||||||
- package-ecosystem: github-actions
|
|
||||||
directory: /
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
name: Rust CI
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
pull_request:
|
|
||||||
types: [opened, repoened, synchronize]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
cargo-audit:
|
|
||||||
name: Audit
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Cache cargo-audit
|
|
||||||
uses: actions/cache@v3
|
|
||||||
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@v3
|
|
||||||
|
|
||||||
- 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.57.0
|
|
||||||
name: test
|
|
||||||
steps:
|
|
||||||
- name: Checkout sources
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- 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@v3
|
|
||||||
- uses: hecrj/setup-rust-action@v1
|
|
||||||
- run: rustup target add ${{ matrix.target }}
|
|
||||||
- run: cargo build --verbose --target=${{ matrix.target }} --no-default-features ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }}
|
|
||||||
|
|
||||||
|
|
||||||
clippy:
|
|
||||||
name: cargo clippy
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout sources
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Install stable toolchain
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: stable
|
|
||||||
override: true
|
|
||||||
components: clippy
|
|
||||||
|
|
||||||
- name: Run cargo clippy
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: clippy
|
|
||||||
args: --all-targets -- -D warnings
|
|
||||||
|
|
||||||
- name: Run cargo doc
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
env:
|
|
||||||
RUSTDOCFLAGS: -D warnings
|
|
||||||
with:
|
|
||||||
command: doc
|
|
||||||
args: --no-deps --document-private-items --features danger,std
|
|
||||||
|
|
||||||
|
|
||||||
rustfmt:
|
|
||||||
name: cargo fmt
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout sources
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- 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@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cargo/.crates.toml
|
|
||||||
~/.cargo/.crates2.json
|
|
||||||
~/.cargo/bin/taplo
|
|
||||||
key: taplo
|
|
||||||
|
|
||||||
- name: Install Taplo
|
|
||||||
run: cargo install taplo-cli
|
|
||||||
|
|
||||||
- name: Checkout sources
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Run Taplo
|
|
||||||
run: taplo fmt --check
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
name: Publish
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish:
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: hecrj/setup-rust-action@v1
|
|
||||||
with:
|
|
||||||
rust-version: ${{ matrix.rust }}
|
|
||||||
- uses: actions/checkout@master
|
|
||||||
- name: Login to crates.io
|
|
||||||
run: cargo login $CRATES_IO_TOKEN
|
|
||||||
env:
|
|
||||||
CRATES_IO_TOKEN: ${{ secrets.crates_io_token }}
|
|
||||||
- name: Dry run publish voprf
|
|
||||||
run: cargo publish --dry-run --manifest-path Cargo.toml
|
|
||||||
- name: Publish voprf
|
|
||||||
run: cargo publish --manifest-path Cargo.toml
|
|
||||||
env:
|
|
||||||
CARGO_REGISTRY_TOKEN: ${{ secrets.crates_io_token }}
|
|
||||||
+7
-5
@@ -1,10 +1,12 @@
|
|||||||
# Generated by Cargo
|
# Generated by Cargo
|
||||||
# will have compiled files and executables
|
# will have compiled files and executables
|
||||||
/target/
|
debug/
|
||||||
|
target/
|
||||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
pkg/
|
||||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
demo/
|
||||||
Cargo.lock
|
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
||||||
|
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||||
|
*.pdb
|
||||||
|
|||||||
@@ -1,9 +1,99 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 1.0.0 (July 8, 2026)
|
||||||
|
|
||||||
|
* Deduplicated serialization with `impl_serde_scalar`, `impl_serde_elem`, and `impl_serde_scalar_elem` macros
|
||||||
|
* Moved `finalize_after_unblind` to `common.rs`, shared by OPRF and VOPRF
|
||||||
|
* Added shared test helpers (`test_all_curves` macro, `prf` function)
|
||||||
|
* Updated dependencies to stable releases
|
||||||
|
|
||||||
|
## 1.0.0-rc.1 (July 3, 2026)
|
||||||
|
|
||||||
|
* Reject trailing bytes in all `deserialize` methods
|
||||||
|
* Reject identity element in `deterministic_blind_unchecked` to prevent blinding bypass
|
||||||
|
* Added roundtrip, trailing bytes, truncated, and empty input tests for serialization
|
||||||
|
|
||||||
|
## 1.0.0-rc.0 (July 2, 2026)
|
||||||
|
|
||||||
|
* Added missing license in Cargo manifest
|
||||||
|
* Implement `zeroize` feature for `digest`, `hybrid-array` and `sha2`
|
||||||
|
* Replaced license appendix in files while keeping original copyright
|
||||||
|
|
||||||
|
## 1.0.0-pre.1 (July 2, 2026)
|
||||||
|
|
||||||
|
* Simplified ciphersuite trait
|
||||||
|
* Moved multiplication operator to SecurityLevel type in Group trait
|
||||||
|
|
||||||
|
## 1.0.0-pre.0 (June 29, 2026)
|
||||||
|
|
||||||
|
Forked from [facebook/voprf](https://github.com/facebook/voprf/) at `0.6.0-pre.1`.
|
||||||
|
|
||||||
|
* MSRV bumped to 1.87
|
||||||
|
* Migrated from `elliptic-curve 0.13` to `0.14`
|
||||||
|
* Replaced `generic-array` with `hybrid-array 0.4`
|
||||||
|
* Updated `digest` to 0.11, `rand_core` to 0.10, `rand` to 0.10, `sha2` to 0.11
|
||||||
|
* Updated `p256`, `p384`, `p521` to `0.14`
|
||||||
|
* Replaced `elliptic-curve/hash2curve` feature with standalone `hash2curve 0.14` crate
|
||||||
|
* Removed `VoprfParameters` dependency to be replaced with `OprfParameters` + `GroupDigest`
|
||||||
|
* Added `SecurityLevel` associated type to `Group` trait for generic hash bounds
|
||||||
|
* Added `OkmLen` associated type to `Group` trait (`MapToCurve::Length`)
|
||||||
|
* Updated `hash_to_scalar` to use `MapToCurve::Length` as OKM length per RFC 9380
|
||||||
|
* Updated `random_scalar` for deterministic byte consumption with `rand_core 0.10`
|
||||||
|
* Auto-impl `CipherSuite` for any `OprfParameters + Group` type via `OprfHash<T>`
|
||||||
|
|
||||||
|
## 0.6.0-pre.1 (April 6, 2026)
|
||||||
|
|
||||||
|
* MSRV bumped to 1.85
|
||||||
|
* Updated rand_core dependency to 0.9
|
||||||
|
* Updated rand dependency to 0.9
|
||||||
|
* Updated subtle dependency to 2.6
|
||||||
|
* Fixed docs issue
|
||||||
|
|
||||||
|
## 0.6.0-pre.0 (November 8, 2025)
|
||||||
|
|
||||||
|
* MSRV bumped to 1.83
|
||||||
|
* Updated Ristretto255 random scalar generation
|
||||||
|
* Updated generic-array to v1
|
||||||
|
|
||||||
|
## 0.5.0 (March 6, 2024)
|
||||||
|
|
||||||
|
* Just a version bump from v0.5.0-pre.7
|
||||||
|
|
||||||
|
## 0.5.0-pre.7 (January 11, 2024)
|
||||||
|
|
||||||
|
* Updated to be in sync with RFC 9497
|
||||||
|
|
||||||
|
## 0.5.0-pre.6 (July 24, 2023)
|
||||||
|
|
||||||
|
* Updated curve25519-dalek dependency to 4
|
||||||
|
|
||||||
|
## 0.5.0-pre.5 (June 27, 2023)
|
||||||
|
|
||||||
|
* Updated curve25519-dalek dependency to 4.0.0-rc.3
|
||||||
|
|
||||||
|
## 0.5.0-pre.4 (May 20, 2023)
|
||||||
|
|
||||||
|
* Updated curve25519-dalek dependency to 4.0.0-rc.2
|
||||||
|
|
||||||
|
## 0.5.0-pre.3 (March 4, 2023)
|
||||||
|
|
||||||
|
* Updated to be in sync with draft-irtf-cfrg-voprf-19
|
||||||
|
* Increased MSRV to 1.65
|
||||||
|
* Updated p256 dependency to v0.13
|
||||||
|
* Added p384 tests
|
||||||
|
|
||||||
|
## 0.5.0-pre.2 (February 3, 2023)
|
||||||
|
|
||||||
|
* Increased MSRV to 1.60
|
||||||
|
* Updated p256 dependency to v0.12
|
||||||
|
* Updated curve25519-dalek dependency to 4.0.0-rc.1
|
||||||
|
|
||||||
## 0.5.0-pre.1 (December 19, 2022)
|
## 0.5.0-pre.1 (December 19, 2022)
|
||||||
|
|
||||||
* Updated curve25519-dalek dependency to 4.0.0-pre.5
|
* Updated curve25519-dalek dependency to 4.0.0-pre.5
|
||||||
|
|
||||||
## 0.4.0 (September 15, 2022)
|
## 0.4.0 (September 15, 2022)
|
||||||
|
|
||||||
* Updated to be in sync with draft-irtf-cfrg-voprf-11, with
|
* Updated to be in sync with draft-irtf-cfrg-voprf-11, with
|
||||||
the addition of the POPRF mode
|
the addition of the POPRF mode
|
||||||
* Adds the evaluate() function to the servers to calculate the output of the OPRF
|
* Adds the evaluate() function to the servers to calculate the output of the OPRF
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
# Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
In the interest of fostering an open and welcoming environment, we as
|
|
||||||
contributors and maintainers pledge to make participation in our project and
|
|
||||||
our community a harassment-free experience for everyone, regardless of age, body
|
|
||||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
|
||||||
level of experience, education, socio-economic status, nationality, personal
|
|
||||||
appearance, race, religion, or sexual identity and orientation.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment
|
|
||||||
include:
|
|
||||||
|
|
||||||
* Using welcoming and inclusive language
|
|
||||||
* Being respectful of differing viewpoints and experiences
|
|
||||||
* Gracefully accepting constructive criticism
|
|
||||||
* Focusing on what is best for the community
|
|
||||||
* Showing empathy towards other community members
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
|
||||||
advances
|
|
||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing others' private information, such as a physical or electronic
|
|
||||||
address, without explicit permission
|
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
|
||||||
professional setting
|
|
||||||
|
|
||||||
## Our Responsibilities
|
|
||||||
|
|
||||||
Project maintainers are responsible for clarifying the standards of acceptable
|
|
||||||
behavior and are expected to take appropriate and fair corrective action in
|
|
||||||
response to any instances of unacceptable behavior.
|
|
||||||
|
|
||||||
Project maintainers have the right and responsibility to remove, edit, or
|
|
||||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
|
||||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
|
||||||
permanently any contributor for other behaviors that they deem inappropriate,
|
|
||||||
threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies within all project spaces, and it also applies when
|
|
||||||
an individual is representing the project or its community in public spaces.
|
|
||||||
Examples of representing a project or community include using an official
|
|
||||||
project e-mail address, posting via an official social media account, or acting
|
|
||||||
as an appointed representative at an online or offline event. Representation of
|
|
||||||
a project may be further defined and clarified by project maintainers.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
||||||
reported by contacting the project team at <opensource-conduct@fb.com>. All
|
|
||||||
complaints will be reviewed and investigated and will result in a response that
|
|
||||||
is deemed necessary and appropriate to the circumstances. The project team is
|
|
||||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
|
||||||
Further details of specific enforcement policies may be posted separately.
|
|
||||||
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
|
||||||
faith may face temporary or permanent repercussions as determined by other
|
|
||||||
members of the project's leadership.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
|
||||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
|
||||||
|
|
||||||
[homepage]: https://www.contributor-covenant.org
|
|
||||||
|
|
||||||
For answers to common questions about this code of conduct, see
|
|
||||||
https://www.contributor-covenant.org/faq
|
|
||||||
+3
-21
@@ -2,29 +2,11 @@
|
|||||||
We want to make contributing to this project as easy and transparent as
|
We want to make contributing to this project as easy and transparent as
|
||||||
possible.
|
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
|
## Issues
|
||||||
We use GitHub issues to track public bugs. Please ensure your description is
|
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.
|
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
|
## License
|
||||||
By contributing to voprf, you agree that your contributions will be
|
By contributing to voprf-vx, you agree that your contributions will be
|
||||||
licensed under the LICENSE file in the root directory of this source tree.
|
licensed under both the LICENSE-MIT and LICENSE-APACHE files in the root
|
||||||
|
directory of this source tree.
|
||||||
|
|||||||
Generated
+844
@@ -0,0 +1,844 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base16ct"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit-set"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
|
||||||
|
dependencies = [
|
||||||
|
"bit-vec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit-vec"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
|
||||||
|
dependencies = [
|
||||||
|
"hybrid-array",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chacha20"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"rand_core 0.10.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cmov"
|
||||||
|
version = "0.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-oid"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpubits"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-bigint"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a52aa3fcda4e6302a9f48734f234d35d4721b96f8fe07d073f07ce9df4f0271"
|
||||||
|
dependencies = [
|
||||||
|
"cpubits",
|
||||||
|
"ctutils",
|
||||||
|
"hybrid-array",
|
||||||
|
"num-traits",
|
||||||
|
"rand_core 0.10.1",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
|
||||||
|
dependencies = [
|
||||||
|
"hybrid-array",
|
||||||
|
"rand_core 0.10.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctutils"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
|
||||||
|
dependencies = [
|
||||||
|
"cmov",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "curve25519-dalek"
|
||||||
|
version = "5.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"curve25519-dalek-derive",
|
||||||
|
"digest",
|
||||||
|
"fiat-crypto",
|
||||||
|
"rand_core 0.10.1",
|
||||||
|
"rustc_version",
|
||||||
|
"serde",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "curve25519-dalek-derive"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "der"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b"
|
||||||
|
dependencies = [
|
||||||
|
"const-oid",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive-where"
|
||||||
|
version = "1.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"const-oid",
|
||||||
|
"crypto-common",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "displaydoc"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "elliptic-curve"
|
||||||
|
version = "0.14.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d65aa39b3a5c1c9c1b745c9a019234bb7a21b77abcb4f4d266d706e2d577d65"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"crypto-bigint",
|
||||||
|
"crypto-common",
|
||||||
|
"digest",
|
||||||
|
"ff",
|
||||||
|
"group",
|
||||||
|
"hybrid-array",
|
||||||
|
"rand_core 0.10.1",
|
||||||
|
"sec1",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ff"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1f686ab92a9fb0eaf188f6c6c87b89490baa6fdb0db4544ba4dc47f7942489f"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.10.1",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fiat-crypto"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi 5.3.0",
|
||||||
|
"wasip2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi 6.0.0",
|
||||||
|
"rand_core 0.10.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "group"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fd1a1c7a5206c5b7a3f5a0d7ccd3ff85d0c8f5133d62a02680255b0004af5f4"
|
||||||
|
dependencies = [
|
||||||
|
"ff",
|
||||||
|
"rand_core 0.10.1",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hash2curve"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1eaf40612d7d854743e7189228a6d528f0f6e8502cf6a0cb831d28a218b7f3f6"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
"elliptic-curve",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hybrid-array"
|
||||||
|
version = "0.4.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"subtle",
|
||||||
|
"typenum",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.186"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "p256"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2c9239b2dbc807adbbe147e8cf72ea7450c3a0aabe62cb8e75ff4ec22e1f72a"
|
||||||
|
dependencies = [
|
||||||
|
"elliptic-curve",
|
||||||
|
"hash2curve",
|
||||||
|
"primefield",
|
||||||
|
"primeorder",
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "p384"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d17b851e6b3e378ab4ecb07fa2ed23f4d15f075735f8fec9fa1e7bdce5f8301f"
|
||||||
|
dependencies = [
|
||||||
|
"elliptic-curve",
|
||||||
|
"fiat-crypto",
|
||||||
|
"hash2curve",
|
||||||
|
"primefield",
|
||||||
|
"primeorder",
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "p521"
|
||||||
|
version = "0.14.0-rc.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff42e4ace5424e3b6d7cb82514be89866b85af87015f80341e37dcc21a66ce6e"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"elliptic-curve",
|
||||||
|
"hash2curve",
|
||||||
|
"primefield",
|
||||||
|
"primeorder",
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "primefield"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c555a6e4eb7d4e158fcb028c835c3b8642206ddc279b5c6b202ef9a8bdb592f4"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-bigint",
|
||||||
|
"crypto-common",
|
||||||
|
"ff",
|
||||||
|
"rand_core 0.10.1",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "primeorder"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c9f42978c78a00e3d68f69fc03e57a234debae69da4020a4fb588fcdcd07b06"
|
||||||
|
dependencies = [
|
||||||
|
"elliptic-curve",
|
||||||
|
"primefield",
|
||||||
|
"wnaf",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.106"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proptest"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744"
|
||||||
|
dependencies = [
|
||||||
|
"bit-set",
|
||||||
|
"bit-vec",
|
||||||
|
"bitflags",
|
||||||
|
"num-traits",
|
||||||
|
"rand 0.9.4",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_xorshift",
|
||||||
|
"regex-syntax",
|
||||||
|
"rusty-fork",
|
||||||
|
"tempfile",
|
||||||
|
"unarray",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-error"
|
||||||
|
version = "1.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.46"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "5.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
|
||||||
|
dependencies = [
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core 0.9.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
|
||||||
|
dependencies = [
|
||||||
|
"chacha20",
|
||||||
|
"getrandom 0.4.3",
|
||||||
|
"rand_core 0.10.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core 0.9.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.3.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_xorshift"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.9.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.12.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rusty-fork"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"quick-error",
|
||||||
|
"tempfile",
|
||||||
|
"wait-timeout",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sec1"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4d"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"ctutils",
|
||||||
|
"der",
|
||||||
|
"hybrid-array",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_core"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.150"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
"zmij",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.118"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||||
|
dependencies = [
|
||||||
|
"fastrand",
|
||||||
|
"getrandom 0.4.3",
|
||||||
|
"once_cell",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.20.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unarray"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "voprf-vx"
|
||||||
|
version = "1.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"curve25519-dalek",
|
||||||
|
"derive-where",
|
||||||
|
"digest",
|
||||||
|
"displaydoc",
|
||||||
|
"elliptic-curve",
|
||||||
|
"hash2curve",
|
||||||
|
"hex",
|
||||||
|
"hybrid-array",
|
||||||
|
"p256",
|
||||||
|
"p384",
|
||||||
|
"p521",
|
||||||
|
"proptest",
|
||||||
|
"rand 0.10.2",
|
||||||
|
"rand_core 0.10.1",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sha2",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wait-timeout"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasip2"
|
||||||
|
version = "1.0.4+wasi-0.2.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen"
|
||||||
|
version = "0.57.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wnaf"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab12e7090f27e2ffd9322651492942d50c2926094af30601e1964337db39daf1"
|
||||||
|
dependencies = [
|
||||||
|
"ff",
|
||||||
|
"group",
|
||||||
|
"hybrid-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.53"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75726053136156d419e285b9b7eddaaea9e3fea6ce32eed44a89901f0bd98de1"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.53"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4714fd92cf900833d49538023a9b3915155210801d1c1169eba513b2addefd71"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zmij"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||||
+40
-33
@@ -1,60 +1,67 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["Kevin Lewi <[email protected]>"]
|
authors = ["VexaHub Developers", "Kevin Lewi <[email protected]>"]
|
||||||
categories = ["no-std", "algorithms", "cryptography"]
|
categories = ["no-std", "algorithms", "cryptography"]
|
||||||
description = "An implementation of a verifiable oblivious pseudorandom function (VOPRF)"
|
description = "An implementation of a verifiable oblivious pseudorandom function (VOPRF)"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
keywords = ["oprf"]
|
keywords = ["oprf", "voprf", "cryptography", "oblivious-prf"]
|
||||||
license = "MIT"
|
license = "MIT OR Apache-2.0"
|
||||||
name = "voprf"
|
name = "voprf-vx"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/novifinancial/voprf/"
|
repository = "https://github.com/vexahub/voprf-vx/"
|
||||||
rust-version = "1.57"
|
rust-version = "1.87"
|
||||||
version = "0.5.0-pre.1"
|
version = "1.0.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
alloc = []
|
alloc = []
|
||||||
danger = []
|
danger = []
|
||||||
default = ["ristretto255-ciphersuite", "serde"]
|
doctest = ["dep:p256", "dep:sha2"]
|
||||||
ristretto255 = ["curve25519-dalek", "generic-array/more_lengths"]
|
default = ["ristretto255-ciphersuite", "dep:serde"]
|
||||||
ristretto255-ciphersuite = ["ristretto255", "sha2"]
|
ristretto255 = ["dep:curve25519-dalek"]
|
||||||
serde = ["generic-array/serde", "serde_"]
|
ristretto255-ciphersuite = ["ristretto255", "dep:sha2"]
|
||||||
|
serde = ["curve25519-dalek?/serde", "hybrid-array/serde", "dep:serde"]
|
||||||
std = ["alloc"]
|
std = ["alloc"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
curve25519-dalek = { version = "=4.0.0-pre.5", default-features = false, features = [
|
curve25519-dalek = { version = "5", default-features = false, features = ["rand_core", "zeroize"], optional = true }
|
||||||
"rand_core",
|
|
||||||
], optional = true }
|
|
||||||
derive-where = { version = "1", features = ["zeroize-on-drop"] }
|
derive-where = { version = "1", features = ["zeroize-on-drop"] }
|
||||||
digest = "0.10"
|
digest = { version = "0.11", features = ["zeroize"] }
|
||||||
displaydoc = { version = "0.2", default-features = false }
|
displaydoc = { version = "0.2", default-features = false }
|
||||||
elliptic-curve = { version = "0.12", features = [
|
elliptic-curve = { version = "0.14", features = [
|
||||||
"hash2curve",
|
"sec1",
|
||||||
"sec1",
|
|
||||||
"voprf",
|
|
||||||
] }
|
] }
|
||||||
generic-array = "0.14"
|
hash2curve = "0.14"
|
||||||
rand_core = { version = "0.6", default-features = false }
|
hybrid-array = { version = "0.4", features = ["zeroize"] }
|
||||||
serde_ = { version = "1", package = "serde", default-features = false, features = [
|
rand_core = { version = "0.10", default-features = false }
|
||||||
"derive",
|
serde = { version = "1", default-features = false, features = [
|
||||||
|
"derive",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
sha2 = { version = "0.10", default-features = false, optional = true }
|
sha2 = { version = "0.11", default-features = false, features = ["zeroize"], optional = true }
|
||||||
subtle = { version = "2.3", default-features = false }
|
p256 = { version = "0.14", default-features = false, features = ["hash2curve", "oprf"], optional = true }
|
||||||
|
subtle = { version = "2.6", default-features = false }
|
||||||
zeroize = { version = "1.5", default-features = false }
|
zeroize = { version = "1.5", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
generic-array = { version = "0.14", features = ["more_lengths"] }
|
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
json = "0.12"
|
p256 = { version = "0.14", default-features = false, features = [
|
||||||
p256 = { version = "0.11", default-features = false, features = [
|
"hash2curve",
|
||||||
"hash2curve",
|
"oprf",
|
||||||
"voprf",
|
] }
|
||||||
|
p384 = { version = "0.14", default-features = false, features = [
|
||||||
|
"hash2curve",
|
||||||
|
"oprf",
|
||||||
|
] }
|
||||||
|
p521 = { version = "0.14.0-rc.15", default-features = false, features = [
|
||||||
|
"hash2curve",
|
||||||
|
"oprf",
|
||||||
] }
|
] }
|
||||||
proptest = "1"
|
proptest = "1"
|
||||||
rand = "0.8"
|
rand = "0.10"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
sha2 = "0.10"
|
serde_json = "1"
|
||||||
|
sha2 = "0.11"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
targets = []
|
targets = []
|
||||||
|
features = ["doctest"]
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
## License
|
|
||||||
|
|
||||||
Licensed under either of
|
|
||||||
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
|
||||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
|
||||||
at your option.
|
|
||||||
|
|
||||||
### Contribution
|
|
||||||
|
|
||||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
|
||||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall
|
|
||||||
be dual licensed as above, without any additional terms or conditions.
|
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
# voprf 
|
# voprf
|
||||||
An implementation of a (verifiable) oblivious pseudorandom function (VOPRF)
|
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.
|
A VOPRF is a verifiable oblivious pseudorandom function, a protocol between a client and a server. The regular (non-verifiable) OPRF is also supported in this implementation.
|
||||||
|
|
||||||
This implementation is based on the [Internet Draft for VOPRF](https://github.com/cfrg/draft-irtf-cfrg-voprf).
|
This implementation is based on [RFC 9497](https://www.rfc-editor.org/rfc/rfc9497).
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
The API can be found [here](https://docs.rs/voprf/) along with an example for usage.
|
The API can be found [here](https://docs.rs/voprf-vx/) along with an example for usage.
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
@@ -16,20 +16,24 @@ Installation
|
|||||||
Add the following line to the dependencies of your `Cargo.toml`:
|
Add the following line to the dependencies of your `Cargo.toml`:
|
||||||
|
|
||||||
```
|
```
|
||||||
voprf = "0.5.0-pre.1"
|
voprf-vx = "1.0.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Minimum Supported Rust Version
|
### Minimum Supported Rust Version
|
||||||
|
|
||||||
Rust **1.57** or higher.
|
Rust **1.87** or higher.
|
||||||
|
|
||||||
Contributors
|
Contributors
|
||||||
------------
|
------------
|
||||||
|
|
||||||
The author of this code is Kevin Lewi ([@kevinlewi](https://github.com/kevinlewi)).
|
This is a fork of [facebook/voprf](https://github.com/facebook/voprf/) maintained by [VexaHub](https://github.com/vexahub).
|
||||||
|
|
||||||
|
The original author is Kevin Lewi ([@kevinlewi](https://github.com/kevinlewi)).
|
||||||
To learn more about contributing to this project, [see this document](./CONTRIBUTING.md).
|
To learn more about contributing to this project, [see this document](./CONTRIBUTING.md).
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
This project is [licensed](./LICENSE) under either Apache 2.0 or MIT, at your option.
|
This project is dual-licensed under either the [MIT license](./LICENSE-MIT)
|
||||||
|
or the [Apache License, Version 2.0](./LICENSE-APACHE).
|
||||||
|
You may select, at your option, one of the above-listed licenses.
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": [
|
||||||
|
"config:recommended"
|
||||||
|
],
|
||||||
|
"dependencyDashboard": true,
|
||||||
|
"osvVulnerabilityAlerts": true,
|
||||||
|
"rangeStrategy": "auto",
|
||||||
|
"packageRules": [
|
||||||
|
{
|
||||||
|
"matchManagers": [
|
||||||
|
"cargo"
|
||||||
|
],
|
||||||
|
"groupName": "rust deps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchManagers": [
|
||||||
|
"cargo"
|
||||||
|
],
|
||||||
|
"matchUpdateTypes": [
|
||||||
|
"major"
|
||||||
|
],
|
||||||
|
"automerge": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lockFileMaintenance": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"configMigration": true
|
||||||
|
}
|
||||||
@@ -1,8 +1 @@
|
|||||||
format_code_in_doc_comments = true
|
|
||||||
format_strings = true
|
|
||||||
group_imports = "StdExternalCrate"
|
|
||||||
imports_granularity = "Module"
|
|
||||||
license_template_path = ".cargo/license.rs"
|
|
||||||
newline_style = "Unix"
|
newline_style = "Unix"
|
||||||
unstable_features = true
|
|
||||||
wrap_comments = true
|
|
||||||
|
|||||||
+28
-24
@@ -1,28 +1,28 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
//
|
// Copyright (c) VexaHub and contributors.
|
||||||
// This source code is licensed under both the MIT license found in the
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
||||||
// of this source tree.
|
|
||||||
|
|
||||||
//! Defines the CipherSuite trait to specify the underlying primitives for VOPRF
|
//! Defines the CipherSuite trait to specify the underlying primitives for VOPRF
|
||||||
|
|
||||||
use digest::core_api::BlockSizeUser;
|
|
||||||
use digest::{Digest, OutputSizeUser};
|
|
||||||
use elliptic_curve::VoprfParameters;
|
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
|
|
||||||
|
|
||||||
use crate::Group;
|
use crate::Group;
|
||||||
|
use digest::block_api::BlockSizeUser;
|
||||||
|
use digest::typenum::{IsLess, IsLessOrEqual, U256};
|
||||||
|
use digest::{FixedOutput, HashMarker, OutputSizeUser};
|
||||||
|
use hash2curve::{ExpandMsg, GroupDigest, MapToCurve, OprfParameters};
|
||||||
|
use hybrid_array::ArraySize;
|
||||||
|
use hybrid_array::typenum::{IsGreaterOrEqual, Prod, True, U2};
|
||||||
|
|
||||||
/// Configures the underlying primitives used in VOPRF
|
/// Configures the underlying primitives used in VOPRF
|
||||||
pub trait CipherSuite
|
pub trait CipherSuite
|
||||||
where
|
where
|
||||||
<Self::Hash as OutputSizeUser>::OutputSize:
|
<Self::Hash as OutputSizeUser>::OutputSize: ArraySize
|
||||||
IsLess<U256> + IsLessOrEqual<<Self::Hash as BlockSizeUser>::BlockSize>,
|
+ IsLess<U256>
|
||||||
|
+ IsLessOrEqual<<Self::Hash as BlockSizeUser>::BlockSize, Output = True>
|
||||||
|
+ IsGreaterOrEqual<Prod<<Self::Group as Group>::SecurityLevel, U2>, Output = True>,
|
||||||
{
|
{
|
||||||
/// The ciphersuite identifier as dictated by
|
/// The ciphersuite identifier as dictated by
|
||||||
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-voprf/>
|
/// <https://www.rfc-editor.org/rfc/rfc9497>
|
||||||
const ID: u16;
|
const ID: &'static [u8];
|
||||||
|
|
||||||
/// A finite cyclic group along with a point representation that allows some
|
/// 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`].
|
/// customization on how to hash an input to a curve point. See [`Group`].
|
||||||
@@ -30,19 +30,23 @@ where
|
|||||||
|
|
||||||
/// The main hash function to use (for HKDF computations and hashing
|
/// The main hash function to use (for HKDF computations and hashing
|
||||||
/// transcripts).
|
/// transcripts).
|
||||||
type Hash: BlockSizeUser + Digest;
|
type Hash: BlockSizeUser + Default + FixedOutput + HashMarker;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: VoprfParameters> CipherSuite for T
|
/// The hash function associated with a curve's OPRF `expand_message` implementation.
|
||||||
|
type OprfHash<T> =
|
||||||
|
<<T as GroupDigest>::ExpandMsg as ExpandMsg<<T as MapToCurve>::SecurityLevel>>::Hash;
|
||||||
|
|
||||||
|
impl<T: OprfParameters> CipherSuite for T
|
||||||
where
|
where
|
||||||
T: Group,
|
T: Group,
|
||||||
T::Hash: BlockSizeUser + Digest,
|
OprfHash<T>: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||||
<T::Hash as OutputSizeUser>::OutputSize:
|
<OprfHash<T> as OutputSizeUser>::OutputSize: ArraySize
|
||||||
IsLess<U256> + IsLessOrEqual<<T::Hash as BlockSizeUser>::BlockSize>,
|
+ IsLess<U256>
|
||||||
|
+ IsLessOrEqual<<OprfHash<T> as BlockSizeUser>::BlockSize, Output = True>
|
||||||
|
+ IsGreaterOrEqual<Prod<<T as Group>::SecurityLevel, U2>, Output = True>,
|
||||||
{
|
{
|
||||||
const ID: u16 = T::ID;
|
const ID: &'static [u8] = T::ID;
|
||||||
|
|
||||||
type Group = T;
|
type Group = T;
|
||||||
|
type Hash = OprfHash<T>;
|
||||||
type Hash = T::Hash;
|
|
||||||
}
|
}
|
||||||
|
|||||||
+148
-120
@@ -1,21 +1,18 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
//
|
// Copyright (c) VexaHub and contributors.
|
||||||
// This source code is licensed under both the MIT license found in the
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
||||||
// of this source tree.
|
|
||||||
|
|
||||||
//! Common functionality between multiple OPRF modes.
|
//! Common functionality between multiple OPRF modes.
|
||||||
|
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
|
use core::iter::Map;
|
||||||
|
use core::ops::Add;
|
||||||
|
|
||||||
use derive_where::derive_where;
|
use derive_where::derive_where;
|
||||||
use digest::core_api::BlockSizeUser;
|
|
||||||
use digest::{Digest, Output, OutputSizeUser};
|
use digest::{Digest, Output, OutputSizeUser};
|
||||||
use generic_array::sequence::Concat;
|
use hybrid_array::typenum::{IsLess, U2, U9, U256, Unsigned};
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U11, U2, U256};
|
use hybrid_array::{Array, ArrayN, ArraySize};
|
||||||
use generic_array::{ArrayLength, GenericArray};
|
use rand_core::{TryCryptoRng, TryRng};
|
||||||
use rand_core::{CryptoRng, RngCore};
|
|
||||||
use subtle::ConstantTimeEq;
|
use subtle::ConstantTimeEq;
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
@@ -28,14 +25,14 @@ use crate::{CipherSuite, Error, Group, InternalError, Result};
|
|||||||
///////////////
|
///////////////
|
||||||
|
|
||||||
pub(crate) const STR_FINALIZE: [u8; 8] = *b"Finalize";
|
pub(crate) const STR_FINALIZE: [u8; 8] = *b"Finalize";
|
||||||
pub(crate) const STR_SEED: [u8; 5] = *b"Seed-";
|
pub(crate) const STR_SEED: ArrayN<u8, 5> = Array(*b"Seed-");
|
||||||
pub(crate) const STR_DERIVE_KEYPAIR: [u8; 13] = *b"DeriveKeyPair";
|
pub(crate) const STR_DERIVE_KEYPAIR: ArrayN<u8, 13> = Array(*b"DeriveKeyPair");
|
||||||
pub(crate) const STR_COMPOSITE: [u8; 9] = *b"Composite";
|
pub(crate) const STR_COMPOSITE: [u8; 9] = *b"Composite";
|
||||||
pub(crate) const STR_CHALLENGE: [u8; 9] = *b"Challenge";
|
pub(crate) const STR_CHALLENGE: [u8; 9] = *b"Challenge";
|
||||||
pub(crate) const STR_INFO: [u8; 4] = *b"Info";
|
pub(crate) const STR_INFO: [u8; 4] = *b"Info";
|
||||||
pub(crate) const STR_VOPRF: [u8; 8] = *b"VOPRF10-";
|
pub(crate) const STR_OPRF: [u8; 7] = *b"OPRFV1-";
|
||||||
pub(crate) const STR_HASH_TO_SCALAR: [u8; 13] = *b"HashToScalar-";
|
pub(crate) const STR_HASH_TO_SCALAR: ArrayN<u8, 13> = Array(*b"HashToScalar-");
|
||||||
pub(crate) const STR_HASH_TO_GROUP: [u8; 12] = *b"HashToGroup-";
|
pub(crate) const STR_HASH_TO_GROUP: ArrayN<u8, 12> = Array(*b"HashToGroup-");
|
||||||
|
|
||||||
/// Determines the mode of operation (either base mode or verifiable mode). This
|
/// Determines the mode of operation (either base mode or verifiable mode). This
|
||||||
/// is only used for custom implementations for [`Group`].
|
/// is only used for custom implementations for [`Group`].
|
||||||
@@ -72,15 +69,12 @@ impl Mode {
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
pub struct BlindedElement<CS: CipherSuite>(
|
pub struct BlindedElement<CS: CipherSuite>(
|
||||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||||
pub(crate) <CS::Group as Group>::Elem,
|
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
|
/// The server's response to the [BlindedElement] message from a client (either
|
||||||
/// verifiable or not) to a server (either verifiable or not).
|
/// verifiable or not) to a server (either verifiable or not).
|
||||||
@@ -89,15 +83,12 @@ where
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
pub struct EvaluationElement<CS: CipherSuite>(
|
pub struct EvaluationElement<CS: CipherSuite>(
|
||||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||||
pub(crate) <CS::Group as Group>::Elem,
|
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
|
/// Contains prepared [`EvaluationElement`]s by a server batch evaluate
|
||||||
/// preparation.
|
/// preparation.
|
||||||
@@ -106,12 +97,9 @@ where
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
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
|
/// A proof produced by a server that the OPRF output matches against a server
|
||||||
/// public key.
|
/// public key.
|
||||||
@@ -120,13 +108,9 @@ where
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
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>"))]
|
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||||
pub(crate) c_scalar: <CS::Group as Group>::Scalar,
|
pub(crate) c_scalar: <CS::Group as Group>::Scalar,
|
||||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||||
@@ -140,24 +124,20 @@ where
|
|||||||
|
|
||||||
/// Can only fail with [`Error::Batch`].
|
/// Can only fail with [`Error::Batch`].
|
||||||
#[allow(clippy::many_single_char_names)]
|
#[allow(clippy::many_single_char_names)]
|
||||||
pub(crate) fn generate_proof<CS: CipherSuite, R: RngCore + CryptoRng>(
|
pub(crate) fn generate_proof<CS: CipherSuite, R: TryRng + TryCryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
k: <CS::Group as Group>::Scalar,
|
k: <CS::Group as Group>::Scalar,
|
||||||
a: <CS::Group as Group>::Elem,
|
a: <CS::Group as Group>::Elem,
|
||||||
b: <CS::Group as Group>::Elem,
|
b: <CS::Group as Group>::Elem,
|
||||||
cs: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
cs: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
|
||||||
ds: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
ds: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
) -> Result<Proof<CS>>
|
) -> Result<Proof<CS>> {
|
||||||
where
|
// https://www.rfc-editor.org/rfc/rfc9497#section-2.2.1
|
||||||
<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 (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 t2 = a * &r;
|
||||||
let t3 = m * &r;
|
let t3 = m * &r;
|
||||||
|
|
||||||
@@ -194,9 +174,9 @@ where
|
|||||||
&STR_CHALLENGE,
|
&STR_CHALLENGE,
|
||||||
];
|
];
|
||||||
|
|
||||||
let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(mode));
|
let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, mode);
|
||||||
// This can't fail, the size of the `input` is known.
|
// This can't fail, the size of the `input` is known.
|
||||||
let c_scalar = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst).unwrap();
|
let c_scalar = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
|
||||||
let s_scalar = r - &(c_scalar * &k);
|
let s_scalar = r - &(c_scalar * &k);
|
||||||
|
|
||||||
Ok(Proof { c_scalar, s_scalar })
|
Ok(Proof { c_scalar, s_scalar })
|
||||||
@@ -207,16 +187,12 @@ where
|
|||||||
pub(crate) fn verify_proof<CS: CipherSuite>(
|
pub(crate) fn verify_proof<CS: CipherSuite>(
|
||||||
a: <CS::Group as Group>::Elem,
|
a: <CS::Group as Group>::Elem,
|
||||||
b: <CS::Group as Group>::Elem,
|
b: <CS::Group as Group>::Elem,
|
||||||
cs: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
cs: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
|
||||||
ds: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
ds: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
|
||||||
proof: &Proof<CS>,
|
proof: &Proof<CS>,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
) -> Result<()>
|
) -> Result<()> {
|
||||||
where
|
// https://www.rfc-editor.org/rfc/rfc9497#section-2.2.2
|
||||||
<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 (m, z) = compute_composites::<CS, _, _>(None, b, cs, ds, mode)?;
|
||||||
let t2 = (a * &proof.s_scalar) + &(b * &proof.c_scalar);
|
let t2 = (a * &proof.s_scalar) + &(b * &proof.c_scalar);
|
||||||
let t3 = (m * &proof.s_scalar) + &(z * &proof.c_scalar);
|
let t3 = (m * &proof.s_scalar) + &(z * &proof.c_scalar);
|
||||||
@@ -254,9 +230,9 @@ where
|
|||||||
&STR_CHALLENGE,
|
&STR_CHALLENGE,
|
||||||
];
|
];
|
||||||
|
|
||||||
let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(mode));
|
let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, mode);
|
||||||
// This can't fail, the size of the `input` is known.
|
// This can't fail, the size of the `input` is known.
|
||||||
let c = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst).unwrap();
|
let c = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
|
||||||
|
|
||||||
match c.ct_eq(&proof.c_scalar).into() {
|
match c.ct_eq(&proof.c_scalar).into() {
|
||||||
true => Ok(()),
|
true => Ok(()),
|
||||||
@@ -280,12 +256,8 @@ fn compute_composites<
|
|||||||
c_slice: IC,
|
c_slice: IC,
|
||||||
d_slice: ID,
|
d_slice: ID,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
) -> Result<ComputeCompositesResult<CS>>
|
) -> Result<ComputeCompositesResult<CS>> {
|
||||||
where
|
// https://www.rfc-editor.org/rfc/rfc9497#section-2.2.1
|
||||||
<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();
|
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
|
||||||
|
|
||||||
@@ -296,7 +268,7 @@ where
|
|||||||
let len = u16::try_from(c_slice.len()).map_err(|_| Error::Batch)?;
|
let len = u16::try_from(c_slice.len()).map_err(|_| Error::Batch)?;
|
||||||
|
|
||||||
// seedDST = "Seed-" || contextString
|
// seedDST = "Seed-" || contextString
|
||||||
let seed_dst = GenericArray::from(STR_SEED).concat(create_context_string::<CS>(mode));
|
let seed_dst = Dst::new::<CS, _>(STR_SEED, mode);
|
||||||
|
|
||||||
// h1Input = I2OSP(len(Bm), 2) || Bm ||
|
// h1Input = I2OSP(len(Bm), 2) || Bm ||
|
||||||
// I2OSP(len(seedDST), 2) || seedDST
|
// I2OSP(len(seedDST), 2) || seedDST
|
||||||
@@ -304,10 +276,10 @@ where
|
|||||||
let seed = CS::Hash::new()
|
let seed = CS::Hash::new()
|
||||||
.chain_update(elem_len)
|
.chain_update(elem_len)
|
||||||
.chain_update(CS::Group::serialize_elem(b))
|
.chain_update(CS::Group::serialize_elem(b))
|
||||||
.chain_update(i2osp_2_array(&seed_dst))
|
.chain_update(seed_dst.i2osp_2())
|
||||||
.chain_update(seed_dst)
|
.chain_update_multi(&seed_dst.as_dst())
|
||||||
.finalize();
|
.finalize();
|
||||||
let seed_len = i2osp_2_array(&seed);
|
let seed_len = i2osp_2_array::<<CS::Hash as OutputSizeUser>::OutputSize>();
|
||||||
|
|
||||||
let mut m = CS::Group::identity_elem();
|
let mut m = CS::Group::identity_elem();
|
||||||
let mut z = CS::Group::identity_elem();
|
let mut z = CS::Group::identity_elem();
|
||||||
@@ -332,9 +304,9 @@ where
|
|||||||
&STR_COMPOSITE,
|
&STR_COMPOSITE,
|
||||||
];
|
];
|
||||||
|
|
||||||
let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(mode));
|
let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, mode);
|
||||||
// This can't fail, the size of the `input` is known.
|
// This can't fail, the size of the `input` is known.
|
||||||
let di = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst).unwrap();
|
let di = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
|
||||||
m = c * &di + &m;
|
m = c * &di + &m;
|
||||||
z = match k_option {
|
z = match k_option {
|
||||||
Some(_) => z,
|
Some(_) => z,
|
||||||
@@ -360,13 +332,8 @@ pub(crate) fn derive_key_internal<CS: CipherSuite>(
|
|||||||
seed: &[u8],
|
seed: &[u8],
|
||||||
info: &[u8],
|
info: &[u8],
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
) -> Result<<CS::Group as Group>::Scalar, Error>
|
) -> Result<<CS::Group as Group>::Scalar, Error> {
|
||||||
where
|
let dst = Dst::new::<CS, _>(STR_DERIVE_KEYPAIR, mode);
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
{
|
|
||||||
let context_string = create_context_string::<CS>(mode);
|
|
||||||
let dst = GenericArray::from(STR_DERIVE_KEYPAIR).concat(context_string);
|
|
||||||
|
|
||||||
let info_len = i2osp_2(info.len()).map_err(|_| Error::DeriveKeyPair)?;
|
let info_len = i2osp_2(info.len()).map_err(|_| Error::DeriveKeyPair)?;
|
||||||
|
|
||||||
@@ -376,7 +343,7 @@ where
|
|||||||
// || contextString)
|
// || contextString)
|
||||||
let sk_s = CS::Group::hash_to_scalar::<CS::Hash>(
|
let sk_s = CS::Group::hash_to_scalar::<CS::Hash>(
|
||||||
&[seed, &info_len, info, &counter.to_be_bytes()],
|
&[seed, &info_len, info, &counter.to_be_bytes()],
|
||||||
&dst,
|
&dst.as_dst(),
|
||||||
)
|
)
|
||||||
.map_err(|_| Error::DeriveKeyPair)?;
|
.map_err(|_| Error::DeriveKeyPair)?;
|
||||||
|
|
||||||
@@ -388,17 +355,18 @@ where
|
|||||||
Err(Error::Protocol)
|
Err(Error::Protocol)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Can only fail with [`Error::DeriveKeyPair`] and [`Error::Protocol`].
|
/// Corresponds to DeriveKeyPair() function from the VOPRF specification.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// - [`Error::DeriveKeyPair`] if the `input` and `seed` together are longer
|
||||||
|
/// then `u16::MAX - 3`.
|
||||||
|
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||||
#[cfg(feature = "danger")]
|
#[cfg(feature = "danger")]
|
||||||
pub fn derive_key<CS: CipherSuite>(
|
pub fn derive_key<CS: CipherSuite>(
|
||||||
seed: &[u8],
|
seed: &[u8],
|
||||||
info: &[u8],
|
info: &[u8],
|
||||||
mode: Mode,
|
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)
|
derive_key_internal::<CS>(seed, info, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,11 +380,7 @@ pub(crate) fn derive_keypair<CS: CipherSuite>(
|
|||||||
seed: &[u8],
|
seed: &[u8],
|
||||||
info: &[u8],
|
info: &[u8],
|
||||||
mode: Mode,
|
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 sk_s = derive_key_internal::<CS>(seed, info, mode)?;
|
||||||
let pk_s = CS::Group::base_elem() * &sk_s;
|
let pk_s = CS::Group::base_elem() * &sk_s;
|
||||||
|
|
||||||
@@ -432,12 +396,14 @@ pub(crate) fn deterministic_blind_unchecked<CS: CipherSuite>(
|
|||||||
input: &[u8],
|
input: &[u8],
|
||||||
blind: &<CS::Group as Group>::Scalar,
|
blind: &<CS::Group as Group>::Scalar,
|
||||||
mode: Mode,
|
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)?;
|
let hashed_point = hash_to_group::<CS>(input, mode)?;
|
||||||
|
|
||||||
|
// Identity element would nullify blinding, revealing the input.
|
||||||
|
if CS::Group::is_identity_elem(hashed_point).into() {
|
||||||
|
return Err(Error::Input);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(hashed_point * blind)
|
Ok(hashed_point * blind)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,13 +411,9 @@ where
|
|||||||
pub(crate) fn hash_to_group<CS: CipherSuite>(
|
pub(crate) fn hash_to_group<CS: CipherSuite>(
|
||||||
input: &[u8],
|
input: &[u8],
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
) -> Result<<CS::Group as Group>::Elem>
|
) -> Result<<CS::Group as Group>::Elem> {
|
||||||
where
|
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, mode);
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).map_err(|_| Error::Input)
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
{
|
|
||||||
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
|
|
||||||
CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst).map_err(|_| Error::Input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Internal function that finalizes the hash input for OPRF, VOPRF & POPRF.
|
/// Internal function that finalizes the hash input for OPRF, VOPRF & POPRF.
|
||||||
@@ -459,12 +421,8 @@ where
|
|||||||
pub(crate) fn server_evaluate_hash_input<CS: CipherSuite>(
|
pub(crate) fn server_evaluate_hash_input<CS: CipherSuite>(
|
||||||
input: &[u8],
|
input: &[u8],
|
||||||
info: Option<&[u8]>,
|
info: Option<&[u8]>,
|
||||||
issued_element: GenericArray<u8, <<CS as CipherSuite>::Group as Group>::ElemLen>,
|
issued_element: Array<u8, <<CS as CipherSuite>::Group as Group>::ElemLen>,
|
||||||
) -> Result<Output<CS::Hash>>
|
) -> Result<Output<CS::Hash>> {
|
||||||
where
|
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
{
|
|
||||||
// OPRF & VOPRF
|
// OPRF & VOPRF
|
||||||
// hashInput = I2OSP(len(input), 2) || input ||
|
// hashInput = I2OSP(len(input), 2) || input ||
|
||||||
// I2OSP(len(issuedElement), 2) || issuedElement ||
|
// I2OSP(len(issuedElement), 2) || issuedElement ||
|
||||||
@@ -486,22 +444,94 @@ where
|
|||||||
.chain_update(info.as_ref());
|
.chain_update(info.as_ref());
|
||||||
}
|
}
|
||||||
Ok(hash
|
Ok(hash
|
||||||
.chain_update(i2osp_2(issued_element.as_ref().len()).map_err(|_| Error::Input)?)
|
.chain_update(i2osp_2(issued_element.as_slice().len()).map_err(|_| Error::Input)?)
|
||||||
.chain_update(issued_element)
|
.chain_update(issued_element)
|
||||||
.chain_update(STR_FINALIZE)
|
.chain_update(STR_FINALIZE)
|
||||||
.finalize())
|
.finalize())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates the contextString parameter as defined in
|
pub(crate) type FinalizeAfterUnblindResult<'a, C, I, IE> = Map<
|
||||||
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-voprf/>
|
IE,
|
||||||
pub(crate) fn create_context_string<CS: CipherSuite>(mode: Mode) -> GenericArray<u8, U11>
|
fn((I, <<C as CipherSuite>::Group as Group>::Elem)) -> Result<Output<<C as CipherSuite>::Hash>>,
|
||||||
|
>;
|
||||||
|
|
||||||
|
/// Returned values can only fail with [`Error::Input`].
|
||||||
|
pub(crate) fn finalize_after_unblind<
|
||||||
|
'a,
|
||||||
|
CS: CipherSuite,
|
||||||
|
I: AsRef<[u8]>,
|
||||||
|
IE: 'a + Iterator<Item = (I, <CS::Group as Group>::Elem)>,
|
||||||
|
>(
|
||||||
|
inputs_and_unblinded_elements: IE,
|
||||||
|
) -> FinalizeAfterUnblindResult<'a, CS, I, IE> {
|
||||||
|
inputs_and_unblinded_elements.map(|(input, unblinded_element)| {
|
||||||
|
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
|
||||||
|
|
||||||
|
Ok(CS::Hash::new()
|
||||||
|
.chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?)
|
||||||
|
.chain_update(input.as_ref())
|
||||||
|
.chain_update(elem_len)
|
||||||
|
.chain_update(CS::Group::serialize_elem(unblinded_element))
|
||||||
|
.chain_update(STR_FINALIZE)
|
||||||
|
.finalize())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct Dst<L: ArraySize> {
|
||||||
|
dst_1: Array<u8, L>,
|
||||||
|
dst_2: &'static [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L: ArraySize> Dst<L> {
|
||||||
|
pub(crate) fn new<CS, TL>(par_1: Array<u8, TL>, mode: Mode) -> Self
|
||||||
|
where
|
||||||
|
CS: CipherSuite,
|
||||||
|
TL: ArraySize + Add<U9, Output = L>,
|
||||||
|
{
|
||||||
|
// Generates the contextString parameter as defined in
|
||||||
|
// <https://www.rfc-editor.org/rfc/rfc9497#section-3.1>
|
||||||
|
let par_2 = ArrayN::<u8, 7>::from(STR_OPRF)
|
||||||
|
.concat(ArrayN::<u8, 1>::from([mode.to_u8()]))
|
||||||
|
.concat(ArrayN::<u8, 1>::from([b'-']));
|
||||||
|
|
||||||
|
let dst_1 = par_1.concat(par_2);
|
||||||
|
let dst_2 = CS::ID;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
L::USIZE + dst_2.len() <= u16::MAX.into(),
|
||||||
|
"constructed DST longer then {}",
|
||||||
|
u16::MAX
|
||||||
|
);
|
||||||
|
|
||||||
|
Self { dst_1, dst_2 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_dst(&self) -> [&[u8]; 2] {
|
||||||
|
[&self.dst_1, self.dst_2]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn i2osp_2(&self) -> [u8; 2] {
|
||||||
|
u16::try_from(L::USIZE + self.dst_2.len())
|
||||||
|
.unwrap()
|
||||||
|
.to_be_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait DigestExt {
|
||||||
|
fn chain_update_multi(self, data: &[&[u8]]) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DigestExt for T
|
||||||
where
|
where
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
T: Digest,
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
{
|
{
|
||||||
GenericArray::from(STR_VOPRF)
|
fn chain_update_multi(mut self, datas: &[&[u8]]) -> Self {
|
||||||
.concat([mode.to_u8()].into())
|
for data in datas {
|
||||||
.concat(CS::ID.to_be_bytes().into())
|
self.update(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////
|
///////////////////////
|
||||||
@@ -515,8 +545,6 @@ pub(crate) fn i2osp_2(input: usize) -> Result<[u8; 2], InternalError> {
|
|||||||
.map_err(|_| InternalError::I2osp)
|
.map_err(|_| InternalError::I2osp)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn i2osp_2_array<L: ArrayLength<u8> + IsLess<U256>>(
|
pub(crate) fn i2osp_2_array<L: ArraySize + IsLess<U256>>() -> Array<u8, U2> {
|
||||||
_: &GenericArray<u8, L>,
|
|
||||||
) -> GenericArray<u8, U2> {
|
|
||||||
L::U16.to_be_bytes().into()
|
L::U16.to_be_bytes().into()
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-12
@@ -1,19 +1,14 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
//
|
// Copyright (c) VexaHub and contributors.
|
||||||
// This source code is licensed under both the MIT license found in the
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
||||||
// of this source tree.
|
|
||||||
|
|
||||||
//! Errors which are produced during an execution of the protocol
|
//! Errors which are produced during an execution of the protocol
|
||||||
|
|
||||||
use displaydoc::Display;
|
|
||||||
|
|
||||||
/// [`Result`](core::result::Result) shorthand that uses [`Error`].
|
/// [`Result`](core::result::Result) shorthand that uses [`Error`].
|
||||||
pub type Result<T, E = Error> = core::result::Result<T, E>;
|
pub type Result<T, E = Error> = core::result::Result<T, E>;
|
||||||
|
|
||||||
/// Represents an error in the manipulation of internal cryptographic data
|
/// Represents an error in the manipulation of internal cryptographic data
|
||||||
#[derive(Clone, Copy, Debug, Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Copy, Debug, displaydoc::Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Size of info is longer then [`u16::MAX`].
|
/// Size of info is longer then [`u16::MAX`].
|
||||||
Info,
|
Info,
|
||||||
@@ -23,12 +18,14 @@ pub enum Error {
|
|||||||
DeriveKeyPair,
|
DeriveKeyPair,
|
||||||
/// Failure to deserialize bytes
|
/// Failure to deserialize bytes
|
||||||
Deserialization,
|
Deserialization,
|
||||||
/// Batched items are more then [`u16::MAX`] or length don't match.
|
/// Batched items are more than [`u16::MAX`] or length don't match.
|
||||||
Batch,
|
Batch,
|
||||||
/// In verifiable mode, occurs when the proof failed to verify
|
/// In verifiable mode, occurs when the proof failed to verify
|
||||||
ProofVerification,
|
ProofVerification,
|
||||||
/// The protocol has failed and can't be completed.
|
/// The protocol has failed and can't be completed.
|
||||||
Protocol,
|
Protocol,
|
||||||
|
/// Random number generator failure.
|
||||||
|
Rng,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Only used to implement [`Group`](crate::Group).
|
/// Only used to implement [`Group`](crate::Group).
|
||||||
@@ -40,5 +37,4 @@ pub enum InternalError {
|
|||||||
I2osp,
|
I2osp,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
impl core::error::Error for Error {}
|
||||||
impl std::error::Error for Error {}
|
|
||||||
|
|||||||
+66
-40
@@ -1,58 +1,73 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
//
|
// Copyright (c) VexaHub and contributors.
|
||||||
// This source code is licensed under both the MIT license found in the
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
||||||
// of this source tree.
|
|
||||||
|
|
||||||
use digest::core_api::BlockSizeUser;
|
use core::ops::{Add, Mul};
|
||||||
use digest::Digest;
|
use digest::block_api::BlockSizeUser;
|
||||||
|
use digest::typenum::{IsLess, IsLessOrEqual, U256};
|
||||||
|
use digest::{FixedOutput, HashMarker};
|
||||||
use elliptic_curve::group::cofactor::CofactorGroup;
|
use elliptic_curve::group::cofactor::CofactorGroup;
|
||||||
use elliptic_curve::hash2curve::{ExpandMsgXmd, FromOkm, GroupDigest};
|
use elliptic_curve::sec1::{FromSec1Point, ModulusSize, ToSec1Point};
|
||||||
use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint};
|
|
||||||
use elliptic_curve::{
|
use elliptic_curve::{
|
||||||
AffinePoint, Field, FieldSize, Group as _, ProjectivePoint, PublicKey, Scalar, SecretKey,
|
AffinePoint, Field, FieldBytes, FieldBytesSize, Group as _, ProjectivePoint, PublicKey, Scalar,
|
||||||
|
SecretKey,
|
||||||
};
|
};
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
|
use hash2curve::{ExpandMsgXmd, GroupDigest, MapToCurve, hash_to_scalar};
|
||||||
use generic_array::GenericArray;
|
use hybrid_array::typenum::{IsGreaterOrEqual, Prod, Sum, True, U2};
|
||||||
use rand_core::{CryptoRng, RngCore};
|
use hybrid_array::{Array, ArraySize};
|
||||||
|
use rand_core::TryCryptoRng;
|
||||||
|
|
||||||
use super::Group;
|
use super::Group;
|
||||||
use crate::{Error, InternalError, Result};
|
use crate::{Error, InternalError, Result};
|
||||||
|
|
||||||
|
type ElemLen<C> = <ScalarLen<C> as ModulusSize>::CompressedPointSize;
|
||||||
|
type ScalarLen<C> = FieldBytesSize<C>;
|
||||||
|
|
||||||
impl<C> Group for C
|
impl<C> Group for C
|
||||||
where
|
where
|
||||||
C: GroupDigest,
|
C: GroupDigest,
|
||||||
ProjectivePoint<Self>: CofactorGroup + ToEncodedPoint<Self>,
|
C::SecurityLevel: Mul<U2>,
|
||||||
FieldSize<Self>: ModulusSize,
|
C::SecurityLevel: ArraySize,
|
||||||
AffinePoint<Self>: FromEncodedPoint<Self> + ToEncodedPoint<Self>,
|
<C::SecurityLevel as Mul<U2>>::Output: ArraySize,
|
||||||
Scalar<Self>: FromOkm,
|
ProjectivePoint<Self>: CofactorGroup + ToSec1Point<Self>,
|
||||||
|
ScalarLen<Self>: ModulusSize,
|
||||||
|
ScalarLen<Self>: ArraySize,
|
||||||
|
ScalarLen<Self>: hybrid_array::typenum::NonZero,
|
||||||
|
Scalar<Self>: elliptic_curve::ops::Reduce<Array<u8, <C as MapToCurve>::Length>>,
|
||||||
|
AffinePoint<Self>: FromSec1Point<Self> + ToSec1Point<Self>,
|
||||||
|
// `VoprfClientLen`, `PoprfClientLen`, `VoprfServerLen`, `PoprfServerLen`
|
||||||
|
ScalarLen<Self>: Add<ElemLen<Self>>,
|
||||||
|
Sum<ScalarLen<Self>, ElemLen<Self>>: ArraySize,
|
||||||
|
// `ProofLen`
|
||||||
|
ScalarLen<Self>: Add<ScalarLen<Self>>,
|
||||||
|
Sum<ScalarLen<Self>, ScalarLen<Self>>: ArraySize,
|
||||||
|
ElemLen<Self>: ArraySize,
|
||||||
{
|
{
|
||||||
type Elem = ProjectivePoint<Self>;
|
type Elem = ProjectivePoint<Self>;
|
||||||
|
|
||||||
type ElemLen = <FieldSize<Self> as ModulusSize>::CompressedPointSize;
|
type ElemLen = ElemLen<Self>;
|
||||||
|
|
||||||
type Scalar = Scalar<Self>;
|
type Scalar = Scalar<Self>;
|
||||||
|
|
||||||
type ScalarLen = FieldSize<Self>;
|
type ScalarLen = ScalarLen<Self>;
|
||||||
|
|
||||||
|
type SecurityLevel = C::SecurityLevel;
|
||||||
|
|
||||||
// Implements the `hash_to_curve()` function from
|
// Implements the `hash_to_curve()` function from
|
||||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
// https://www.rfc-editor.org/rfc/rfc9380.html#section-3
|
||||||
fn hash_to_curve<H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Elem, InternalError>
|
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError> {
|
||||||
where
|
Self::hash_from_bytes(input, dst).map_err(|_| InternalError::Input)
|
||||||
H: Digest + BlockSizeUser,
|
|
||||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
|
||||||
{
|
|
||||||
Self::hash_from_bytes::<ExpandMsgXmd<H>>(input, dst).map_err(|_| InternalError::Input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements the `HashToScalar()` function
|
// Implements the `HashToScalar()` function
|
||||||
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Scalar, InternalError>
|
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
|
||||||
where
|
where
|
||||||
H: Digest + BlockSizeUser,
|
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
H::OutputSize: IsLess<U256>
|
||||||
|
+ IsLessOrEqual<H::BlockSize, Output = True>
|
||||||
|
+ IsGreaterOrEqual<Prod<C::SecurityLevel, U2>, Output = True>,
|
||||||
{
|
{
|
||||||
<Self as GroupDigest>::hash_to_scalar::<ExpandMsgXmd<H>>(input, dst)
|
hash_to_scalar::<C, ExpandMsgXmd<H>, <C as MapToCurve>::Length>(input, dst)
|
||||||
.map_err(|_| InternalError::Input)
|
.map_err(|_| InternalError::Input)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,10 +79,10 @@ where
|
|||||||
ProjectivePoint::<Self>::identity()
|
ProjectivePoint::<Self>::identity()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> {
|
fn serialize_elem(elem: Self::Elem) -> Array<u8, Self::ElemLen> {
|
||||||
let bytes = elem.to_encoded_point(true);
|
let bytes = elem.to_sec1_point(true);
|
||||||
let bytes = bytes.as_bytes();
|
let bytes = bytes.as_bytes();
|
||||||
let mut result = GenericArray::default();
|
let mut result = Array::default();
|
||||||
result[..bytes.len()].copy_from_slice(bytes);
|
result[..bytes.len()].copy_from_slice(bytes);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
@@ -78,8 +93,16 @@ where
|
|||||||
.map_err(|_| Error::Deserialization)
|
.map_err(|_| Error::Deserialization)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
|
fn random_scalar<R: TryCryptoRng>(rng: &mut R) -> Result<Self::Scalar> {
|
||||||
*SecretKey::<Self>::random(rng).to_nonzero_scalar()
|
loop {
|
||||||
|
let mut bytes = FieldBytes::<Self>::default();
|
||||||
|
|
||||||
|
rng.try_fill_bytes(&mut bytes).map_err(|_| Error::Rng)?;
|
||||||
|
|
||||||
|
if let Ok(key) = SecretKey::<Self>::from_slice(&bytes) {
|
||||||
|
return Ok(*key.to_nonzero_scalar());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar {
|
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar {
|
||||||
@@ -92,15 +115,18 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn zero_scalar() -> Self::Scalar {
|
fn zero_scalar() -> Self::Scalar {
|
||||||
Scalar::<Self>::zero()
|
Scalar::<Self>::ZERO
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
fn serialize_scalar(scalar: Self::Scalar) -> Array<u8, Self::ScalarLen> {
|
||||||
scalar.into()
|
let bytes: FieldBytes<Self> = scalar.into();
|
||||||
|
let mut result = Array::<u8, Self::ScalarLen>::default();
|
||||||
|
result.as_mut_slice().copy_from_slice(bytes.as_ref());
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_scalar(scalar_bits: &[u8]) -> Result<Self::Scalar> {
|
fn deserialize_scalar(scalar_bits: &[u8]) -> Result<Self::Scalar> {
|
||||||
SecretKey::<Self>::from_be_bytes(scalar_bits)
|
SecretKey::<Self>::from_slice(scalar_bits)
|
||||||
.map(|secret_key| *secret_key.to_nonzero_scalar())
|
.map(|secret_key| *secret_key.to_nonzero_scalar())
|
||||||
.map_err(|_| Error::Deserialization)
|
.map_err(|_| Error::Deserialization)
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-25
@@ -1,9 +1,6 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
//
|
// Copyright (c) VexaHub and contributors.
|
||||||
// This source code is licensed under both the MIT license found in the
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
||||||
// of this source tree.
|
|
||||||
|
|
||||||
//! Defines the Group trait to specify the underlying prime order group
|
//! Defines the Group trait to specify the underlying prime order group
|
||||||
|
|
||||||
@@ -13,11 +10,11 @@ mod ristretto;
|
|||||||
|
|
||||||
use core::ops::{Add, Mul, Sub};
|
use core::ops::{Add, Mul, Sub};
|
||||||
|
|
||||||
use digest::core_api::BlockSizeUser;
|
use digest::block_api::BlockSizeUser;
|
||||||
use digest::Digest;
|
use digest::{FixedOutput, HashMarker};
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
|
use hybrid_array::typenum::{IsGreaterOrEqual, IsLess, IsLessOrEqual, Prod, Sum, True, U2, U256};
|
||||||
use generic_array::{ArrayLength, GenericArray};
|
use hybrid_array::{Array, ArraySize};
|
||||||
use rand_core::{CryptoRng, RngCore};
|
use rand_core::{TryCryptoRng, TryRng};
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
pub use ristretto::Ristretto255;
|
pub use ristretto::Ristretto255;
|
||||||
use subtle::{Choice, ConstantTimeEq};
|
use subtle::{Choice, ConstantTimeEq};
|
||||||
@@ -26,8 +23,16 @@ use zeroize::Zeroize;
|
|||||||
use crate::{InternalError, Result};
|
use crate::{InternalError, Result};
|
||||||
|
|
||||||
/// A prime-order subgroup of a base field (EC, prime-order field ...). This
|
/// A prime-order subgroup of a base field (EC, prime-order field ...). This
|
||||||
/// subgroup is noted additively — as in the draft RFC — in this trait.
|
/// subgroup is noted additively — as in the RFC — in this trait.
|
||||||
pub trait Group {
|
pub trait Group
|
||||||
|
where
|
||||||
|
// `VoprfClientLen`, `PoprfClientLen`, `VoprfServerLen`, `PoprfServerLen`
|
||||||
|
Self::ScalarLen: Add<Self::ElemLen>,
|
||||||
|
Sum<Self::ScalarLen, Self::ElemLen>: ArraySize,
|
||||||
|
// `ProofLen`
|
||||||
|
Self::ScalarLen: Add<Self::ScalarLen>,
|
||||||
|
Sum<Self::ScalarLen, Self::ScalarLen>: ArraySize,
|
||||||
|
{
|
||||||
/// The type of group elements
|
/// The type of group elements
|
||||||
type Elem: ConstantTimeEq
|
type Elem: ConstantTimeEq
|
||||||
+ Copy
|
+ Copy
|
||||||
@@ -36,7 +41,7 @@ pub trait Group {
|
|||||||
+ for<'a> Mul<&'a Self::Scalar, Output = Self::Elem>;
|
+ for<'a> Mul<&'a Self::Scalar, Output = Self::Elem>;
|
||||||
|
|
||||||
/// The byte length necessary to represent group elements
|
/// The byte length necessary to represent group elements
|
||||||
type ElemLen: ArrayLength<u8> + 'static;
|
type ElemLen: ArraySize + 'static;
|
||||||
|
|
||||||
/// The type of base field scalars
|
/// The type of base field scalars
|
||||||
type Scalar: ConstantTimeEq
|
type Scalar: ConstantTimeEq
|
||||||
@@ -47,27 +52,39 @@ pub trait Group {
|
|||||||
+ for<'a> Sub<&'a Self::Scalar, Output = Self::Scalar>;
|
+ for<'a> Sub<&'a Self::Scalar, Output = Self::Scalar>;
|
||||||
|
|
||||||
/// The byte length necessary to represent scalars
|
/// The byte length necessary to represent scalars
|
||||||
type ScalarLen: ArrayLength<u8> + 'static;
|
type ScalarLen: ArraySize + 'static;
|
||||||
|
|
||||||
|
/// Security parameter `k` in bytes (i.e. `k / 8`), as defined in
|
||||||
|
/// [RFC 9380 §8](https://www.rfc-editor.org/rfc/rfc9380#section-8).
|
||||||
|
///
|
||||||
|
/// Used to enforce `H::OutputSize >= 2 * SecurityLevel` in
|
||||||
|
/// `hash_to_curve` and `hash_to_scalar`, which corresponds to the
|
||||||
|
/// `expand_message` requirement `len_in_bytes = 2 * k / 8`.
|
||||||
|
type SecurityLevel: ArraySize + Mul<U2>;
|
||||||
|
|
||||||
/// Transforms a password and domain separation tag (DST) into a curve point
|
/// Transforms a password and domain separation tag (DST) into a curve point
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer
|
/// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer
|
||||||
/// then [`u16::MAX`].
|
/// then [`u16::MAX`].
|
||||||
fn hash_to_curve<H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Elem, InternalError>
|
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError>
|
||||||
where
|
where
|
||||||
H: Digest + BlockSizeUser,
|
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>;
|
H::OutputSize: IsLess<U256>
|
||||||
|
+ IsLessOrEqual<H::BlockSize, Output = True>
|
||||||
|
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>;
|
||||||
|
|
||||||
/// Hashes a slice of pseudo-random bytes to a scalar
|
/// Hashes a slice of pseudo-random bytes to a scalar
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer
|
/// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer
|
||||||
/// then [`u16::MAX`].
|
/// then [`u16::MAX`].
|
||||||
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Scalar, InternalError>
|
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
|
||||||
where
|
where
|
||||||
H: Digest + BlockSizeUser,
|
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>;
|
H::OutputSize: IsLess<U256>
|
||||||
|
+ IsLessOrEqual<H::BlockSize, Output = True>
|
||||||
|
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>;
|
||||||
|
|
||||||
/// Get the base point for the group
|
/// Get the base point for the group
|
||||||
fn base_elem() -> Self::Elem;
|
fn base_elem() -> Self::Elem;
|
||||||
@@ -81,7 +98,7 @@ pub trait Group {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Serializes the `self` group element
|
/// Serializes the `self` group element
|
||||||
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen>;
|
fn serialize_elem(elem: Self::Elem) -> Array<u8, Self::ElemLen>;
|
||||||
|
|
||||||
/// Return an element from its fixed-length bytes representation. If the
|
/// Return an element from its fixed-length bytes representation. If the
|
||||||
/// element is the identity element, return an error.
|
/// element is the identity element, return an error.
|
||||||
@@ -91,8 +108,11 @@ pub trait Group {
|
|||||||
/// is not a valid point on the group or the identity element.
|
/// is not a valid point on the group or the identity element.
|
||||||
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem>;
|
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem>;
|
||||||
|
|
||||||
/// picks a scalar at random
|
/// Picks a scalar at random.
|
||||||
fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar;
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// [`Error::Rng`](crate::Error::Rng) if the random number generator fails.
|
||||||
|
fn random_scalar<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self::Scalar>;
|
||||||
|
|
||||||
/// The multiplicative inverse of this scalar
|
/// The multiplicative inverse of this scalar
|
||||||
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar;
|
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar;
|
||||||
@@ -105,7 +125,7 @@ pub trait Group {
|
|||||||
fn zero_scalar() -> Self::Scalar;
|
fn zero_scalar() -> Self::Scalar;
|
||||||
|
|
||||||
/// Serializes a scalar to bytes
|
/// Serializes a scalar to bytes
|
||||||
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen>;
|
fn serialize_scalar(scalar: Self::Scalar) -> Array<u8, Self::ScalarLen>;
|
||||||
|
|
||||||
/// Return a scalar from its fixed-length bytes representation. If the
|
/// Return a scalar from its fixed-length bytes representation. If the
|
||||||
/// scalar is zero or invalid, then return an error.
|
/// scalar is zero or invalid, then return an error.
|
||||||
|
|||||||
+63
-44
@@ -1,20 +1,21 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
//
|
// Copyright (c) VexaHub and contributors.
|
||||||
// This source code is licensed under both the MIT license found in the
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
use core::num::NonZeroU16;
|
||||||
// of this source tree.
|
|
||||||
|
|
||||||
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
|
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
|
||||||
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
|
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
|
||||||
use curve25519_dalek::scalar::Scalar;
|
use curve25519_dalek::scalar::Scalar;
|
||||||
use curve25519_dalek::traits::Identity;
|
use curve25519_dalek::traits::Identity;
|
||||||
use digest::core_api::BlockSizeUser;
|
use digest::block_api::BlockSizeUser;
|
||||||
use digest::Digest;
|
use digest::{FixedOutput, HashMarker};
|
||||||
use elliptic_curve::hash2curve::{ExpandMsg, ExpandMsgXmd, Expander};
|
use hash2curve::{ExpandMsg, ExpandMsgXmd, Expander};
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256, U32, U64};
|
use hybrid_array::Array;
|
||||||
use generic_array::GenericArray;
|
use hybrid_array::typenum::{
|
||||||
use rand_core::{CryptoRng, RngCore};
|
IsGreaterOrEqual, IsLess, IsLessOrEqual, Prod, True, U2, U16, U32, U256,
|
||||||
|
};
|
||||||
|
use rand_core::{TryCryptoRng, TryRng};
|
||||||
use subtle::ConstantTimeEq;
|
use subtle::ConstantTimeEq;
|
||||||
|
|
||||||
use super::Group;
|
use super::Group;
|
||||||
@@ -26,7 +27,7 @@ pub struct Ristretto255;
|
|||||||
|
|
||||||
#[cfg(feature = "ristretto255-ciphersuite")]
|
#[cfg(feature = "ristretto255-ciphersuite")]
|
||||||
impl crate::CipherSuite for Ristretto255 {
|
impl crate::CipherSuite for Ristretto255 {
|
||||||
const ID: u16 = 0x0001;
|
const ID: &'static [u8] = b"ristretto255-SHA512";
|
||||||
|
|
||||||
type Group = Ristretto255;
|
type Group = Ristretto255;
|
||||||
|
|
||||||
@@ -42,34 +43,34 @@ impl Group for Ristretto255 {
|
|||||||
|
|
||||||
type ScalarLen = U32;
|
type ScalarLen = U32;
|
||||||
|
|
||||||
// Implements the `hash_to_ristretto255()` function from
|
type SecurityLevel = U16;
|
||||||
// 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: Digest + BlockSizeUser,
|
|
||||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
|
||||||
{
|
|
||||||
let mut uniform_bytes = GenericArray::<_, U64>::default();
|
|
||||||
ExpandMsgXmd::<H>::expand_message(input, dst, 64)
|
|
||||||
.map_err(|_| InternalError::Input)?
|
|
||||||
.fill_bytes(&mut uniform_bytes);
|
|
||||||
|
|
||||||
Ok(RistrettoPoint::from_uniform_bytes(&uniform_bytes.into()))
|
// Implements the `hash_to_ristretto255()` function from
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc9380.html#appendix-B
|
||||||
|
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError>
|
||||||
|
where
|
||||||
|
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||||
|
H::OutputSize: IsLess<U256>
|
||||||
|
+ IsLessOrEqual<H::BlockSize, Output = True>
|
||||||
|
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>,
|
||||||
|
{
|
||||||
|
let uniform_bytes = expand_uniform_bytes::<H>(input, dst)?;
|
||||||
|
|
||||||
|
Ok(RistrettoPoint::from_uniform_bytes(&uniform_bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements the `HashToScalar()` function from
|
// Implements the `HashToScalar()` function from
|
||||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.1
|
// https://www.rfc-editor.org/rfc/rfc9497#section-4.1
|
||||||
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Scalar, InternalError>
|
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
|
||||||
where
|
where
|
||||||
H: Digest + BlockSizeUser,
|
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
H::OutputSize: IsLess<U256>
|
||||||
|
+ IsLessOrEqual<H::BlockSize, Output = True>
|
||||||
|
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>,
|
||||||
{
|
{
|
||||||
let mut uniform_bytes = GenericArray::<_, U64>::default();
|
let uniform_bytes = expand_uniform_bytes::<H>(input, dst)?;
|
||||||
ExpandMsgXmd::<H>::expand_message(input, dst, 64)
|
|
||||||
.map_err(|_| InternalError::Input)?
|
|
||||||
.fill_bytes(&mut uniform_bytes);
|
|
||||||
|
|
||||||
Ok(Scalar::from_bytes_mod_order_wide(&uniform_bytes.into()))
|
Ok(Scalar::from_bytes_mod_order_wide(&uniform_bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn base_elem() -> Self::Elem {
|
fn base_elem() -> Self::Elem {
|
||||||
@@ -81,27 +82,26 @@ impl Group for Ristretto255 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// serialization of a group element
|
// serialization of a group element
|
||||||
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> {
|
fn serialize_elem(elem: Self::Elem) -> Array<u8, Self::ElemLen> {
|
||||||
elem.compress().to_bytes().into()
|
elem.compress().to_bytes().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem> {
|
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem> {
|
||||||
if element_bits.len() != 32 {
|
|
||||||
return Err(Error::Deserialization);
|
|
||||||
}
|
|
||||||
|
|
||||||
CompressedRistretto::from_slice(element_bits)
|
CompressedRistretto::from_slice(element_bits)
|
||||||
|
.map_err(|_| Error::Deserialization)?
|
||||||
.decompress()
|
.decompress()
|
||||||
.filter(|point| point != &RistrettoPoint::identity())
|
.filter(|point| point != &RistrettoPoint::identity())
|
||||||
.ok_or(Error::Deserialization)
|
.ok_or(Error::Deserialization)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
|
fn random_scalar<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self::Scalar> {
|
||||||
loop {
|
loop {
|
||||||
let scalar = Scalar::random(rng);
|
let mut scalar_bytes = [0u8; 32];
|
||||||
|
rng.try_fill_bytes(&mut scalar_bytes)
|
||||||
|
.map_err(|_| Error::Rng)?;
|
||||||
|
|
||||||
if scalar != Scalar::ZERO {
|
if let Ok(scalar) = Self::deserialize_scalar(&scalar_bytes) {
|
||||||
break scalar;
|
break Ok(scalar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ impl Group for Ristretto255 {
|
|||||||
Scalar::ZERO
|
Scalar::ZERO
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
fn serialize_scalar(scalar: Self::Scalar) -> Array<u8, Self::ScalarLen> {
|
||||||
scalar.to_bytes().into()
|
scalar.to_bytes().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,3 +132,22 @@ impl Group for Ristretto255 {
|
|||||||
.ok_or(Error::Deserialization)
|
.ok_or(Error::Deserialization)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HELPERS
|
||||||
|
|
||||||
|
fn expand_uniform_bytes<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<[u8; 64], InternalError>
|
||||||
|
where
|
||||||
|
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||||
|
H::OutputSize: IsLess<U256>
|
||||||
|
+ IsLessOrEqual<H::BlockSize, Output = True>
|
||||||
|
+ IsGreaterOrEqual<Prod<U16, U2>, Output = True>,
|
||||||
|
{
|
||||||
|
let mut uniform_bytes = [0u8; 64];
|
||||||
|
|
||||||
|
<ExpandMsgXmd<H> as ExpandMsg<U16>>::expand_message(input, dst, NonZeroU16::new(64).unwrap())
|
||||||
|
.map_err(|_| InternalError::Input)?
|
||||||
|
.fill_bytes(&mut uniform_bytes)
|
||||||
|
.map_err(|_| InternalError::Input)?;
|
||||||
|
|
||||||
|
Ok(uniform_bytes)
|
||||||
|
}
|
||||||
|
|||||||
+11
-6
@@ -1,9 +1,6 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
//
|
// Copyright (c) VexaHub and contributors.
|
||||||
// This source code is licensed under both the MIT license found in the
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
||||||
// of this source tree.
|
|
||||||
|
|
||||||
//! Includes a series of tests for the group implementations
|
//! Includes a series of tests for the group implementations
|
||||||
|
|
||||||
@@ -15,6 +12,8 @@ use crate::{Error, Group, Result};
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_group_properties() -> Result<()> {
|
fn test_group_properties() -> Result<()> {
|
||||||
use p256::NistP256;
|
use p256::NistP256;
|
||||||
|
use p384::NistP384;
|
||||||
|
use p521::NistP521;
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
{
|
{
|
||||||
@@ -27,6 +26,12 @@ fn test_group_properties() -> Result<()> {
|
|||||||
test_identity_element_error::<NistP256>()?;
|
test_identity_element_error::<NistP256>()?;
|
||||||
test_zero_scalar_error::<NistP256>()?;
|
test_zero_scalar_error::<NistP256>()?;
|
||||||
|
|
||||||
|
test_identity_element_error::<NistP384>()?;
|
||||||
|
test_zero_scalar_error::<NistP384>()?;
|
||||||
|
|
||||||
|
test_identity_element_error::<NistP521>()?;
|
||||||
|
test_zero_scalar_error::<NistP521>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+85
-93
@@ -1,16 +1,11 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
//
|
// Copyright (c) VexaHub and contributors.
|
||||||
// This source code is licensed under both the MIT license found in the
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
||||||
// of this source tree.
|
|
||||||
|
|
||||||
//! An implementation of a verifiable oblivious pseudorandom function (VOPRF)
|
//! An implementation of a verifiable oblivious pseudorandom function (VOPRF)
|
||||||
//!
|
//!
|
||||||
//! Note: This implementation is in sync with
|
//! Note: This implementation is in sync with
|
||||||
//! [draft-irtf-cfrg-voprf-11](https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html),
|
//! [RFC 9497](https://www.rfc-editor.org/rfc/rfc9497).
|
||||||
//! but this specification is subject to change, until the final version
|
|
||||||
//! published by the IETF.
|
|
||||||
//!
|
//!
|
||||||
//! # Overview
|
//! # Overview
|
||||||
//!
|
//!
|
||||||
@@ -21,7 +16,7 @@
|
|||||||
//! We will use the following choice in this example:
|
//! We will use the following choice in this example:
|
||||||
//!
|
//!
|
||||||
//! ```ignore
|
//! ```ignore
|
||||||
//! type CipherSuite = voprf::Ristretto255;
|
//! type CipherSuite = voprf_vx::Ristretto255;
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ## Modes of Operation
|
//! ## Modes of Operation
|
||||||
@@ -52,14 +47,14 @@
|
|||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # type CipherSuite = voprf::Ristretto255;
|
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # type CipherSuite = p256::NistP256;
|
//! # type CipherSuite = p256::NistP256;
|
||||||
//! use rand::rngs::OsRng;
|
//! use rand::rngs::SysRng;
|
||||||
//! use rand::RngCore;
|
//! use rand::Rng;
|
||||||
//! use voprf::OprfServer;
|
//! use voprf_vx::OprfServer;
|
||||||
//!
|
//!
|
||||||
//! let mut server_rng = OsRng;
|
//! let mut server_rng = SysRng;
|
||||||
//! let server = OprfServer::<CipherSuite>::new(&mut server_rng);
|
//! let server = OprfServer::<CipherSuite>::new(&mut server_rng);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
@@ -72,14 +67,14 @@
|
|||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # type CipherSuite = voprf::Ristretto255;
|
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # type CipherSuite = p256::NistP256;
|
//! # type CipherSuite = p256::NistP256;
|
||||||
//! use rand::rngs::OsRng;
|
//! use rand::rngs::SysRng;
|
||||||
//! use rand::RngCore;
|
//! use rand::Rng;
|
||||||
//! use voprf::OprfClient;
|
//! use voprf_vx::OprfClient;
|
||||||
//!
|
//!
|
||||||
//! let mut client_rng = OsRng;
|
//! let mut client_rng = SysRng;
|
||||||
//! let client_blind_result = OprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
|
//! let client_blind_result = OprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
|
||||||
//! .expect("Unable to construct client");
|
//! .expect("Unable to construct client");
|
||||||
//! ```
|
//! ```
|
||||||
@@ -93,19 +88,19 @@
|
|||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # type CipherSuite = voprf::Ristretto255;
|
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # type CipherSuite = p256::NistP256;
|
//! # type CipherSuite = p256::NistP256;
|
||||||
//! # use voprf::OprfClient;
|
//! # use voprf_vx::OprfClient;
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::SysRng, Rng};
|
||||||
//! #
|
//! #
|
||||||
//! # let mut client_rng = OsRng;
|
//! # let mut client_rng = SysRng;
|
||||||
//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
|
//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
|
||||||
//! # b"input",
|
//! # b"input",
|
||||||
//! # &mut client_rng,
|
//! # &mut client_rng,
|
||||||
//! # ).expect("Unable to construct client");
|
//! # ).expect("Unable to construct client");
|
||||||
//! # use voprf::OprfServer;
|
//! # use voprf_vx::OprfServer;
|
||||||
//! # let mut server_rng = OsRng;
|
//! # let mut server_rng = SysRng;
|
||||||
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||||
//! let server_evaluate_result = server.blind_evaluate(&client_blind_result.message);
|
//! let server_evaluate_result = server.blind_evaluate(&client_blind_result.message);
|
||||||
//! ```
|
//! ```
|
||||||
@@ -118,19 +113,19 @@
|
|||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # type CipherSuite = voprf::Ristretto255;
|
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # type CipherSuite = p256::NistP256;
|
//! # type CipherSuite = p256::NistP256;
|
||||||
//! # use voprf::OprfClient;
|
//! # use voprf_vx::OprfClient;
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::SysRng, Rng};
|
||||||
//! #
|
//! #
|
||||||
//! # let mut client_rng = OsRng;
|
//! # let mut client_rng = SysRng;
|
||||||
//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
|
//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
|
||||||
//! # b"input",
|
//! # b"input",
|
||||||
//! # &mut client_rng,
|
//! # &mut client_rng,
|
||||||
//! # ).expect("Unable to construct client");
|
//! # ).expect("Unable to construct client");
|
||||||
//! # use voprf::OprfServer;
|
//! # use voprf_vx::OprfServer;
|
||||||
//! # let mut server_rng = OsRng;
|
//! # let mut server_rng = SysRng;
|
||||||
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||||
//! # let message = server.blind_evaluate(&client_blind_result.message);
|
//! # let message = server.blind_evaluate(&client_blind_result.message);
|
||||||
//! let client_finalize_result = client_blind_result
|
//! let client_finalize_result = client_blind_result
|
||||||
@@ -151,19 +146,19 @@
|
|||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # type CipherSuite = voprf::Ristretto255;
|
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # type CipherSuite = p256::NistP256;
|
//! # type CipherSuite = p256::NistP256;
|
||||||
//! # use voprf::OprfClient;
|
//! # use voprf_vx::OprfClient;
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::SysRng, Rng};
|
||||||
//! #
|
//! #
|
||||||
//! # let mut client_rng = OsRng;
|
//! # let mut client_rng = SysRng;
|
||||||
//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
|
//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
|
||||||
//! # b"input",
|
//! # b"input",
|
||||||
//! # &mut client_rng,
|
//! # &mut client_rng,
|
||||||
//! # ).expect("Unable to construct client");
|
//! # ).expect("Unable to construct client");
|
||||||
//! # use voprf::OprfServer;
|
//! # use voprf_vx::OprfServer;
|
||||||
//! # let mut server_rng = OsRng;
|
//! # let mut server_rng = SysRng;
|
||||||
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||||
//! # let message = server.blind_evaluate(&client_blind_result.message);
|
//! # let message = server.blind_evaluate(&client_blind_result.message);
|
||||||
//! let client_finalize_result = client_blind_result
|
//! let client_finalize_result = client_blind_result
|
||||||
@@ -198,14 +193,14 @@
|
|||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # type CipherSuite = voprf::Ristretto255;
|
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # type CipherSuite = p256::NistP256;
|
//! # type CipherSuite = p256::NistP256;
|
||||||
//! use rand::rngs::OsRng;
|
//! use rand::rngs::SysRng;
|
||||||
//! use rand::RngCore;
|
//! use rand::Rng;
|
||||||
//! use voprf::VoprfServer;
|
//! use voprf_vx::VoprfServer;
|
||||||
//!
|
//!
|
||||||
//! let mut server_rng = OsRng;
|
//! let mut server_rng = SysRng;
|
||||||
//! let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
//! let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||||
//!
|
//!
|
||||||
//! // To be sent to the client
|
//! // To be sent to the client
|
||||||
@@ -225,14 +220,14 @@
|
|||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # type CipherSuite = voprf::Ristretto255;
|
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # type CipherSuite = p256::NistP256;
|
//! # type CipherSuite = p256::NistP256;
|
||||||
//! use rand::rngs::OsRng;
|
//! use rand::rngs::SysRng;
|
||||||
//! use rand::RngCore;
|
//! use rand::Rng;
|
||||||
//! use voprf::VoprfClient;
|
//! use voprf_vx::VoprfClient;
|
||||||
//!
|
//!
|
||||||
//! let mut client_rng = OsRng;
|
//! let mut client_rng = SysRng;
|
||||||
//! let client_blind_result = VoprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
|
//! let client_blind_result = VoprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
|
||||||
//! .expect("Unable to construct client");
|
//! .expect("Unable to construct client");
|
||||||
//! ```
|
//! ```
|
||||||
@@ -247,19 +242,19 @@
|
|||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # type CipherSuite = voprf::Ristretto255;
|
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # type CipherSuite = p256::NistP256;
|
//! # type CipherSuite = p256::NistP256;
|
||||||
//! # use voprf::{VoprfServerEvaluateResult, VoprfClient};
|
//! # use voprf_vx::{VoprfServerEvaluateResult, VoprfClient};
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::SysRng, Rng};
|
||||||
//! #
|
//! #
|
||||||
//! # let mut client_rng = OsRng;
|
//! # let mut client_rng = SysRng;
|
||||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
|
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
|
||||||
//! # b"input",
|
//! # b"input",
|
||||||
//! # &mut client_rng,
|
//! # &mut client_rng,
|
||||||
//! # ).expect("Unable to construct client");
|
//! # ).expect("Unable to construct client");
|
||||||
//! # use voprf::VoprfServer;
|
//! # use voprf_vx::VoprfServer;
|
||||||
//! # let mut server_rng = OsRng;
|
//! # let mut server_rng = SysRng;
|
||||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||||
//! let VoprfServerEvaluateResult { message, proof } =
|
//! let VoprfServerEvaluateResult { message, proof } =
|
||||||
//! server.blind_evaluate(&mut server_rng, &client_blind_result.message);
|
//! server.blind_evaluate(&mut server_rng, &client_blind_result.message);
|
||||||
@@ -274,19 +269,19 @@
|
|||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # type CipherSuite = voprf::Ristretto255;
|
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # type CipherSuite = p256::NistP256;
|
//! # type CipherSuite = p256::NistP256;
|
||||||
//! # use voprf::VoprfClient;
|
//! # use voprf_vx::VoprfClient;
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::SysRng, Rng};
|
||||||
//! #
|
//! #
|
||||||
//! # let mut client_rng = OsRng;
|
//! # let mut client_rng = SysRng;
|
||||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
|
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
|
||||||
//! # b"input",
|
//! # b"input",
|
||||||
//! # &mut client_rng,
|
//! # &mut client_rng,
|
||||||
//! # ).expect("Unable to construct client");
|
//! # ).expect("Unable to construct client");
|
||||||
//! # use voprf::VoprfServer;
|
//! # use voprf_vx::VoprfServer;
|
||||||
//! # let mut server_rng = OsRng;
|
//! # let mut server_rng = SysRng;
|
||||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||||
//! # let server_evaluate_result = server.blind_evaluate(
|
//! # let server_evaluate_result = server.blind_evaluate(
|
||||||
//! # &mut server_rng,
|
//! # &mut server_rng,
|
||||||
@@ -315,19 +310,19 @@
|
|||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # type CipherSuite = voprf::Ristretto255;
|
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # type CipherSuite = p256::NistP256;
|
//! # type CipherSuite = p256::NistP256;
|
||||||
//! # use voprf::VoprfClient;
|
//! # use voprf_vx::VoprfClient;
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::SysRng, Rng};
|
||||||
//! #
|
//! #
|
||||||
//! # let mut client_rng = OsRng;
|
//! # let mut client_rng = SysRng;
|
||||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
|
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
|
||||||
//! # b"input",
|
//! # b"input",
|
||||||
//! # &mut client_rng,
|
//! # &mut client_rng,
|
||||||
//! # ).expect("Unable to construct client");
|
//! # ).expect("Unable to construct client");
|
||||||
//! # use voprf::VoprfServer;
|
//! # use voprf_vx::VoprfServer;
|
||||||
//! # let mut server_rng = OsRng;
|
//! # let mut server_rng = SysRng;
|
||||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||||
//! # let server_evaluate_result = server.blind_evaluate(
|
//! # let server_evaluate_result = server.blind_evaluate(
|
||||||
//! # &mut server_rng,
|
//! # &mut server_rng,
|
||||||
@@ -369,13 +364,13 @@
|
|||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # type CipherSuite = voprf::Ristretto255;
|
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # type CipherSuite = p256::NistP256;
|
//! # type CipherSuite = p256::NistP256;
|
||||||
//! # use voprf::VoprfClient;
|
//! # use voprf_vx::VoprfClient;
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::SysRng, Rng};
|
||||||
//! #
|
//! #
|
||||||
//! let mut client_rng = OsRng;
|
//! let mut client_rng = SysRng;
|
||||||
//! let mut client_states = vec![];
|
//! let mut client_states = vec![];
|
||||||
//! let mut client_messages = vec![];
|
//! let mut client_messages = vec![];
|
||||||
//! for _ in 0..10 {
|
//! for _ in 0..10 {
|
||||||
@@ -393,13 +388,13 @@
|
|||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # type CipherSuite = voprf::Ristretto255;
|
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # type CipherSuite = p256::NistP256;
|
//! # type CipherSuite = p256::NistP256;
|
||||||
//! # use voprf::{VoprfServerBatchEvaluateFinishResult, VoprfClient};
|
//! # use voprf_vx::{VoprfServerBatchEvaluateFinishResult, VoprfClient};
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::SysRng, Rng};
|
||||||
//! #
|
//! #
|
||||||
//! # let mut client_rng = OsRng;
|
//! # let mut client_rng = SysRng;
|
||||||
//! # let mut client_states = vec![];
|
//! # let mut client_states = vec![];
|
||||||
//! # let mut client_messages = vec![];
|
//! # let mut client_messages = vec![];
|
||||||
//! # for _ in 0..10 {
|
//! # for _ in 0..10 {
|
||||||
@@ -410,8 +405,8 @@
|
|||||||
//! # client_states.push(client_blind_result.state);
|
//! # client_states.push(client_blind_result.state);
|
||||||
//! # client_messages.push(client_blind_result.message);
|
//! # client_messages.push(client_blind_result.message);
|
||||||
//! # }
|
//! # }
|
||||||
//! # use voprf::VoprfServer;
|
//! # use voprf_vx::VoprfServer;
|
||||||
//! let mut server_rng = OsRng;
|
//! let mut server_rng = SysRng;
|
||||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||||
//! let prepared_evaluation_elements = server.batch_blind_evaluate_prepare(client_messages.iter());
|
//! let prepared_evaluation_elements = server.batch_blind_evaluate_prepare(client_messages.iter());
|
||||||
//! let prepared_elements: Vec<_> = prepared_evaluation_elements.collect();
|
//! let prepared_elements: Vec<_> = prepared_evaluation_elements.collect();
|
||||||
@@ -427,13 +422,13 @@
|
|||||||
//! ```
|
//! ```
|
||||||
//! # #[cfg(feature = "alloc")] {
|
//! # #[cfg(feature = "alloc")] {
|
||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # type CipherSuite = voprf::Ristretto255;
|
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # type CipherSuite = p256::NistP256;
|
//! # type CipherSuite = p256::NistP256;
|
||||||
//! # use voprf::{VoprfServerBatchEvaluateResult, VoprfClient};
|
//! # use voprf_vx::{VoprfServerBatchEvaluateResult, VoprfClient};
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::SysRng, Rng};
|
||||||
//! #
|
//! #
|
||||||
//! # let mut client_rng = OsRng;
|
//! # let mut client_rng = SysRng;
|
||||||
//! # let mut client_states = vec![];
|
//! # let mut client_states = vec![];
|
||||||
//! # let mut client_messages = vec![];
|
//! # let mut client_messages = vec![];
|
||||||
//! # for _ in 0..10 {
|
//! # for _ in 0..10 {
|
||||||
@@ -444,8 +439,8 @@
|
|||||||
//! # client_states.push(client_blind_result.state);
|
//! # client_states.push(client_blind_result.state);
|
||||||
//! # client_messages.push(client_blind_result.message);
|
//! # client_messages.push(client_blind_result.message);
|
||||||
//! # }
|
//! # }
|
||||||
//! # use voprf::VoprfServer;
|
//! # use voprf_vx::VoprfServer;
|
||||||
//! let mut server_rng = OsRng;
|
//! let mut server_rng = SysRng;
|
||||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||||
//! let VoprfServerBatchEvaluateResult { messages, proof } = server
|
//! let VoprfServerBatchEvaluateResult { messages, proof } = server
|
||||||
//! .batch_blind_evaluate(&mut server_rng, &client_messages)
|
//! .batch_blind_evaluate(&mut server_rng, &client_messages)
|
||||||
@@ -461,13 +456,13 @@
|
|||||||
//! ```
|
//! ```
|
||||||
//! # #[cfg(feature = "alloc")] {
|
//! # #[cfg(feature = "alloc")] {
|
||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # type CipherSuite = voprf::Ristretto255;
|
//! # type CipherSuite = voprf_vx::Ristretto255;
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # type CipherSuite = p256::NistP256;
|
//! # type CipherSuite = p256::NistP256;
|
||||||
//! # use voprf::{VoprfServerBatchEvaluateResult, VoprfClient};
|
//! # use voprf_vx::{VoprfServerBatchEvaluateResult, VoprfClient};
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::SysRng, Rng};
|
||||||
//! #
|
//! #
|
||||||
//! # let mut client_rng = OsRng;
|
//! # let mut client_rng = SysRng;
|
||||||
//! # let mut client_states = vec![];
|
//! # let mut client_states = vec![];
|
||||||
//! # let mut client_messages = vec![];
|
//! # let mut client_messages = vec![];
|
||||||
//! # for _ in 0..10 {
|
//! # for _ in 0..10 {
|
||||||
@@ -478,8 +473,8 @@
|
|||||||
//! # client_states.push(client_blind_result.state);
|
//! # client_states.push(client_blind_result.state);
|
||||||
//! # client_messages.push(client_blind_result.message);
|
//! # client_messages.push(client_blind_result.message);
|
||||||
//! # }
|
//! # }
|
||||||
//! # use voprf::VoprfServer;
|
//! # use voprf_vx::VoprfServer;
|
||||||
//! # let mut server_rng = OsRng;
|
//! # let mut server_rng = SysRng;
|
||||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||||
//! # let VoprfServerBatchEvaluateResult { messages, proof } = server
|
//! # let VoprfServerBatchEvaluateResult { messages, proof } = server
|
||||||
//! # .batch_blind_evaluate(&mut server_rng, &client_messages)
|
//! # .batch_blind_evaluate(&mut server_rng, &client_messages)
|
||||||
@@ -511,7 +506,7 @@
|
|||||||
//! and [PoprfClient] are used, and that each of the functions accept an
|
//! and [PoprfClient] are used, and that each of the functions accept an
|
||||||
//! additional (and optional) info parameter which represents the public input.
|
//! additional (and optional) info parameter which represents the public input.
|
||||||
//! See
|
//! See
|
||||||
//! <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html#name-poprf-public-input>
|
//! <https://www.rfc-editor.org/rfc/rfc9497#name-poprf-public-input>
|
||||||
//! for more detailed information on how this public input should be used.
|
//! for more detailed information on how this public input should be used.
|
||||||
//!
|
//!
|
||||||
//! # Features
|
//! # Features
|
||||||
@@ -539,7 +534,7 @@
|
|||||||
//! (https://docs.rs/curve25519-dalek/4.0.0-pre.5/curve25519_dalek/index.html#backends)
|
//! (https://docs.rs/curve25519-dalek/4.0.0-pre.5/curve25519_dalek/index.html#backends)
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![cfg_attr(not(test), deny(unsafe_code))]
|
#![cfg_attr(not(test), deny(unsafe_code))]
|
||||||
#![warn(
|
#![warn(
|
||||||
clippy::cargo,
|
clippy::cargo,
|
||||||
@@ -555,9 +550,6 @@ extern crate alloc;
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
extern crate std;
|
extern crate std;
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
extern crate serde_ as serde;
|
|
||||||
|
|
||||||
mod ciphersuite;
|
mod ciphersuite;
|
||||||
mod common;
|
mod common;
|
||||||
mod error;
|
mod error;
|
||||||
|
|||||||
+49
-174
@@ -1,24 +1,19 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
//
|
// Copyright (c) VexaHub and contributors.
|
||||||
// This source code is licensed under both the MIT license found in the
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
||||||
// of this source tree.
|
|
||||||
|
|
||||||
//! Contains the main OPRF API
|
//! Contains the main OPRF API
|
||||||
|
|
||||||
use core::iter::{self, Map};
|
use core::iter::{self};
|
||||||
|
|
||||||
use derive_where::derive_where;
|
use derive_where::derive_where;
|
||||||
use digest::core_api::BlockSizeUser;
|
use digest::Output;
|
||||||
use digest::{Digest, Output, OutputSizeUser};
|
use hybrid_array::Array;
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256};
|
use rand_core::{TryCryptoRng, TryRng};
|
||||||
use generic_array::GenericArray;
|
|
||||||
use rand_core::{CryptoRng, RngCore};
|
|
||||||
|
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
derive_key_internal, deterministic_blind_unchecked, hash_to_group, i2osp_2,
|
BlindedElement, EvaluationElement, Mode, derive_key_internal, deterministic_blind_unchecked,
|
||||||
server_evaluate_hash_input, BlindedElement, EvaluationElement, Mode, STR_FINALIZE,
|
finalize_after_unblind, hash_to_group, server_evaluate_hash_input,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use crate::serialization::serde::Scalar;
|
use crate::serialization::serde::Scalar;
|
||||||
@@ -41,13 +36,9 @@ use crate::{CipherSuite, Error, Group, Result};
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
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>"))]
|
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||||
pub(crate) blind: <CS::Group as Group>::Scalar,
|
pub(crate) blind: <CS::Group as Group>::Scalar,
|
||||||
}
|
}
|
||||||
@@ -59,13 +50,9 @@ where
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
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>"))]
|
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||||
pub(crate) sk: <CS::Group as Group>::Scalar,
|
pub(crate) sk: <CS::Group as Group>::Scalar,
|
||||||
}
|
}
|
||||||
@@ -75,21 +62,17 @@ where
|
|||||||
// =================== //
|
// =================== //
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
|
|
||||||
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
|
/// Computes the first step for the multiplicative blinding version of
|
||||||
/// DH-OPRF.
|
/// DH-OPRF.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`].
|
/// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`].
|
||||||
pub fn blind<R: RngCore + CryptoRng>(
|
pub fn blind<R: TryRng + TryCryptoRng>(
|
||||||
input: &[u8],
|
input: &[u8],
|
||||||
blinding_factor_rng: &mut R,
|
blinding_factor_rng: &mut R,
|
||||||
) -> Result<OprfClientBlindResult<CS>> {
|
) -> 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)
|
Self::deterministic_blind_unchecked_inner(input, blind)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +119,7 @@ where
|
|||||||
) -> Result<Output<CS::Hash>> {
|
) -> Result<Output<CS::Hash>> {
|
||||||
let unblinded_element = evaluation_element.0 * &CS::Group::invert_scalar(self.blind);
|
let unblinded_element = evaluation_element.0 * &CS::Group::invert_scalar(self.blind);
|
||||||
let mut outputs =
|
let mut outputs =
|
||||||
finalize_after_unblind::<CS, _, _>(iter::once((input, unblinded_element)), &[]);
|
finalize_after_unblind::<CS, _, _>(iter::once((input, unblinded_element)));
|
||||||
outputs.next().unwrap()
|
outputs.next().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,18 +136,14 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
/// Produces a new instance of a [OprfServer] using a supplied RNG
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// [`Error::Protocol`] if the protocol fails and can't be completed.
|
/// [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self> {
|
pub fn new<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self> {
|
||||||
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
|
let mut seed = Array::<_, <CS::Group as Group>::ScalarLen>::default();
|
||||||
rng.fill_bytes(&mut seed);
|
rng.try_fill_bytes(&mut seed).map_err(|_| Error::Protocol)?;
|
||||||
Self::new_from_seed(&seed, &[])
|
Self::new_from_seed(&seed, &[])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,7 +172,7 @@ where
|
|||||||
Ok(Self { sk })
|
Ok(Self { sk })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only used for tests
|
/// Only used for tests
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
|
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
|
||||||
self.sk
|
self.sk
|
||||||
@@ -230,58 +209,13 @@ where
|
|||||||
|
|
||||||
/// Contains the fields that are returned by a non-verifiable client blind
|
/// 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)]
|
#[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
|
/// The state to be persisted on the client
|
||||||
pub state: OprfClient<CS>,
|
pub state: OprfClient<CS>,
|
||||||
/// The message to send to the server
|
/// The message to send to the server
|
||||||
pub message: BlindedElement<CS>,
|
pub message: BlindedElement<CS>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////
|
|
||||||
// Inner functions //
|
|
||||||
// =============== //
|
|
||||||
/////////////////////
|
|
||||||
|
|
||||||
type FinalizeAfterUnblindResult<'a, C, I, IE> = Map<
|
|
||||||
IE,
|
|
||||||
fn((I, <<C as CipherSuite>::Group as Group>::Elem)) -> Result<Output<<C as CipherSuite>::Hash>>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
/// Returned values can only fail with [`Error::Input`].
|
|
||||||
fn finalize_after_unblind<
|
|
||||||
'a,
|
|
||||||
CS: CipherSuite,
|
|
||||||
I: AsRef<[u8]>,
|
|
||||||
IE: 'a + Iterator<Item = (I, <CS::Group as Group>::Elem)>,
|
|
||||||
>(
|
|
||||||
inputs_and_unblinded_elements: IE,
|
|
||||||
_unused: &'a [u8],
|
|
||||||
) -> 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();
|
|
||||||
|
|
||||||
// hashInput = I2OSP(len(input), 2) || input ||
|
|
||||||
// I2OSP(len(unblindedElement), 2) || unblindedElement ||
|
|
||||||
// "Finalize"
|
|
||||||
// return Hash(hashInput)
|
|
||||||
Ok(CS::Hash::new()
|
|
||||||
.chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?)
|
|
||||||
.chain_update(input.as_ref())
|
|
||||||
.chain_update(elem_len)
|
|
||||||
.chain_update(CS::Group::serialize_elem(unblinded_element))
|
|
||||||
.chain_update(STR_FINALIZE)
|
|
||||||
.finalize())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////
|
///////////
|
||||||
// Tests //
|
// Tests //
|
||||||
// ===== //
|
// ===== //
|
||||||
@@ -291,67 +225,38 @@ where
|
|||||||
mod tests {
|
mod tests {
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
|
|
||||||
use generic_array::sequence::Concat;
|
use rand::TryRng;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::SysRng;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::{create_context_string, STR_HASH_TO_GROUP};
|
|
||||||
use crate::Group;
|
use crate::Group;
|
||||||
|
use crate::common::{Dst, STR_HASH_TO_GROUP};
|
||||||
|
use crate::tests::helpers::prf;
|
||||||
|
|
||||||
fn prf<CS: CipherSuite>(
|
fn base_retrieval<CS: CipherSuite>() {
|
||||||
input: &[u8],
|
|
||||||
key: <CS::Group as Group>::Scalar,
|
|
||||||
info: &[u8],
|
|
||||||
mode: Mode,
|
|
||||||
) -> Output<CS::Hash>
|
|
||||||
where
|
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
{
|
|
||||||
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
|
|
||||||
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst).unwrap();
|
|
||||||
|
|
||||||
let res = point * &key;
|
|
||||||
|
|
||||||
finalize_after_unblind::<CS, _, _>(iter::once((input, res)), info)
|
|
||||||
.next()
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn base_retrieval<CS: CipherSuite>()
|
|
||||||
where
|
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
{
|
|
||||||
let input = b"input";
|
let input = b"input";
|
||||||
let mut rng = OsRng;
|
let mut rng = SysRng;
|
||||||
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
|
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||||
let server = OprfServer::<CS>::new(&mut rng).unwrap();
|
let server = OprfServer::<CS>::new(&mut rng).unwrap();
|
||||||
let message = server.blind_evaluate(&client_blind_result.message);
|
let message = server.blind_evaluate(&client_blind_result.message);
|
||||||
let client_finalize_result = client_blind_result.state.finalize(input, &message).unwrap();
|
let client_finalize_result = client_blind_result.state.finalize(input, &message).unwrap();
|
||||||
let res2 = prf::<CS>(input, server.get_private_key(), &[], Mode::Oprf);
|
let res2 = prf::<CS>(input, server.get_private_key(), Mode::Oprf);
|
||||||
assert_eq!(client_finalize_result, res2);
|
assert_eq!(client_finalize_result, res2);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn base_inversion_unsalted<CS: CipherSuite>()
|
fn base_inversion_unsalted<CS: CipherSuite>() {
|
||||||
where
|
let mut rng = SysRng;
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
{
|
|
||||||
let mut rng = OsRng;
|
|
||||||
let mut input = [0u8; 64];
|
let mut input = [0u8; 64];
|
||||||
rng.fill_bytes(&mut input);
|
rng.try_fill_bytes(&mut input).unwrap();
|
||||||
let client_blind_result = OprfClient::<CS>::blind(&input, &mut rng).unwrap();
|
let client_blind_result = OprfClient::<CS>::blind(&input, &mut rng).unwrap();
|
||||||
let client_finalize_result = client_blind_result
|
let client_finalize_result = client_blind_result
|
||||||
.state
|
.state
|
||||||
.finalize(&input, &EvaluationElement(client_blind_result.message.0))
|
.finalize(&input, &EvaluationElement(client_blind_result.message.0))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let dst =
|
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, Mode::Oprf);
|
||||||
GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(Mode::Oprf));
|
let point = CS::Group::hash_to_curve::<CS::Hash>(&[&input], &dst.as_dst()).unwrap();
|
||||||
let point = CS::Group::hash_to_curve::<CS::Hash>(&[&input], &dst).unwrap();
|
let res2 = finalize_after_unblind::<CS, _, _>(iter::once((input.as_ref(), point)))
|
||||||
let res2 = finalize_after_unblind::<CS, _, _>(iter::once((input.as_ref(), point)), &[])
|
|
||||||
.next()
|
.next()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -359,13 +264,9 @@ mod tests {
|
|||||||
assert_eq!(client_finalize_result, res2);
|
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 input = b"input";
|
||||||
let mut rng = OsRng;
|
let mut rng = SysRng;
|
||||||
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
|
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||||
let server = OprfServer::<CS>::new(&mut rng).unwrap();
|
let server = OprfServer::<CS>::new(&mut rng).unwrap();
|
||||||
let server_result = server.blind_evaluate(&client_blind_result.message);
|
let server_result = server.blind_evaluate(&client_blind_result.message);
|
||||||
@@ -387,13 +288,9 @@ mod tests {
|
|||||||
assert!(client_finalize != server_evaluate);
|
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 input = b"input";
|
||||||
let mut rng = OsRng;
|
let mut rng = SysRng;
|
||||||
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
|
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||||
|
|
||||||
let mut state = client_blind_result.state;
|
let mut state = client_blind_result.state;
|
||||||
@@ -405,13 +302,9 @@ mod tests {
|
|||||||
assert!(message.serialize().iter().all(|&x| x == 0));
|
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 input = b"input";
|
||||||
let mut rng = OsRng;
|
let mut rng = SysRng;
|
||||||
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
|
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||||
let server = OprfServer::<CS>::new(&mut rng).unwrap();
|
let server = OprfServer::<CS>::new(&mut rng).unwrap();
|
||||||
let mut message = server.blind_evaluate(&client_blind_result.message);
|
let mut message = server.blind_evaluate(&client_blind_result.message);
|
||||||
@@ -424,29 +317,11 @@ mod tests {
|
|||||||
assert!(message.serialize().iter().all(|&x| x == 0));
|
assert!(message.serialize().iter().all(|&x| x == 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
crate::tests::test_all_curves!(
|
||||||
fn test_functionality() -> Result<()> {
|
base_retrieval,
|
||||||
use p256::NistP256;
|
base_inversion_unsalted,
|
||||||
|
server_evaluate,
|
||||||
#[cfg(feature = "ristretto255")]
|
zeroize_oprf_client,
|
||||||
{
|
zeroize_oprf_server,
|
||||||
use crate::Ristretto255;
|
);
|
||||||
|
|
||||||
base_retrieval::<Ristretto255>();
|
|
||||||
base_inversion_unsalted::<Ristretto255>();
|
|
||||||
server_evaluate::<Ristretto255>();
|
|
||||||
|
|
||||||
zeroize_oprf_client::<Ristretto255>();
|
|
||||||
zeroize_oprf_server::<Ristretto255>();
|
|
||||||
}
|
|
||||||
|
|
||||||
base_retrieval::<NistP256>();
|
|
||||||
base_inversion_unsalted::<NistP256>();
|
|
||||||
server_evaluate::<NistP256>();
|
|
||||||
|
|
||||||
zeroize_oprf_client::<NistP256>();
|
|
||||||
zeroize_oprf_server::<NistP256>();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+72
-184
@@ -1,9 +1,6 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
//
|
// Copyright (c) VexaHub and contributors.
|
||||||
// This source code is licensed under both the MIT license found in the
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
||||||
// of this source tree.
|
|
||||||
|
|
||||||
//! Contains the main POPRF API
|
//! Contains the main POPRF API
|
||||||
|
|
||||||
@@ -12,18 +9,15 @@ use alloc::vec::Vec;
|
|||||||
use core::iter::{self, Map, Repeat, Zip};
|
use core::iter::{self, Map, Repeat, Zip};
|
||||||
|
|
||||||
use derive_where::derive_where;
|
use derive_where::derive_where;
|
||||||
use digest::core_api::BlockSizeUser;
|
|
||||||
use digest::{Digest, Output, OutputSizeUser};
|
use digest::{Digest, Output, OutputSizeUser};
|
||||||
use generic_array::sequence::Concat;
|
use hybrid_array::typenum::Unsigned;
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256};
|
use hybrid_array::{Array, ArraySize};
|
||||||
use generic_array::GenericArray;
|
use rand_core::{TryCryptoRng, TryRng};
|
||||||
use rand_core::{CryptoRng, RngCore};
|
|
||||||
|
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
create_context_string, derive_keypair, deterministic_blind_unchecked, generate_proof,
|
BlindedElement, Dst, EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE,
|
||||||
hash_to_group, i2osp_2, server_evaluate_hash_input, verify_proof, BlindedElement,
|
STR_HASH_TO_SCALAR, STR_INFO, derive_keypair, deterministic_blind_unchecked, generate_proof,
|
||||||
EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE, STR_HASH_TO_SCALAR,
|
hash_to_group, i2osp_2, server_evaluate_hash_input, verify_proof,
|
||||||
STR_INFO,
|
|
||||||
};
|
};
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use crate::serialization::serde::{Element, Scalar};
|
use crate::serialization::serde::{Element, Scalar};
|
||||||
@@ -41,13 +35,9 @@ use crate::{CipherSuite, Error, Group, Result};
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
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>"))]
|
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||||
pub(crate) blind: <CS::Group as Group>::Scalar,
|
pub(crate) blind: <CS::Group as Group>::Scalar,
|
||||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||||
@@ -61,13 +51,9 @@ where
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
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>"))]
|
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||||
pub(crate) sk: <CS::Group as Group>::Scalar,
|
pub(crate) sk: <CS::Group as Group>::Scalar,
|
||||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||||
@@ -79,21 +65,17 @@ where
|
|||||||
// =================== //
|
// =================== //
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
|
|
||||||
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
|
/// Computes the first step for the multiplicative blinding version of
|
||||||
/// DH-OPRF.
|
/// DH-OPRF.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`].
|
/// [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`].
|
||||||
pub fn blind<R: RngCore + CryptoRng>(
|
pub fn blind<R: TryRng + TryCryptoRng>(
|
||||||
input: &[u8],
|
input: &[u8],
|
||||||
blinding_factor_rng: &mut R,
|
blinding_factor_rng: &mut R,
|
||||||
) -> Result<PoprfClientBlindResult<CS>> {
|
) -> 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)
|
Self::deterministic_blind_unchecked_inner(input, blind)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,7 +128,10 @@ where
|
|||||||
proof: &Proof<CS>,
|
proof: &Proof<CS>,
|
||||||
pk: <CS::Group as Group>::Elem,
|
pk: <CS::Group as Group>::Elem,
|
||||||
info: Option<&[u8]>,
|
info: Option<&[u8]>,
|
||||||
) -> Result<Output<CS::Hash>> {
|
) -> Result<Output<CS::Hash>>
|
||||||
|
where
|
||||||
|
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArraySize,
|
||||||
|
{
|
||||||
let clients = core::array::from_ref(self);
|
let clients = core::array::from_ref(self);
|
||||||
let messages = core::array::from_ref(evaluation_element);
|
let messages = core::array::from_ref(evaluation_element);
|
||||||
|
|
||||||
@@ -181,6 +166,7 @@ where
|
|||||||
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
|
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||||
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
|
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
|
||||||
<&'a IM as IntoIterator>::IntoIter: ExactSizeIterator,
|
<&'a IM as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||||
|
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArraySize,
|
||||||
{
|
{
|
||||||
let unblinded_elements = poprf_unblind(clients, messages, pk, proof, info)?;
|
let unblinded_elements = poprf_unblind(clients, messages, pk, proof, info)?;
|
||||||
|
|
||||||
@@ -194,18 +180,14 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
/// Produces a new instance of a [PoprfServer] using a supplied RNG
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// [`Error::Protocol`] if the protocol fails and can't be completed.
|
/// [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self> {
|
pub fn new<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self> {
|
||||||
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
|
let mut seed = Array::<_, <CS::Group as Group>::ScalarLen>::default();
|
||||||
rng.fill_bytes(&mut seed);
|
rng.try_fill_bytes(&mut seed).map_err(|_| Error::Protocol)?;
|
||||||
|
|
||||||
Self::new_from_seed(&seed, &[])
|
Self::new_from_seed(&seed, &[])
|
||||||
}
|
}
|
||||||
@@ -236,7 +218,7 @@ where
|
|||||||
Ok(Self { sk, pk })
|
Ok(Self { sk, pk })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only used for tests
|
/// Only used for tests
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
|
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
|
||||||
self.sk
|
self.sk
|
||||||
@@ -249,7 +231,7 @@ where
|
|||||||
/// # Errors
|
/// # Errors
|
||||||
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
|
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
|
||||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||||
pub fn blind_evaluate<R: RngCore + CryptoRng>(
|
pub fn blind_evaluate<R: TryRng + TryCryptoRng>(
|
||||||
&self,
|
&self,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
blinded_element: &BlindedElement<CS>,
|
blinded_element: &BlindedElement<CS>,
|
||||||
@@ -287,7 +269,7 @@ where
|
|||||||
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
|
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
|
||||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub fn batch_blind_evaluate<'a, R: RngCore + CryptoRng, IE>(
|
pub fn batch_blind_evaluate<'a, R: TryRng + TryCryptoRng, IE>(
|
||||||
&self,
|
&self,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
blinded_elements: &'a IE,
|
blinded_elements: &'a IE,
|
||||||
@@ -360,7 +342,7 @@ where
|
|||||||
pub fn batch_blind_evaluate_finish<
|
pub fn batch_blind_evaluate_finish<
|
||||||
'a,
|
'a,
|
||||||
'b,
|
'b,
|
||||||
R: RngCore + CryptoRng,
|
R: TryRng + TryCryptoRng,
|
||||||
IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator,
|
IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator,
|
||||||
IE,
|
IE,
|
||||||
>(
|
>(
|
||||||
@@ -385,7 +367,7 @@ where
|
|||||||
tweaked_key,
|
tweaked_key,
|
||||||
prepared_evaluation_elements
|
prepared_evaluation_elements
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|element| element.0 .0),
|
.map(|element| element.0.0),
|
||||||
blinded_elements.map(|element| element.0),
|
blinded_elements.map(|element| element.0),
|
||||||
Mode::Poprf,
|
Mode::Poprf,
|
||||||
)?;
|
)?;
|
||||||
@@ -393,7 +375,7 @@ where
|
|||||||
let messages = prepared_evaluation_elements.into_iter().map(<fn(
|
let messages = prepared_evaluation_elements.into_iter().map(<fn(
|
||||||
&PreparedEvaluationElement<CS>,
|
&PreparedEvaluationElement<CS>,
|
||||||
) -> _>::from(
|
) -> _>::from(
|
||||||
|element| EvaluationElement(element.0 .0),
|
|element| EvaluationElement(element.0.0),
|
||||||
));
|
));
|
||||||
|
|
||||||
Ok(PoprfServerBatchEvaluateFinishResult { messages, proof })
|
Ok(PoprfServerBatchEvaluateFinishResult { messages, proof })
|
||||||
@@ -428,11 +410,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
/// Creates a [BlindedElement] from a raw group element.
|
||||||
///
|
///
|
||||||
/// # Caution
|
/// # Caution
|
||||||
@@ -451,11 +429,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
/// Creates an [EvaluationElement] from a raw group element.
|
||||||
///
|
///
|
||||||
/// # Caution
|
/// # Caution
|
||||||
@@ -481,11 +455,7 @@ where
|
|||||||
|
|
||||||
/// Contains the fields that are returned by a verifiable client blind
|
/// Contains the fields that are returned by a verifiable client blind
|
||||||
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
#[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
|
/// The state to be persisted on the client
|
||||||
pub state: PoprfClient<CS>,
|
pub state: PoprfClient<CS>,
|
||||||
/// The message to send to the server
|
/// The message to send to the server
|
||||||
@@ -498,11 +468,7 @@ pub type PoprfClientBatchFinalizeResult<'a, CS, II, IC, IM> =
|
|||||||
|
|
||||||
/// Contains the fields that are returned by a verifiable server evaluate
|
/// Contains the fields that are returned by a verifiable server evaluate
|
||||||
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
#[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
|
/// The message to send to the client
|
||||||
pub message: EvaluationElement<CS>,
|
pub message: EvaluationElement<CS>,
|
||||||
/// The proof for the client to verify
|
/// The proof for the client to verify
|
||||||
@@ -512,11 +478,7 @@ where
|
|||||||
/// Contains the fields that are returned by a verifiable server batch evaluate
|
/// 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)]
|
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||||
#[cfg(feature = "alloc")]
|
#[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
|
/// The messages to send to the client
|
||||||
pub messages: Vec<EvaluationElement<CS>>,
|
pub messages: Vec<EvaluationElement<CS>>,
|
||||||
/// The proof for the client to verify
|
/// The proof for the client to verify
|
||||||
@@ -541,24 +503,17 @@ pub type PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I> = Map<
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
pub struct PoprfPreparedTweak<CS: CipherSuite>(
|
pub struct PoprfPreparedTweak<CS: CipherSuite>(
|
||||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||||
<CS::Group as Group>::Scalar,
|
<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
|
/// Contains the fields that are returned by a partially verifiable server batch
|
||||||
/// evaluate prepare
|
/// evaluate prepare
|
||||||
#[derive_where(Debug; I, <CS::Group as Group>::Scalar)]
|
#[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`].
|
/// Prepared [`EvaluationElement`].
|
||||||
pub prepared_evaluation_elements: PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I>,
|
pub prepared_evaluation_elements: PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I>,
|
||||||
/// Prepared tweak.
|
/// Prepared tweak.
|
||||||
@@ -577,8 +532,6 @@ pub type PoprfServerBatchEvaluateFinishedMessages<'a, CS, I> = Map<
|
|||||||
#[derive_where(Debug; <&'a I as IntoIterator>::IntoIter, <CS::Group as Group>::Scalar)]
|
#[derive_where(Debug; <&'a I as IntoIterator>::IntoIter, <CS::Group as Group>::Scalar)]
|
||||||
pub struct PoprfServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I>
|
pub struct PoprfServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I>
|
||||||
where
|
where
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
&'a I: IntoIterator<Item = &'a PreparedEvaluationElement<CS>>,
|
&'a I: IntoIterator<Item = &'a PreparedEvaluationElement<CS>>,
|
||||||
{
|
{
|
||||||
/// The [`EvaluationElement`]s to send to the client
|
/// The [`EvaluationElement`]s to send to the client
|
||||||
@@ -599,11 +552,7 @@ where
|
|||||||
fn compute_tweaked_key<CS: CipherSuite>(
|
fn compute_tweaked_key<CS: CipherSuite>(
|
||||||
pk: <CS::Group as Group>::Elem,
|
pk: <CS::Group as Group>::Elem,
|
||||||
info: Option<&[u8]>,
|
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
|
// None for info is treated the same as empty bytes
|
||||||
let info = info.unwrap_or_default();
|
let info = info.unwrap_or_default();
|
||||||
|
|
||||||
@@ -616,10 +565,9 @@ where
|
|||||||
let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
|
let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
|
||||||
let framed_info = [STR_INFO.as_slice(), &info_len, info];
|
let framed_info = [STR_INFO.as_slice(), &info_len, info];
|
||||||
|
|
||||||
let dst =
|
let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, Mode::Poprf);
|
||||||
GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(Mode::Poprf));
|
|
||||||
// This can't fail, the size of the `input` is known.
|
// This can't fail, the size of the `input` is known.
|
||||||
let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst).unwrap();
|
let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst.as_dst()).unwrap();
|
||||||
|
|
||||||
let t = CS::Group::base_elem() * &m;
|
let t = CS::Group::base_elem() * &m;
|
||||||
let tweaked_key = t + &pk;
|
let tweaked_key = t + &pk;
|
||||||
@@ -638,11 +586,7 @@ where
|
|||||||
fn compute_tweak<CS: CipherSuite>(
|
fn compute_tweak<CS: CipherSuite>(
|
||||||
sk: <CS::Group as Group>::Scalar,
|
sk: <CS::Group as Group>::Scalar,
|
||||||
info: Option<&[u8]>,
|
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
|
// None for info is treated the same as empty bytes
|
||||||
let info = info.unwrap_or_default();
|
let info = info.unwrap_or_default();
|
||||||
|
|
||||||
@@ -654,10 +598,9 @@ where
|
|||||||
let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
|
let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
|
||||||
let framed_info = [STR_INFO.as_slice(), &info_len, info];
|
let framed_info = [STR_INFO.as_slice(), &info_len, info];
|
||||||
|
|
||||||
let dst =
|
let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, Mode::Poprf);
|
||||||
GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(Mode::Poprf));
|
|
||||||
// This can't fail, the size of the `input` is known.
|
// This can't fail, the size of the `input` is known.
|
||||||
let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst).unwrap();
|
let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst.as_dst()).unwrap();
|
||||||
|
|
||||||
let t = sk + &m;
|
let t = sk + &m;
|
||||||
|
|
||||||
@@ -694,8 +637,6 @@ fn poprf_unblind<'a, CS: 'a + CipherSuite, IC, IM>(
|
|||||||
info: Option<&[u8]>,
|
info: Option<&[u8]>,
|
||||||
) -> Result<PoprfUnblindResult<'a, CS, IC, IM>>
|
) -> Result<PoprfUnblindResult<'a, CS, IC, IM>>
|
||||||
where
|
where
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
&'a IC: 'a + IntoIterator<Item = &'a PoprfClient<CS>>,
|
&'a IC: 'a + IntoIterator<Item = &'a PoprfClient<CS>>,
|
||||||
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
|
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||||
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
|
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
|
||||||
@@ -723,7 +664,7 @@ where
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(blinds
|
Ok(blinds
|
||||||
.zip(messages.into_iter())
|
.zip(messages)
|
||||||
.map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
|
.map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -731,7 +672,7 @@ type FinalizeAfterUnblindResult<'a, CS, IE, II> = Map<
|
|||||||
Zip<Zip<IE, II>, Repeat<&'a [u8]>>,
|
Zip<Zip<IE, II>, Repeat<&'a [u8]>>,
|
||||||
fn(
|
fn(
|
||||||
((<<CS as CipherSuite>::Group as Group>::Elem, &[u8]), &[u8]),
|
((<<CS as CipherSuite>::Group as Group>::Elem, &[u8]), &[u8]),
|
||||||
) -> Result<GenericArray<u8, <<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize>>,
|
) -> Result<Output<<CS as CipherSuite>::Hash>>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/// Can only fail with [`Error::Batch`] and returned values can only fail with
|
/// Can only fail with [`Error::Batch`] and returned values can only fail with
|
||||||
@@ -747,8 +688,7 @@ fn finalize_after_unblind<
|
|||||||
info: Option<&'a [u8]>,
|
info: Option<&'a [u8]>,
|
||||||
) -> Result<FinalizeAfterUnblindResult<'a, CS, IE, II>>
|
) -> Result<FinalizeAfterUnblindResult<'a, CS, IE, II>>
|
||||||
where
|
where
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArraySize,
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
{
|
{
|
||||||
if unblinded_elements.len() != inputs.len() {
|
if unblinded_elements.len() != inputs.len() {
|
||||||
return Err(Error::Batch);
|
return Err(Error::Batch);
|
||||||
@@ -787,31 +727,24 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use core::ops::Add;
|
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
|
|
||||||
use generic_array::typenum::Sum;
|
use rand::rngs::SysRng;
|
||||||
use generic_array::ArrayLength;
|
|
||||||
use rand::rngs::OsRng;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::STR_HASH_TO_GROUP;
|
|
||||||
use crate::Group;
|
use crate::Group;
|
||||||
|
use crate::common::STR_HASH_TO_GROUP;
|
||||||
|
|
||||||
fn prf<CS: CipherSuite>(
|
fn prf<CS: CipherSuite>(
|
||||||
input: &[u8],
|
input: &[u8],
|
||||||
key: <CS::Group as Group>::Scalar,
|
key: <CS::Group as Group>::Scalar,
|
||||||
info: &[u8],
|
info: &[u8],
|
||||||
mode: Mode,
|
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 t = compute_tweak::<CS>(key, Some(info)).unwrap();
|
||||||
|
|
||||||
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
|
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, mode);
|
||||||
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst).unwrap();
|
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
|
||||||
|
|
||||||
// evaluatedElement = G.ScalarInverse(t) * blindedElement
|
// evaluatedElement = G.ScalarInverse(t) * blindedElement
|
||||||
let res = point * &CS::Group::invert_scalar(t);
|
let res = point * &CS::Group::invert_scalar(t);
|
||||||
@@ -823,14 +756,10 @@ mod tests {
|
|||||||
.unwrap()
|
.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 input = b"input";
|
||||||
let info = b"info";
|
let info = b"info";
|
||||||
let mut rng = OsRng;
|
let mut rng = SysRng;
|
||||||
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
||||||
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||||
let server_result = server
|
let server_result = server
|
||||||
@@ -850,24 +779,19 @@ mod tests {
|
|||||||
assert_eq!(client_finalize_result, res2);
|
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 input = b"input";
|
||||||
let info = b"info";
|
let info = b"info";
|
||||||
let mut rng = OsRng;
|
let mut rng = SysRng;
|
||||||
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
||||||
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||||
let server_result = server
|
let server_result = server
|
||||||
.blind_evaluate(&mut rng, &client_blind_result.message, Some(info))
|
.blind_evaluate(&mut rng, &client_blind_result.message, Some(info))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let wrong_pk = {
|
let wrong_pk = {
|
||||||
let dst = GenericArray::from(STR_HASH_TO_GROUP)
|
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, Mode::Oprf);
|
||||||
.concat(create_context_string::<CS>(Mode::Oprf));
|
|
||||||
// Choose a group element that is unlikely to be the right public key
|
// Choose a group element that is unlikely to be the right public key
|
||||||
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst).unwrap()
|
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
|
||||||
};
|
};
|
||||||
let client_finalize_result = client_blind_result.state.finalize(
|
let client_finalize_result = client_blind_result.state.finalize(
|
||||||
input,
|
input,
|
||||||
@@ -879,14 +803,10 @@ mod tests {
|
|||||||
assert!(client_finalize_result.is_err());
|
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 input = b"input";
|
||||||
let info = Some(b"info".as_slice());
|
let info = Some(b"info".as_slice());
|
||||||
let mut rng = OsRng;
|
let mut rng = SysRng;
|
||||||
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||||
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
||||||
let server_result = server
|
let server_result = server
|
||||||
@@ -916,15 +836,9 @@ mod tests {
|
|||||||
assert!(client_finalize != server_evaluate);
|
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 input = b"input";
|
||||||
let mut rng = OsRng;
|
let mut rng = SysRng;
|
||||||
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||||
|
|
||||||
let mut state = client_blind_result.state;
|
let mut state = client_blind_result.state;
|
||||||
@@ -936,18 +850,10 @@ mod tests {
|
|||||||
assert!(message.serialize().iter().all(|&x| x == 0));
|
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 input = b"input";
|
||||||
let info = b"info";
|
let info = b"info";
|
||||||
let mut rng = OsRng;
|
let mut rng = SysRng;
|
||||||
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
||||||
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||||
let server_result = server
|
let server_result = server
|
||||||
@@ -967,29 +873,11 @@ mod tests {
|
|||||||
assert!(proof.serialize().iter().all(|&x| x == 0));
|
assert!(proof.serialize().iter().all(|&x| x == 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
crate::tests::test_all_curves!(
|
||||||
fn test_functionality() -> Result<()> {
|
verifiable_retrieval,
|
||||||
use p256::NistP256;
|
verifiable_bad_public_key,
|
||||||
|
verifiable_server_evaluate,
|
||||||
#[cfg(feature = "ristretto255")]
|
zeroize_verifiable_client,
|
||||||
{
|
zeroize_verifiable_server,
|
||||||
use crate::Ristretto255;
|
);
|
||||||
|
|
||||||
verifiable_retrieval::<Ristretto255>();
|
|
||||||
verifiable_bad_public_key::<Ristretto255>();
|
|
||||||
verifiable_server_evaluate::<Ristretto255>();
|
|
||||||
|
|
||||||
zeroize_verifiable_client::<Ristretto255>();
|
|
||||||
zeroize_verifiable_server::<Ristretto255>();
|
|
||||||
}
|
|
||||||
|
|
||||||
verifiable_retrieval::<NistP256>();
|
|
||||||
verifiable_bad_public_key::<NistP256>();
|
|
||||||
verifiable_server_evaluate::<NistP256>();
|
|
||||||
|
|
||||||
zeroize_verifiable_client::<NistP256>();
|
|
||||||
zeroize_verifiable_server::<NistP256>();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+300
-288
@@ -1,295 +1,22 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
//
|
// Copyright (c) VexaHub and contributors.
|
||||||
// This source code is licensed under both the MIT license found in the
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
||||||
// of this source tree.
|
|
||||||
|
|
||||||
//! Handles the serialization of each of the components used in the VOPRF
|
//! Handles the serialization of each of the components used in the VOPRF
|
||||||
//! protocol
|
//! protocol
|
||||||
|
|
||||||
use core::ops::Add;
|
use hybrid_array::Array;
|
||||||
|
use hybrid_array::typenum::{Sum, Unsigned};
|
||||||
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::{
|
use crate::{
|
||||||
BlindedElement, CipherSuite, Error, EvaluationElement, Group, OprfClient, OprfServer,
|
BlindedElement, CipherSuite, Error, EvaluationElement, Group, OprfClient, OprfServer,
|
||||||
PoprfClient, PoprfServer, Proof, Result, VoprfClient, VoprfServer,
|
PoprfClient, PoprfServer, Proof, Result, VoprfClient, VoprfServer,
|
||||||
};
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////
|
/////////////////////////////
|
||||||
// Serialization and Deserialization for High-Level API //
|
// Deserialization Helpers //
|
||||||
// ==================================================== //
|
// ======================= //
|
||||||
//////////////////////////////////////////////////////////
|
/////////////////////////////
|
||||||
|
|
||||||
/// Length of [`OprfClient`] in bytes for serialization.
|
|
||||||
pub type OprfClientLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
|
|
||||||
|
|
||||||
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) -> GenericArray<u8, OprfClientLen<CS>> {
|
|
||||||
CS::Group::serialize_scalar(self.blind)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
|
||||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
|
||||||
let blind = deserialize_scalar::<CS::Group>(&mut input)?;
|
|
||||||
|
|
||||||
Ok(Self { blind })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Length of [`VoprfClient`] in bytes for serialization.
|
|
||||||
pub type VoprfClientLen<CS> = Sum<
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
|
||||||
>;
|
|
||||||
|
|
||||||
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) -> 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))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
|
||||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
|
||||||
let blind = deserialize_scalar::<CS::Group>(&mut input)?;
|
|
||||||
let blinded_element = deserialize_elem::<CS::Group>(&mut input)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
blind,
|
|
||||||
blinded_element,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Length of [`PoprfClient`] in bytes for serialization.
|
|
||||||
pub type PoprfClientLen<CS> = Sum<
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
|
||||||
>;
|
|
||||||
|
|
||||||
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) -> 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))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
|
||||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
|
||||||
let blind = deserialize_scalar::<CS::Group>(&mut input)?;
|
|
||||||
let blinded_element = deserialize_elem::<CS::Group>(&mut input)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
blind,
|
|
||||||
blinded_element,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Length of [`OprfServer`] in bytes for serialization.
|
|
||||||
pub type OprfServerLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
|
|
||||||
|
|
||||||
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) -> GenericArray<u8, OprfServerLen<CS>> {
|
|
||||||
CS::Group::serialize_scalar(self.sk)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
|
||||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
|
||||||
let sk = deserialize_scalar::<CS::Group>(&mut input)?;
|
|
||||||
|
|
||||||
Ok(Self { sk })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Length of [`VoprfServer`] in bytes for serialization.
|
|
||||||
pub type VoprfServerLen<CS> = Sum<
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
|
||||||
>;
|
|
||||||
|
|
||||||
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) -> 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))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
|
||||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
|
||||||
let sk = deserialize_scalar::<CS::Group>(&mut input)?;
|
|
||||||
let pk = deserialize_elem::<CS::Group>(&mut input)?;
|
|
||||||
|
|
||||||
Ok(Self { sk, pk })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Length of [`PoprfServer`] in bytes for serialization.
|
|
||||||
pub type PoprfServerLen<CS> = Sum<
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
|
||||||
>;
|
|
||||||
|
|
||||||
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) -> 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))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
|
||||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
|
||||||
let sk = deserialize_scalar::<CS::Group>(&mut input)?;
|
|
||||||
let pk = deserialize_elem::<CS::Group>(&mut input)?;
|
|
||||||
|
|
||||||
Ok(Self { sk, pk })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Length of [`Proof`] in bytes for serialization.
|
|
||||||
pub type ProofLen<CS> = Sum<
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
|
||||||
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
|
||||||
>;
|
|
||||||
|
|
||||||
impl<CS: CipherSuite> Proof<CS>
|
|
||||||
where
|
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
{
|
|
||||||
/// Serialization into bytes
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
|
||||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
|
||||||
let c_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
|
|
||||||
let s_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
|
|
||||||
|
|
||||||
Ok(Proof { c_scalar, s_scalar })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Length of [`BlindedElement`] in bytes for serialization.
|
|
||||||
pub type BlindedElementLen<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen;
|
|
||||||
|
|
||||||
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) -> GenericArray<u8, BlindedElementLen<CS>> {
|
|
||||||
CS::Group::serialize_elem(self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
|
||||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
|
||||||
let value = deserialize_elem::<CS::Group>(&mut input)?;
|
|
||||||
|
|
||||||
Ok(Self(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Length of [`EvaluationElement`] in bytes for serialization.
|
|
||||||
pub type EvaluationElementLen<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen;
|
|
||||||
|
|
||||||
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) -> GenericArray<u8, EvaluationElementLen<CS>> {
|
|
||||||
CS::Group::serialize_elem(self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
|
||||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
|
||||||
let value = deserialize_elem::<CS::Group>(&mut input)?;
|
|
||||||
|
|
||||||
Ok(Self(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_elem<G: Group>(input: &mut &[u8]) -> Result<G::Elem> {
|
fn deserialize_elem<G: Group>(input: &mut &[u8]) -> Result<G::Elem> {
|
||||||
let input = input
|
let input = input
|
||||||
@@ -306,11 +33,11 @@ fn deserialize_scalar<G: Group>(input: &mut &[u8]) -> Result<G::Scalar> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trait SliceExt {
|
trait SliceExt {
|
||||||
fn take_ext(self: &mut &Self, take: usize) -> Option<&Self>;
|
fn take_ext<'a>(self: &mut &'a Self, take: usize) -> Option<&'a Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> SliceExt for [T] {
|
impl<T> SliceExt for [T] {
|
||||||
fn take_ext(self: &mut &Self, take: usize) -> Option<&Self> {
|
fn take_ext<'a>(self: &mut &'a Self, take: usize) -> Option<&'a Self> {
|
||||||
if take > self.len() {
|
if take > self.len() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@@ -321,11 +48,163 @@ impl<T> SliceExt for [T] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
// Serialization Macros //
|
||||||
|
// ======================== //
|
||||||
|
//////////////////////////////
|
||||||
|
|
||||||
|
macro_rules! impl_serde_scalar {
|
||||||
|
($ty:ident, $len:ident, $field:ident) => {
|
||||||
|
/// Length in bytes for serialization.
|
||||||
|
pub type $len<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
|
||||||
|
|
||||||
|
impl<CS: CipherSuite> $ty<CS> {
|
||||||
|
/// Serialization into bytes
|
||||||
|
pub fn serialize(&self) -> Array<u8, $len<CS>> {
|
||||||
|
CS::Group::serialize_scalar(self.$field)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialization from bytes
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
||||||
|
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
||||||
|
let $field = deserialize_scalar::<CS::Group>(&mut input)?;
|
||||||
|
|
||||||
|
if !input.is_empty() {
|
||||||
|
return Err(Error::Deserialization);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { $field })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_serde_scalar_elem {
|
||||||
|
($ty:ident, $len:ident, $scalar_field:ident, $elem_field:ident) => {
|
||||||
|
/// Length in bytes for serialization.
|
||||||
|
pub type $len<CS> = Sum<
|
||||||
|
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
||||||
|
<<CS as CipherSuite>::Group as Group>::ElemLen,
|
||||||
|
>;
|
||||||
|
|
||||||
|
impl<CS: CipherSuite> $ty<CS> {
|
||||||
|
/// Serialization into bytes
|
||||||
|
pub fn serialize(&self) -> Array<u8, $len<CS>> {
|
||||||
|
<CS::Group as Group>::serialize_scalar(self.$scalar_field)
|
||||||
|
.concat(<CS::Group as Group>::serialize_elem(self.$elem_field))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialization from bytes
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
||||||
|
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
||||||
|
let $scalar_field = deserialize_scalar::<CS::Group>(&mut input)?;
|
||||||
|
let $elem_field = deserialize_elem::<CS::Group>(&mut input)?;
|
||||||
|
|
||||||
|
if !input.is_empty() {
|
||||||
|
return Err(Error::Deserialization);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
$scalar_field,
|
||||||
|
$elem_field,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_serde_elem {
|
||||||
|
($ty:ident, $len:ident) => {
|
||||||
|
/// Length in bytes for serialization.
|
||||||
|
pub type $len<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen;
|
||||||
|
|
||||||
|
impl<CS: CipherSuite> $ty<CS> {
|
||||||
|
/// Serialization into bytes
|
||||||
|
pub fn serialize(&self) -> Array<u8, $len<CS>> {
|
||||||
|
CS::Group::serialize_elem(self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialization from bytes
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
||||||
|
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
||||||
|
let value = deserialize_elem::<CS::Group>(&mut input)?;
|
||||||
|
|
||||||
|
if !input.is_empty() {
|
||||||
|
return Err(Error::Deserialization);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////
|
||||||
|
// Serialization and Deserialization for High-Level API //
|
||||||
|
// ==================================================== //
|
||||||
|
//////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
impl_serde_scalar!(OprfClient, OprfClientLen, blind);
|
||||||
|
impl_serde_scalar!(OprfServer, OprfServerLen, sk);
|
||||||
|
|
||||||
|
impl_serde_elem!(BlindedElement, BlindedElementLen);
|
||||||
|
impl_serde_elem!(EvaluationElement, EvaluationElementLen);
|
||||||
|
|
||||||
|
impl_serde_scalar_elem!(VoprfClient, VoprfClientLen, blind, blinded_element);
|
||||||
|
impl_serde_scalar_elem!(PoprfClient, PoprfClientLen, blind, blinded_element);
|
||||||
|
impl_serde_scalar_elem!(VoprfServer, VoprfServerLen, sk, pk);
|
||||||
|
impl_serde_scalar_elem!(PoprfServer, PoprfServerLen, sk, pk);
|
||||||
|
|
||||||
|
/////////////////////
|
||||||
|
// Proof (One-Off) //
|
||||||
|
// =============== //
|
||||||
|
/////////////////////
|
||||||
|
|
||||||
|
/// Length of [`Proof`] in bytes for serialization.
|
||||||
|
pub type ProofLen<CS> = Sum<
|
||||||
|
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
||||||
|
<<CS as CipherSuite>::Group as Group>::ScalarLen,
|
||||||
|
>;
|
||||||
|
|
||||||
|
impl<CS: CipherSuite> Proof<CS> {
|
||||||
|
/// Serialization into bytes
|
||||||
|
pub fn serialize(&self) -> Array<u8, ProofLen<CS>> {
|
||||||
|
CS::Group::serialize_scalar(self.c_scalar)
|
||||||
|
.concat(CS::Group::serialize_scalar(self.s_scalar))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialization from bytes
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
||||||
|
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
||||||
|
let c_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
|
||||||
|
let s_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
|
||||||
|
|
||||||
|
if !input.is_empty() {
|
||||||
|
return Err(Error::Deserialization);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Proof { c_scalar, s_scalar })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////
|
||||||
|
// Serde Support //
|
||||||
|
// ===================== //
|
||||||
|
///////////////////////////
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
pub(crate) mod serde {
|
pub(crate) mod serde {
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use generic_array::GenericArray;
|
use hybrid_array::Array;
|
||||||
use serde::de::{Deserializer, Error};
|
use serde::de::{Deserializer, Error};
|
||||||
use serde::ser::Serializer;
|
use serde::ser::Serializer;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -339,7 +218,7 @@ pub(crate) mod serde {
|
|||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
GenericArray::<_, G::ElemLen>::deserialize(deserializer)
|
Array::<_, G::ElemLen>::deserialize(deserializer)
|
||||||
.and_then(|bytes| G::deserialize_elem(&bytes).map_err(D::Error::custom))
|
.and_then(|bytes| G::deserialize_elem(&bytes).map_err(D::Error::custom))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,7 +237,7 @@ pub(crate) mod serde {
|
|||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
GenericArray::<_, G::ScalarLen>::deserialize(deserializer)
|
Array::<_, G::ScalarLen>::deserialize(deserializer)
|
||||||
.and_then(|bytes| G::deserialize_scalar(&bytes).map_err(D::Error::custom))
|
.and_then(|bytes| G::deserialize_scalar(&bytes).map_err(D::Error::custom))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,6 +260,7 @@ mod test {
|
|||||||
VoprfClient, VoprfServer,
|
VoprfClient, VoprfServer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Fuzz: no panics on arbitrary input
|
||||||
macro_rules! test_deserialize {
|
macro_rules! test_deserialize {
|
||||||
($item:ident, $bytes:ident) => {
|
($item:ident, $bytes:ident) => {
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
@@ -388,10 +268,43 @@ mod test {
|
|||||||
let _ = $item::<crate::Ristretto255>::deserialize(&$bytes[..]);
|
let _ = $item::<crate::Ristretto255>::deserialize(&$bytes[..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = $item::<p256::NistP256>::deserialize(&$bytes[..]);
|
let _ = $item::<::p256::NistP256>::deserialize(&$bytes[..]);
|
||||||
|
let _ = $item::<::p384::NistP384>::deserialize(&$bytes[..]);
|
||||||
|
let _ = $item::<::p521::NistP521>::deserialize(&$bytes[..]);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Roundtrip: serialize to deserialize == original
|
||||||
|
macro_rules! test_roundtrip {
|
||||||
|
($item:ident, $cs:ty, $constructor:expr) => {{
|
||||||
|
let original = $constructor;
|
||||||
|
let bytes = original.serialize();
|
||||||
|
let recovered = $item::<$cs>::deserialize(&bytes).expect("roundtrip deserialize");
|
||||||
|
assert_eq!(original.serialize(), recovered.serialize());
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trailing bytes: valid serialization + extra byte must fail
|
||||||
|
macro_rules! test_trailing {
|
||||||
|
($item:ident, $cs:ty, $constructor:expr) => {{
|
||||||
|
let original = $constructor;
|
||||||
|
let bytes = original.serialize();
|
||||||
|
let mut extended = bytes.to_vec();
|
||||||
|
extended.push(0x00);
|
||||||
|
assert!($item::<$cs>::deserialize(&extended).is_err());
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Truncated: valid serialization minus one byte must fail
|
||||||
|
macro_rules! test_truncated {
|
||||||
|
($item:ident, $cs:ty, $constructor:expr) => {{
|
||||||
|
let original = $constructor;
|
||||||
|
let bytes = original.serialize();
|
||||||
|
let truncated = &bytes[..bytes.len() - 1];
|
||||||
|
assert!($item::<$cs>::deserialize(truncated).is_err());
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
proptest! {
|
proptest! {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_nocrash_oprf_client(bytes in vec(any::<u8>(), 0..200)) {
|
fn test_nocrash_oprf_client(bytes in vec(any::<u8>(), 0..200)) {
|
||||||
@@ -439,4 +352,103 @@ mod test {
|
|||||||
test_deserialize!(Proof, bytes);
|
test_deserialize!(Proof, bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! structured_tests {
|
||||||
|
($cs:ty, $mod:ident) => {
|
||||||
|
mod $mod {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use rand::rngs::SysRng;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn roundtrip_oprf_client() {
|
||||||
|
let client = OprfClient::<$cs>::blind(b"input", &mut SysRng)
|
||||||
|
.expect("blind")
|
||||||
|
.state;
|
||||||
|
test_roundtrip!(OprfClient, $cs, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn roundtrip_oprf_server() {
|
||||||
|
let server = OprfServer::<$cs>::new(&mut SysRng).expect("new");
|
||||||
|
test_roundtrip!(OprfServer, $cs, server);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn roundtrip_voprf_client() {
|
||||||
|
let client = VoprfClient::<$cs>::blind(b"input", &mut SysRng)
|
||||||
|
.expect("blind")
|
||||||
|
.state;
|
||||||
|
test_roundtrip!(VoprfClient, $cs, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn roundtrip_voprf_server() {
|
||||||
|
let server = VoprfServer::<$cs>::new(&mut SysRng).expect("new");
|
||||||
|
test_roundtrip!(VoprfServer, $cs, server);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn roundtrip_poprf_client() {
|
||||||
|
let client = PoprfClient::<$cs>::blind(b"input", &mut SysRng)
|
||||||
|
.expect("blind")
|
||||||
|
.state;
|
||||||
|
test_roundtrip!(PoprfClient, $cs, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn roundtrip_poprf_server() {
|
||||||
|
let server = PoprfServer::<$cs>::new(&mut SysRng).expect("new");
|
||||||
|
test_roundtrip!(PoprfServer, $cs, server);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trailing_oprf_client() {
|
||||||
|
let client = OprfClient::<$cs>::blind(b"input", &mut SysRng)
|
||||||
|
.expect("blind")
|
||||||
|
.state;
|
||||||
|
test_trailing!(OprfClient, $cs, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trailing_oprf_server() {
|
||||||
|
let server = OprfServer::<$cs>::new(&mut SysRng).expect("new");
|
||||||
|
test_trailing!(OprfServer, $cs, server);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn truncated_oprf_client() {
|
||||||
|
let client = OprfClient::<$cs>::blind(b"input", &mut SysRng)
|
||||||
|
.expect("blind")
|
||||||
|
.state;
|
||||||
|
test_truncated!(OprfClient, $cs, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn truncated_oprf_server() {
|
||||||
|
let server = OprfServer::<$cs>::new(&mut SysRng).expect("new");
|
||||||
|
test_truncated!(OprfServer, $cs, server);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_input_fails() {
|
||||||
|
assert!(OprfClient::<$cs>::deserialize(&[]).is_err());
|
||||||
|
assert!(OprfServer::<$cs>::deserialize(&[]).is_err());
|
||||||
|
assert!(VoprfClient::<$cs>::deserialize(&[]).is_err());
|
||||||
|
assert!(VoprfServer::<$cs>::deserialize(&[]).is_err());
|
||||||
|
assert!(PoprfClient::<$cs>::deserialize(&[]).is_err());
|
||||||
|
assert!(PoprfServer::<$cs>::deserialize(&[]).is_err());
|
||||||
|
assert!(BlindedElement::<$cs>::deserialize(&[]).is_err());
|
||||||
|
assert!(EvaluationElement::<$cs>::deserialize(&[]).is_err());
|
||||||
|
assert!(Proof::<$cs>::deserialize(&[]).is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ristretto255")]
|
||||||
|
structured_tests!(crate::Ristretto255, ristretto255);
|
||||||
|
structured_tests!(::p256::NistP256, p256);
|
||||||
|
structured_tests!(::p384::NistP384, p384);
|
||||||
|
structured_tests!(::p521::NistP521, p521);
|
||||||
}
|
}
|
||||||
|
|||||||
+913
-1029
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
// Copyright (c) VexaHub and contributors.
|
||||||
|
|
||||||
|
use core::iter;
|
||||||
|
|
||||||
|
use digest::Output;
|
||||||
|
|
||||||
|
use crate::common::{Dst, Mode, STR_HASH_TO_GROUP, finalize_after_unblind};
|
||||||
|
use crate::{CipherSuite, Group};
|
||||||
|
|
||||||
|
pub(crate) fn prf<CS: CipherSuite>(
|
||||||
|
input: &[u8],
|
||||||
|
key: <CS::Group as Group>::Scalar,
|
||||||
|
mode: Mode,
|
||||||
|
) -> Output<CS::Hash> {
|
||||||
|
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, mode);
|
||||||
|
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
|
||||||
|
|
||||||
|
let res = point * &key;
|
||||||
|
|
||||||
|
finalize_after_unblind::<CS, _, _>(iter::once((input, res)))
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
// Copyright (c) VexaHub and contributors.
|
||||||
|
|
||||||
|
macro_rules! test_all_curves {
|
||||||
|
($($test_fn:ident),+ $(,)?) => {
|
||||||
|
#[test]
|
||||||
|
fn test_functionality() -> $crate::Result<()> {
|
||||||
|
#[cfg(feature = "ristretto255")]
|
||||||
|
{
|
||||||
|
$( $test_fn::<$crate::Ristretto255>(); )+
|
||||||
|
}
|
||||||
|
$( $test_fn::<::p256::NistP256>(); )+
|
||||||
|
$( $test_fn::<::p384::NistP384>(); )+
|
||||||
|
$( $test_fn::<::p521::NistP521>(); )+
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use test_all_curves;
|
||||||
+26
-22
@@ -1,14 +1,12 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
//
|
// Copyright (c) VexaHub and contributors.
|
||||||
// This source code is licensed under both the MIT license found in the
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
||||||
// of this source tree.
|
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::cmp::min;
|
use core::cmp::min;
|
||||||
|
|
||||||
use rand_core::{CryptoRng, Error, RngCore};
|
use core::convert::Infallible;
|
||||||
|
use rand_core::{TryCryptoRng, TryRng};
|
||||||
|
|
||||||
/// A simple implementation of `RngCore` for testing purposes.
|
/// A simple implementation of `RngCore` for testing purposes.
|
||||||
///
|
///
|
||||||
@@ -37,29 +35,35 @@ fn rotate_left<T>(data: &mut [T], steps: usize) {
|
|||||||
data.reverse();
|
data.reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RngCore for CycleRng {
|
impl TryRng for CycleRng {
|
||||||
fn next_u32(&mut self) -> u32 {
|
type Error = Infallible;
|
||||||
unimplemented!()
|
|
||||||
|
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
|
||||||
|
let mut buf = [0u8; 4];
|
||||||
|
|
||||||
|
self.try_fill_bytes(&mut buf)?;
|
||||||
|
|
||||||
|
Ok(u32::from_le_bytes(buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
|
||||||
fn next_u64(&mut self) -> u64 {
|
let mut buf = [0u8; 8];
|
||||||
unimplemented!()
|
|
||||||
|
self.try_fill_bytes(&mut buf)?;
|
||||||
|
|
||||||
|
Ok(u64::from_le_bytes(buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
|
||||||
let len = min(self.v.len(), dest.len());
|
let len = min(self.v.len(), dest.len());
|
||||||
dest[..len].copy_from_slice(&self.v[..len]);
|
|
||||||
rotate_left(&mut self.v, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
dest[..len].copy_from_slice(&self.v[..len]);
|
||||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
|
|
||||||
self.fill_bytes(dest);
|
rotate_left(&mut self.v, len);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is meant for testing only
|
// This is meant for testing only
|
||||||
impl CryptoRng for CycleRng {}
|
impl TryCryptoRng for CycleRng {}
|
||||||
|
|||||||
+8
-6
@@ -1,11 +1,13 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
//
|
// Copyright (c) VexaHub and contributors.
|
||||||
// This source code is licensed under both the MIT license found in the
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
||||||
// of this source tree.
|
|
||||||
|
|
||||||
mod cfrg_vectors;
|
mod cfrg_vectors;
|
||||||
|
mod macros;
|
||||||
mod mock_rng;
|
mod mock_rng;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod test_cfrg_vectors;
|
mod test_cfrg_vectors;
|
||||||
|
|
||||||
|
pub(crate) mod helpers;
|
||||||
|
|
||||||
|
pub(crate) use macros::test_all_curves;
|
||||||
|
|||||||
+11
-19
@@ -1,9 +1,6 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
//
|
// Copyright (c) VexaHub and contributors.
|
||||||
// This source code is licensed under both the MIT license found in the
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
||||||
// of this source tree.
|
|
||||||
|
|
||||||
use alloc::string::{String, ToString};
|
use alloc::string::{String, ToString};
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
@@ -14,55 +11,50 @@ pub(crate) fn rfc_to_json(input: &str) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_ciphersuites(input: &str) -> String {
|
fn parse_ciphersuites(input: &str) -> String {
|
||||||
let re = regex::Regex::new(r"## OPRF\((?P<ciphersuite>.+?)\)").unwrap();
|
let re = regex::Regex::new(r"\nA\.\d\. {2}(?P<ciphersuite>.+?)\n\n").unwrap();
|
||||||
let mut ciphersuites = vec![];
|
let mut ciphersuites = vec![];
|
||||||
|
|
||||||
let chunks: Vec<&str> = re.split(input).collect();
|
let chunks: Vec<&str> = re.split(input).collect();
|
||||||
let mut count = 1;
|
for (count, caps) in (1..).zip(re.captures_iter(input)) {
|
||||||
for caps in re.captures_iter(input) {
|
|
||||||
let ciphersuite = format!(
|
let ciphersuite = format!(
|
||||||
"\"{}\": {{ {} }}",
|
"\"{}\": {{ {} }}",
|
||||||
&caps["ciphersuite"],
|
&caps["ciphersuite"],
|
||||||
parse_modes(chunks[count])
|
parse_modes(chunks[count])
|
||||||
);
|
);
|
||||||
|
|
||||||
ciphersuites.push(ciphersuite);
|
ciphersuites.push(ciphersuite);
|
||||||
count += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ciphersuites.join(",\n")
|
ciphersuites.join(",\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_modes(input: &str) -> String {
|
fn parse_modes(input: &str) -> String {
|
||||||
let re = regex::Regex::new(r"### (?P<mode>.*+) Mode").unwrap();
|
let re = regex::Regex::new(r"A\.\d.\d\. {2}(?P<mode>.*?) Mode").unwrap();
|
||||||
let mut modes = vec![];
|
let mut modes = vec![];
|
||||||
|
|
||||||
let chunks: Vec<&str> = re.split(input).collect();
|
let chunks: Vec<&str> = re.split(input).collect();
|
||||||
let mut count = 1;
|
for (count, caps) in (1..).zip(re.captures_iter(input)) {
|
||||||
for caps in re.captures_iter(input) {
|
|
||||||
let mode = format!(
|
let mode = format!(
|
||||||
"\"{}\": [\n {} \n]",
|
"\"{}\": [\n {} \n]",
|
||||||
&caps["mode"],
|
&caps["mode"],
|
||||||
parse_vectors(chunks[count])
|
parse_vectors(chunks[count])
|
||||||
);
|
);
|
||||||
modes.push(mode);
|
modes.push(mode);
|
||||||
count += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
modes.join(",\n")
|
modes.join(",\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_vectors(input: &str) -> String {
|
fn parse_vectors(input: &str) -> String {
|
||||||
let re = regex::Regex::new(r"Test Vector.*+\n").unwrap();
|
let re = regex::Regex::new(r"A\.\d.\d\.\d\. {2}Test Vector.*+\n").unwrap();
|
||||||
let mut vectors = vec![];
|
let mut vectors = vec![];
|
||||||
|
|
||||||
let chunks: Vec<&str> = re.split(input).collect();
|
let chunks: Vec<&str> = re.split(input).collect();
|
||||||
let init_params = parse_params(chunks[0]);
|
let init_params = parse_params(chunks[0]);
|
||||||
|
|
||||||
let mut count = 1;
|
for (count, _) in (1..).zip(re.captures_iter(input)) {
|
||||||
for _ in re.captures_iter(input) {
|
|
||||||
let params = format!("{{\n{},\n{}\n}}", init_params, parse_params(chunks[count]));
|
let params = format!("{{\n{},\n{}\n}}", init_params, parse_params(chunks[count]));
|
||||||
vectors.push(params);
|
vectors.push(params);
|
||||||
count += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vectors.join(",\n")
|
vectors.join(",\n")
|
||||||
@@ -96,7 +88,7 @@ fn parse_params(input: &str) -> String {
|
|||||||
let key = iter.next().unwrap().split_whitespace().next().unwrap();
|
let key = iter.next().unwrap().split_whitespace().next().unwrap();
|
||||||
let val = iter.next().unwrap().split_whitespace().next().unwrap();
|
let val = iter.next().unwrap().split_whitespace().next().unwrap();
|
||||||
|
|
||||||
param = format!(" \"{}\": \"{}", key, val);
|
param = format!(" \"{key}\": \"{val}");
|
||||||
} else {
|
} else {
|
||||||
let s = line.trim().to_string();
|
let s = line.trim().to_string();
|
||||||
if s.contains('~') || s.contains('#') {
|
if s.contains('~') || s.contains('#') {
|
||||||
|
|||||||
+92
-107
@@ -1,20 +1,12 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
//
|
// Copyright (c) VexaHub and contributors.
|
||||||
// This source code is licensed under both the MIT license found in the
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
||||||
// of this source tree.
|
|
||||||
|
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::ops::Add;
|
|
||||||
|
|
||||||
use digest::core_api::BlockSizeUser;
|
use serde_json::Value;
|
||||||
use digest::OutputSizeUser;
|
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256};
|
|
||||||
use generic_array::ArrayLength;
|
|
||||||
use json::JsonValue;
|
|
||||||
|
|
||||||
use crate::tests::mock_rng::CycleRng;
|
use crate::tests::mock_rng::CycleRng;
|
||||||
use crate::tests::parser::*;
|
use crate::tests::parser::*;
|
||||||
@@ -40,7 +32,7 @@ struct VOPRFTestVectorParameters {
|
|||||||
output: Vec<Vec<u8>>,
|
output: Vec<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn populate_test_vectors(values: &JsonValue) -> VOPRFTestVectorParameters {
|
fn populate_test_vectors(values: &Value) -> VOPRFTestVectorParameters {
|
||||||
VOPRFTestVectorParameters {
|
VOPRFTestVectorParameters {
|
||||||
seed: decode(values, "Seed"),
|
seed: decode(values, "Seed"),
|
||||||
sksm: decode(values, "skSm"),
|
sksm: decode(values, "skSm"),
|
||||||
@@ -57,14 +49,14 @@ fn populate_test_vectors(values: &JsonValue) -> VOPRFTestVectorParameters {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode(values: &JsonValue, key: &str) -> Vec<u8> {
|
fn decode(values: &Value, key: &str) -> Vec<u8> {
|
||||||
values[key]
|
values[key]
|
||||||
.as_str()
|
.as_str()
|
||||||
.and_then(|s| hex::decode(s).ok())
|
.and_then(|s| hex::decode(s).ok())
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_vec(values: &JsonValue, key: &str) -> Vec<Vec<u8>> {
|
fn decode_vec(values: &Value, key: &str) -> Vec<Vec<u8>> {
|
||||||
let s = values[key].as_str().unwrap();
|
let s = values[key].as_str().unwrap();
|
||||||
let res = match s.contains(',') {
|
let res = match s.contains(',') {
|
||||||
true => Some(s.split(',').map(|x| hex::decode(x).unwrap()).collect()),
|
true => Some(s.split(',').map(|x| hex::decode(x).unwrap()).collect()),
|
||||||
@@ -76,8 +68,10 @@ fn decode_vec(values: &JsonValue, key: &str) -> Vec<Vec<u8>> {
|
|||||||
macro_rules! json_to_test_vectors {
|
macro_rules! json_to_test_vectors {
|
||||||
( $v:ident, $cs:expr, $mode:expr ) => {
|
( $v:ident, $cs:expr, $mode:expr ) => {
|
||||||
$v[$cs][$mode]
|
$v[$cs][$mode]
|
||||||
.members()
|
.as_array()
|
||||||
.map(|x| populate_test_vectors(&x))
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.map(populate_test_vectors)
|
||||||
.collect::<Vec<VOPRFTestVectorParameters>>()
|
.collect::<Vec<VOPRFTestVectorParameters>>()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -85,8 +79,10 @@ macro_rules! json_to_test_vectors {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_vectors() -> Result<()> {
|
fn test_vectors() -> Result<()> {
|
||||||
use p256::NistP256;
|
use p256::NistP256;
|
||||||
|
use p384::NistP384;
|
||||||
|
use p521::NistP521;
|
||||||
|
|
||||||
let rfc = json::parse(rfc_to_json(super::cfrg_vectors::VECTORS).as_str())
|
let rfc: Value = serde_json::from_str(rfc_to_json(super::cfrg_vectors::VECTORS).as_str())
|
||||||
.expect("Could not parse json");
|
.expect("Could not parse json");
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
@@ -95,7 +91,7 @@ fn test_vectors() -> Result<()> {
|
|||||||
|
|
||||||
let ristretto_oprf_tvs = json_to_test_vectors!(
|
let ristretto_oprf_tvs = json_to_test_vectors!(
|
||||||
rfc,
|
rfc,
|
||||||
String::from("ristretto255, SHA-512"),
|
String::from("ristretto255-SHA512"),
|
||||||
String::from("OPRF")
|
String::from("OPRF")
|
||||||
);
|
);
|
||||||
assert_ne!(ristretto_oprf_tvs.len(), 0);
|
assert_ne!(ristretto_oprf_tvs.len(), 0);
|
||||||
@@ -107,7 +103,7 @@ fn test_vectors() -> Result<()> {
|
|||||||
|
|
||||||
let ristretto_voprf_tvs = json_to_test_vectors!(
|
let ristretto_voprf_tvs = json_to_test_vectors!(
|
||||||
rfc,
|
rfc,
|
||||||
String::from("ristretto255, SHA-512"),
|
String::from("ristretto255-SHA512"),
|
||||||
String::from("VOPRF")
|
String::from("VOPRF")
|
||||||
);
|
);
|
||||||
assert_ne!(ristretto_voprf_tvs.len(), 0);
|
assert_ne!(ristretto_voprf_tvs.len(), 0);
|
||||||
@@ -119,7 +115,7 @@ fn test_vectors() -> Result<()> {
|
|||||||
|
|
||||||
let ristretto_poprf_tvs = json_to_test_vectors!(
|
let ristretto_poprf_tvs = json_to_test_vectors!(
|
||||||
rfc,
|
rfc,
|
||||||
String::from("ristretto255, SHA-512"),
|
String::from("ristretto255-SHA512"),
|
||||||
String::from("POPRF")
|
String::from("POPRF")
|
||||||
);
|
);
|
||||||
assert_ne!(ristretto_poprf_tvs.len(), 0);
|
assert_ne!(ristretto_poprf_tvs.len(), 0);
|
||||||
@@ -131,7 +127,7 @@ fn test_vectors() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let p256_oprf_tvs =
|
let p256_oprf_tvs =
|
||||||
json_to_test_vectors!(rfc, String::from("P-256, SHA-256"), String::from("OPRF"));
|
json_to_test_vectors!(rfc, String::from("P256-SHA256"), String::from("OPRF"));
|
||||||
assert_ne!(p256_oprf_tvs.len(), 0);
|
assert_ne!(p256_oprf_tvs.len(), 0);
|
||||||
test_oprf_seed_to_key::<NistP256>(&p256_oprf_tvs)?;
|
test_oprf_seed_to_key::<NistP256>(&p256_oprf_tvs)?;
|
||||||
test_oprf_blind::<NistP256>(&p256_oprf_tvs)?;
|
test_oprf_blind::<NistP256>(&p256_oprf_tvs)?;
|
||||||
@@ -140,7 +136,7 @@ fn test_vectors() -> Result<()> {
|
|||||||
test_oprf_evaluate::<NistP256>(&p256_oprf_tvs)?;
|
test_oprf_evaluate::<NistP256>(&p256_oprf_tvs)?;
|
||||||
|
|
||||||
let p256_voprf_tvs =
|
let p256_voprf_tvs =
|
||||||
json_to_test_vectors!(rfc, String::from("P-256, SHA-256"), String::from("VOPRF"));
|
json_to_test_vectors!(rfc, String::from("P256-SHA256"), String::from("VOPRF"));
|
||||||
assert_ne!(p256_voprf_tvs.len(), 0);
|
assert_ne!(p256_voprf_tvs.len(), 0);
|
||||||
test_voprf_seed_to_key::<NistP256>(&p256_voprf_tvs)?;
|
test_voprf_seed_to_key::<NistP256>(&p256_voprf_tvs)?;
|
||||||
test_voprf_blind::<NistP256>(&p256_voprf_tvs)?;
|
test_voprf_blind::<NistP256>(&p256_voprf_tvs)?;
|
||||||
@@ -149,7 +145,7 @@ fn test_vectors() -> Result<()> {
|
|||||||
test_voprf_evaluate::<NistP256>(&p256_voprf_tvs)?;
|
test_voprf_evaluate::<NistP256>(&p256_voprf_tvs)?;
|
||||||
|
|
||||||
let p256_poprf_tvs =
|
let p256_poprf_tvs =
|
||||||
json_to_test_vectors!(rfc, String::from("P-256, SHA-256"), String::from("POPRF"));
|
json_to_test_vectors!(rfc, String::from("P256-SHA256"), String::from("POPRF"));
|
||||||
assert_ne!(p256_poprf_tvs.len(), 0);
|
assert_ne!(p256_poprf_tvs.len(), 0);
|
||||||
test_poprf_seed_to_key::<NistP256>(&p256_poprf_tvs)?;
|
test_poprf_seed_to_key::<NistP256>(&p256_poprf_tvs)?;
|
||||||
test_poprf_blind::<NistP256>(&p256_poprf_tvs)?;
|
test_poprf_blind::<NistP256>(&p256_poprf_tvs)?;
|
||||||
@@ -157,14 +153,64 @@ fn test_vectors() -> Result<()> {
|
|||||||
test_poprf_finalize::<NistP256>(&p256_poprf_tvs)?;
|
test_poprf_finalize::<NistP256>(&p256_poprf_tvs)?;
|
||||||
test_poprf_evaluate::<NistP256>(&p256_poprf_tvs)?;
|
test_poprf_evaluate::<NistP256>(&p256_poprf_tvs)?;
|
||||||
|
|
||||||
|
let p384_oprf_tvs =
|
||||||
|
json_to_test_vectors!(rfc, String::from("P384-SHA384"), String::from("OPRF"));
|
||||||
|
assert_ne!(p384_oprf_tvs.len(), 0);
|
||||||
|
test_oprf_seed_to_key::<NistP384>(&p384_oprf_tvs)?;
|
||||||
|
test_oprf_blind::<NistP384>(&p384_oprf_tvs)?;
|
||||||
|
test_oprf_blind_evaluate::<NistP384>(&p384_oprf_tvs)?;
|
||||||
|
test_oprf_finalize::<NistP384>(&p384_oprf_tvs)?;
|
||||||
|
test_oprf_evaluate::<NistP384>(&p384_oprf_tvs)?;
|
||||||
|
|
||||||
|
let p384_voprf_tvs =
|
||||||
|
json_to_test_vectors!(rfc, String::from("P384-SHA384"), String::from("VOPRF"));
|
||||||
|
assert_ne!(p384_voprf_tvs.len(), 0);
|
||||||
|
test_voprf_seed_to_key::<NistP384>(&p384_voprf_tvs)?;
|
||||||
|
test_voprf_blind::<NistP384>(&p384_voprf_tvs)?;
|
||||||
|
test_voprf_blind_evaluate::<NistP384>(&p384_voprf_tvs)?;
|
||||||
|
test_voprf_finalize::<NistP384>(&p384_voprf_tvs)?;
|
||||||
|
test_voprf_evaluate::<NistP384>(&p384_voprf_tvs)?;
|
||||||
|
|
||||||
|
let p384_poprf_tvs =
|
||||||
|
json_to_test_vectors!(rfc, String::from("P384-SHA384"), String::from("POPRF"));
|
||||||
|
assert_ne!(p384_poprf_tvs.len(), 0);
|
||||||
|
test_poprf_seed_to_key::<NistP384>(&p384_poprf_tvs)?;
|
||||||
|
test_poprf_blind::<NistP384>(&p384_poprf_tvs)?;
|
||||||
|
test_poprf_blind_evaluate::<NistP384>(&p384_poprf_tvs)?;
|
||||||
|
test_poprf_finalize::<NistP384>(&p384_poprf_tvs)?;
|
||||||
|
test_poprf_evaluate::<NistP384>(&p384_poprf_tvs)?;
|
||||||
|
|
||||||
|
let p521_oprf_tvs =
|
||||||
|
json_to_test_vectors!(rfc, String::from("P521-SHA512"), String::from("OPRF"));
|
||||||
|
assert_ne!(p521_oprf_tvs.len(), 0);
|
||||||
|
test_oprf_seed_to_key::<NistP521>(&p521_oprf_tvs)?;
|
||||||
|
test_oprf_blind::<NistP521>(&p521_oprf_tvs)?;
|
||||||
|
test_oprf_blind_evaluate::<NistP521>(&p521_oprf_tvs)?;
|
||||||
|
test_oprf_finalize::<NistP521>(&p521_oprf_tvs)?;
|
||||||
|
test_oprf_evaluate::<NistP521>(&p521_oprf_tvs)?;
|
||||||
|
|
||||||
|
let p521_voprf_tvs =
|
||||||
|
json_to_test_vectors!(rfc, String::from("P521-SHA512"), String::from("VOPRF"));
|
||||||
|
assert_ne!(p521_voprf_tvs.len(), 0);
|
||||||
|
test_voprf_seed_to_key::<NistP521>(&p521_voprf_tvs)?;
|
||||||
|
test_voprf_blind::<NistP521>(&p521_voprf_tvs)?;
|
||||||
|
test_voprf_blind_evaluate::<NistP521>(&p521_voprf_tvs)?;
|
||||||
|
test_voprf_finalize::<NistP521>(&p521_voprf_tvs)?;
|
||||||
|
test_voprf_evaluate::<NistP521>(&p521_voprf_tvs)?;
|
||||||
|
|
||||||
|
let p521_poprf_tvs =
|
||||||
|
json_to_test_vectors!(rfc, String::from("P521-SHA512"), String::from("POPRF"));
|
||||||
|
assert_ne!(p521_poprf_tvs.len(), 0);
|
||||||
|
test_poprf_seed_to_key::<NistP521>(&p521_poprf_tvs)?;
|
||||||
|
test_poprf_blind::<NistP521>(&p521_poprf_tvs)?;
|
||||||
|
test_poprf_blind_evaluate::<NistP521>(&p521_poprf_tvs)?;
|
||||||
|
test_poprf_finalize::<NistP521>(&p521_poprf_tvs)?;
|
||||||
|
test_poprf_evaluate::<NistP521>(&p521_poprf_tvs)?;
|
||||||
|
|
||||||
Ok(())
|
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 {
|
for parameters in tvs {
|
||||||
let server = OprfServer::<CS>::new_from_seed(¶meters.seed, ¶meters.key_info)?;
|
let server = OprfServer::<CS>::new_from_seed(¶meters.seed, ¶meters.key_info)?;
|
||||||
|
|
||||||
@@ -176,11 +222,7 @@ where
|
|||||||
Ok(())
|
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 {
|
for parameters in tvs {
|
||||||
let server = VoprfServer::<CS>::new_from_seed(¶meters.seed, ¶meters.key_info)?;
|
let server = VoprfServer::<CS>::new_from_seed(¶meters.seed, ¶meters.key_info)?;
|
||||||
|
|
||||||
@@ -196,11 +238,7 @@ where
|
|||||||
Ok(())
|
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 {
|
for parameters in tvs {
|
||||||
let server = PoprfServer::<CS>::new_from_seed(¶meters.seed, ¶meters.key_info)?;
|
let server = PoprfServer::<CS>::new_from_seed(¶meters.seed, ¶meters.key_info)?;
|
||||||
|
|
||||||
@@ -217,11 +255,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tests input -> blind, blinded_element
|
// 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 parameters in tvs {
|
||||||
for i in 0..parameters.input.len() {
|
for i in 0..parameters.input.len() {
|
||||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||||
@@ -242,11 +276,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tests input -> blind, blinded_element
|
// 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 parameters in tvs {
|
||||||
for i in 0..parameters.input.len() {
|
for i in 0..parameters.input.len() {
|
||||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||||
@@ -267,11 +297,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tests input -> blind, blinded_element
|
// 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 parameters in tvs {
|
||||||
for i in 0..parameters.input.len() {
|
for i in 0..parameters.input.len() {
|
||||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||||
@@ -292,11 +318,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tests sksm, blinded_element -> evaluation_element
|
// 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 parameters in tvs {
|
||||||
for i in 0..parameters.input.len() {
|
for i in 0..parameters.input.len() {
|
||||||
let server = OprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
let server = OprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||||
@@ -313,13 +335,7 @@ where
|
|||||||
Ok(())
|
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 {
|
for parameters in tvs {
|
||||||
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
|
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
|
||||||
let server = VoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
let server = VoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||||
@@ -340,18 +356,12 @@ where
|
|||||||
assert_eq!(¶meter, &message.serialize().as_slice());
|
assert_eq!(¶meter, &message.serialize().as_slice());
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(¶meters.proof, &proof.serialize().as_slice());
|
assert_eq!(¶meters.proof, &proof.serialize().to_vec());
|
||||||
}
|
}
|
||||||
Ok(())
|
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 {
|
for parameters in tvs {
|
||||||
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
|
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
|
||||||
let server = PoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
let server = PoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||||
@@ -372,8 +382,7 @@ where
|
|||||||
blinded_elements.iter(),
|
blinded_elements.iter(),
|
||||||
&prepared_evaluation_elements,
|
&prepared_evaluation_elements,
|
||||||
&prepared_tweak,
|
&prepared_tweak,
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let messages: Vec<_> = messages.collect();
|
let messages: Vec<_> = messages.collect();
|
||||||
|
|
||||||
@@ -381,17 +390,13 @@ where
|
|||||||
assert_eq!(¶meter, &message.serialize().as_slice());
|
assert_eq!(¶meter, &message.serialize().as_slice());
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(¶meters.proof, &proof.serialize().as_slice());
|
assert_eq!(¶meters.proof, &proof.serialize().to_vec());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests input, blind, evaluation_element -> output
|
// 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 parameters in tvs {
|
||||||
for i in 0..parameters.input.len() {
|
for i in 0..parameters.input.len() {
|
||||||
let client =
|
let client =
|
||||||
@@ -408,11 +413,7 @@ where
|
|||||||
Ok(())
|
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 {
|
for parameters in tvs {
|
||||||
let mut clients = vec![];
|
let mut clients = vec![];
|
||||||
for i in 0..parameters.input.len() {
|
for i in 0..parameters.input.len() {
|
||||||
@@ -447,11 +448,7 @@ where
|
|||||||
Ok(())
|
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 {
|
for parameters in tvs {
|
||||||
let mut clients = vec![];
|
let mut clients = vec![];
|
||||||
for i in 0..parameters.input.len() {
|
for i in 0..parameters.input.len() {
|
||||||
@@ -485,11 +482,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tests input, sksm -> output
|
// 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 parameters in tvs {
|
||||||
for i in 0..parameters.input.len() {
|
for i in 0..parameters.input.len() {
|
||||||
let server = OprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
let server = OprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||||
@@ -502,11 +495,7 @@ where
|
|||||||
Ok(())
|
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 parameters in tvs {
|
||||||
for i in 0..parameters.input.len() {
|
for i in 0..parameters.input.len() {
|
||||||
let server = VoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
let server = VoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||||
@@ -519,11 +508,7 @@ where
|
|||||||
Ok(())
|
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 parameters in tvs {
|
||||||
for i in 0..parameters.input.len() {
|
for i in 0..parameters.input.len() {
|
||||||
let server = PoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
let server = PoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||||
|
|||||||
+66
-217
@@ -1,9 +1,6 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
//
|
// Copyright (c) VexaHub and contributors.
|
||||||
// This source code is licensed under both the MIT license found in the
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
||||||
// of this source tree.
|
|
||||||
|
|
||||||
//! Contains the main VOPRF API
|
//! Contains the main VOPRF API
|
||||||
|
|
||||||
@@ -12,16 +9,14 @@ use alloc::vec::Vec;
|
|||||||
use core::iter::{self, Map, Repeat, Zip};
|
use core::iter::{self, Map, Repeat, Zip};
|
||||||
|
|
||||||
use derive_where::derive_where;
|
use derive_where::derive_where;
|
||||||
use digest::core_api::BlockSizeUser;
|
use digest::Output;
|
||||||
use digest::{Digest, Output, OutputSizeUser};
|
use hybrid_array::Array;
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256};
|
use rand_core::{TryCryptoRng, TryRng};
|
||||||
use generic_array::GenericArray;
|
|
||||||
use rand_core::{CryptoRng, RngCore};
|
|
||||||
|
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2,
|
BlindedElement, EvaluationElement, FinalizeAfterUnblindResult, Mode, PreparedEvaluationElement,
|
||||||
server_evaluate_hash_input, verify_proof, BlindedElement, EvaluationElement, Mode,
|
Proof, derive_keypair, deterministic_blind_unchecked, finalize_after_unblind, generate_proof,
|
||||||
PreparedEvaluationElement, Proof, STR_FINALIZE,
|
hash_to_group, server_evaluate_hash_input, verify_proof,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use crate::serialization::serde::{Element, Scalar};
|
use crate::serialization::serde::{Element, Scalar};
|
||||||
@@ -39,13 +34,9 @@ use crate::{CipherSuite, Error, Group, Result};
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
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>"))]
|
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||||
pub(crate) blind: <CS::Group as Group>::Scalar,
|
pub(crate) blind: <CS::Group as Group>::Scalar,
|
||||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||||
@@ -59,13 +50,9 @@ where
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
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>"))]
|
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||||
pub(crate) sk: <CS::Group as Group>::Scalar,
|
pub(crate) sk: <CS::Group as Group>::Scalar,
|
||||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||||
@@ -77,21 +64,17 @@ where
|
|||||||
// =================== //
|
// =================== //
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
|
|
||||||
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
|
/// Computes the first step for the multiplicative blinding version of
|
||||||
/// DH-OPRF.
|
/// DH-OPRF.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`].
|
/// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`].
|
||||||
pub fn blind<R: RngCore + CryptoRng>(
|
pub fn blind<R: TryRng + TryCryptoRng>(
|
||||||
input: &[u8],
|
input: &[u8],
|
||||||
blinding_factor_rng: &mut R,
|
blinding_factor_rng: &mut R,
|
||||||
) -> Result<VoprfClientBlindResult<CS>> {
|
) -> 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)
|
Self::deterministic_blind_unchecked_inner(input, blind)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +143,7 @@ where
|
|||||||
///
|
///
|
||||||
/// The resulting messages can each fail individually with [`Error::Input`]
|
/// The resulting messages can each fail individually with [`Error::Input`]
|
||||||
/// if the `input` is empty or longer then [`u16::MAX`].
|
/// if the `input` is empty or longer then [`u16::MAX`].
|
||||||
pub fn batch_finalize<'a, I: 'a, II, IC, IM>(
|
pub fn batch_finalize<'a, I, II, IC, IM>(
|
||||||
inputs: &'a II,
|
inputs: &'a II,
|
||||||
clients: &'a IC,
|
clients: &'a IC,
|
||||||
messages: &'a IM,
|
messages: &'a IM,
|
||||||
@@ -169,7 +152,7 @@ where
|
|||||||
) -> Result<VoprfClientBatchFinalizeResult<'a, CS, I, II, IC, IM>>
|
) -> Result<VoprfClientBatchFinalizeResult<'a, CS, I, II, IC, IM>>
|
||||||
where
|
where
|
||||||
CS: 'a,
|
CS: 'a,
|
||||||
I: AsRef<[u8]>,
|
I: 'a + AsRef<[u8]>,
|
||||||
&'a II: 'a + IntoIterator<Item = I>,
|
&'a II: 'a + IntoIterator<Item = I>,
|
||||||
<&'a II as IntoIterator>::IntoIter: ExactSizeIterator,
|
<&'a II as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||||
&'a IC: 'a + IntoIterator<Item = &'a VoprfClient<CS>>,
|
&'a IC: 'a + IntoIterator<Item = &'a VoprfClient<CS>>,
|
||||||
@@ -196,25 +179,21 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only used for test functions
|
/// Only used for test functions
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn get_blind(&self) -> <CS::Group as Group>::Scalar {
|
pub fn get_blind(&self) -> <CS::Group as Group>::Scalar {
|
||||||
self.blind
|
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
|
/// Produces a new instance of a [VoprfServer] using a supplied RNG
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// [`Error::Protocol`] if the protocol fails and can't be completed.
|
/// [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self> {
|
pub fn new<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self> {
|
||||||
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
|
let mut seed = Array::<_, <CS::Group as Group>::ScalarLen>::default();
|
||||||
rng.fill_bytes(&mut seed);
|
rng.try_fill_bytes(&mut seed).map_err(|_| Error::Protocol)?;
|
||||||
// This can't fail as the hash output is type constrained.
|
// This can't fail as the hash output is type constrained.
|
||||||
Self::new_from_seed(&seed, &[])
|
Self::new_from_seed(&seed, &[])
|
||||||
}
|
}
|
||||||
@@ -245,7 +224,7 @@ where
|
|||||||
Ok(Self { sk, pk })
|
Ok(Self { sk, pk })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only used for tests
|
/// Only used for tests
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
|
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
|
||||||
self.sk
|
self.sk
|
||||||
@@ -254,7 +233,7 @@ where
|
|||||||
/// Computes the second step for the multiplicative blinding version of
|
/// Computes the second step for the multiplicative blinding version of
|
||||||
/// DH-OPRF. This message is sent from the server (who holds the OPRF key)
|
/// DH-OPRF. This message is sent from the server (who holds the OPRF key)
|
||||||
/// to the client.
|
/// to the client.
|
||||||
pub fn blind_evaluate<R: RngCore + CryptoRng>(
|
pub fn blind_evaluate<R: TryRng + TryCryptoRng>(
|
||||||
&self,
|
&self,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
blinded_element: &BlindedElement<CS>,
|
blinded_element: &BlindedElement<CS>,
|
||||||
@@ -287,7 +266,7 @@ where
|
|||||||
/// [`Error::Batch`] if the number of `blinded_elements` and
|
/// [`Error::Batch`] if the number of `blinded_elements` and
|
||||||
/// `evaluation_elements` don't match or is longer then [`u16::MAX`]
|
/// `evaluation_elements` don't match or is longer then [`u16::MAX`]
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub fn batch_blind_evaluate<'a, R: RngCore + CryptoRng, I>(
|
pub fn batch_blind_evaluate<'a, R: TryRng + TryCryptoRng, I>(
|
||||||
&self,
|
&self,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
blinded_elements: &'a I,
|
blinded_elements: &'a I,
|
||||||
@@ -338,7 +317,7 @@ where
|
|||||||
pub fn batch_blind_evaluate_finish<
|
pub fn batch_blind_evaluate_finish<
|
||||||
'a,
|
'a,
|
||||||
'b,
|
'b,
|
||||||
R: RngCore + CryptoRng,
|
R: TryRng + TryCryptoRng,
|
||||||
IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator,
|
IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator,
|
||||||
IE,
|
IE,
|
||||||
>(
|
>(
|
||||||
@@ -359,14 +338,14 @@ where
|
|||||||
g,
|
g,
|
||||||
self.pk,
|
self.pk,
|
||||||
blinded_elements.map(|element| element.0),
|
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,
|
Mode::Voprf,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let messages = evaluation_elements.into_iter().map(<fn(
|
let messages = evaluation_elements.into_iter().map(<fn(
|
||||||
&PreparedEvaluationElement<CS>,
|
&PreparedEvaluationElement<CS>,
|
||||||
) -> EvaluationElement<CS>>::from(
|
) -> EvaluationElement<CS>>::from(
|
||||||
|element| EvaluationElement(element.0 .0),
|
|element| EvaluationElement(element.0.0),
|
||||||
));
|
));
|
||||||
|
|
||||||
Ok(VoprfServerBatchEvaluateFinishResult { messages, proof })
|
Ok(VoprfServerBatchEvaluateFinishResult { messages, proof })
|
||||||
@@ -401,11 +380,7 @@ where
|
|||||||
|
|
||||||
/// Contains the fields that are returned by a verifiable client blind
|
/// Contains the fields that are returned by a verifiable client blind
|
||||||
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
#[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
|
/// The state to be persisted on the client
|
||||||
pub state: VoprfClient<CS>,
|
pub state: VoprfClient<CS>,
|
||||||
/// The message to send to the server
|
/// The message to send to the server
|
||||||
@@ -422,11 +397,7 @@ pub type VoprfClientBatchFinalizeResult<'a, C, I, II, IC, IM> = FinalizeAfterUnb
|
|||||||
|
|
||||||
/// Contains the fields that are returned by a verifiable server evaluate
|
/// Contains the fields that are returned by a verifiable server evaluate
|
||||||
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
#[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
|
/// The message to send to the client
|
||||||
pub message: EvaluationElement<CS>,
|
pub message: EvaluationElement<CS>,
|
||||||
/// The proof for the client to verify
|
/// The proof for the client to verify
|
||||||
@@ -436,11 +407,7 @@ where
|
|||||||
/// Contains the fields that are returned by a verifiable server batch evaluate
|
/// 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)]
|
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||||
#[cfg(feature = "alloc")]
|
#[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
|
/// The messages to send to the client
|
||||||
pub messages: Vec<EvaluationElement<CS>>,
|
pub messages: Vec<EvaluationElement<CS>>,
|
||||||
/// The proof for the client to verify
|
/// The proof for the client to verify
|
||||||
@@ -471,8 +438,6 @@ pub type VoprfServerBatchEvaluateFinishedMessages<'a, CS, I> = Map<
|
|||||||
#[derive_where(Debug; <&'a I as IntoIterator>::IntoIter, <CS::Group as Group>::Scalar)]
|
#[derive_where(Debug; <&'a I as IntoIterator>::IntoIter, <CS::Group as Group>::Scalar)]
|
||||||
pub struct VoprfServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I>
|
pub struct VoprfServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I>
|
||||||
where
|
where
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
&'a I: IntoIterator<Item = &'a PreparedEvaluationElement<CS>>,
|
&'a I: IntoIterator<Item = &'a PreparedEvaluationElement<CS>>,
|
||||||
{
|
{
|
||||||
/// The [`EvaluationElement`]s to send to the client
|
/// The [`EvaluationElement`]s to send to the client
|
||||||
@@ -510,8 +475,6 @@ fn verifiable_unblind<'a, CS: 'a + CipherSuite, IC, IM>(
|
|||||||
proof: &Proof<CS>,
|
proof: &Proof<CS>,
|
||||||
) -> Result<VoprfUnblindResult<'a, CS, IC, IM>>
|
) -> Result<VoprfUnblindResult<'a, CS, IC, IM>>
|
||||||
where
|
where
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
&'a IC: 'a + IntoIterator<Item = &'a VoprfClient<CS>>,
|
&'a IC: 'a + IntoIterator<Item = &'a VoprfClient<CS>>,
|
||||||
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
|
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||||
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
|
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
|
||||||
@@ -536,45 +499,10 @@ where
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(blinds
|
Ok(blinds
|
||||||
.zip(messages.into_iter())
|
.zip(messages)
|
||||||
.map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
|
.map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
|
||||||
}
|
}
|
||||||
|
|
||||||
type FinalizeAfterUnblindResult<'a, C, I, IE> = Map<
|
|
||||||
IE,
|
|
||||||
fn((I, <<C as CipherSuite>::Group as Group>::Elem)) -> Result<Output<<C as CipherSuite>::Hash>>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
/// Returned values can only fail with [`Error::Input`].
|
|
||||||
fn finalize_after_unblind<
|
|
||||||
'a,
|
|
||||||
CS: CipherSuite,
|
|
||||||
I: AsRef<[u8]>,
|
|
||||||
IE: 'a + Iterator<Item = (I, <CS::Group as Group>::Elem)>,
|
|
||||||
>(
|
|
||||||
inputs_and_unblinded_elements: IE,
|
|
||||||
) -> FinalizeAfterUnblindResult<'a, CS, I, IE>
|
|
||||||
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();
|
|
||||||
|
|
||||||
// hashInput = I2OSP(len(input), 2) || input ||
|
|
||||||
// I2OSP(len(unblindedElement), 2) || unblindedElement ||
|
|
||||||
// "Finalize"
|
|
||||||
// return Hash(hashInput)
|
|
||||||
Ok(CS::Hash::new()
|
|
||||||
.chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?)
|
|
||||||
.chain_update(input.as_ref())
|
|
||||||
.chain_update(elem_len)
|
|
||||||
.chain_update(CS::Group::serialize_elem(unblinded_element))
|
|
||||||
.chain_update(STR_FINALIZE)
|
|
||||||
.finalize())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////
|
///////////
|
||||||
// Tests //
|
// Tests //
|
||||||
// ===== //
|
// ===== //
|
||||||
@@ -582,47 +510,20 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use core::ops::Add;
|
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
|
|
||||||
use ::alloc::vec;
|
use ::alloc::vec;
|
||||||
use ::alloc::vec::Vec;
|
use ::alloc::vec::Vec;
|
||||||
use generic_array::sequence::Concat;
|
use rand::rngs::SysRng;
|
||||||
use generic_array::typenum::Sum;
|
|
||||||
use generic_array::ArrayLength;
|
|
||||||
use rand::rngs::OsRng;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::{create_context_string, STR_HASH_TO_GROUP};
|
|
||||||
use crate::Group;
|
use crate::Group;
|
||||||
|
use crate::common::{Dst, STR_HASH_TO_GROUP};
|
||||||
|
use crate::tests::helpers::prf;
|
||||||
|
|
||||||
fn prf<CS: CipherSuite>(
|
fn verifiable_retrieval<CS: CipherSuite>() {
|
||||||
input: &[u8],
|
|
||||||
key: <CS::Group as Group>::Scalar,
|
|
||||||
mode: Mode,
|
|
||||||
) -> Output<CS::Hash>
|
|
||||||
where
|
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
{
|
|
||||||
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
|
|
||||||
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst).unwrap();
|
|
||||||
|
|
||||||
let res = point * &key;
|
|
||||||
|
|
||||||
finalize_after_unblind::<CS, _, _>(iter::once((input, res)))
|
|
||||||
.next()
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verifiable_retrieval<CS: CipherSuite>()
|
|
||||||
where
|
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
{
|
|
||||||
let input = b"input";
|
let input = b"input";
|
||||||
let mut rng = OsRng;
|
let mut rng = SysRng;
|
||||||
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||||
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
|
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
|
||||||
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
|
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
|
||||||
@@ -639,19 +540,15 @@ mod tests {
|
|||||||
assert_eq!(client_finalize_result, res2);
|
assert_eq!(client_finalize_result, res2);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verifiable_batch_retrieval<CS: CipherSuite>()
|
fn verifiable_batch_retrieval<CS: CipherSuite>() {
|
||||||
where
|
let mut rng = SysRng;
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
{
|
|
||||||
let mut rng = OsRng;
|
|
||||||
let mut inputs = vec![];
|
let mut inputs = vec![];
|
||||||
let mut client_states = vec![];
|
let mut client_states = vec![];
|
||||||
let mut client_messages = vec![];
|
let mut client_messages = vec![];
|
||||||
let num_iterations = 10;
|
let num_iterations = 10;
|
||||||
for _ in 0..num_iterations {
|
for _ in 0..num_iterations {
|
||||||
let mut input = [0u8; 32];
|
let mut input = [0u8; 32];
|
||||||
rng.fill_bytes(&mut input);
|
rng.try_fill_bytes(&mut input).unwrap();
|
||||||
let client_blind_result = VoprfClient::<CS>::blind(&input, &mut rng).unwrap();
|
let client_blind_result = VoprfClient::<CS>::blind(&input, &mut rng).unwrap();
|
||||||
inputs.push(input);
|
inputs.push(input);
|
||||||
client_states.push(client_blind_result.state);
|
client_states.push(client_blind_result.state);
|
||||||
@@ -687,19 +584,15 @@ mod tests {
|
|||||||
assert_eq!(client_finalize_result, res2);
|
assert_eq!(client_finalize_result, res2);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verifiable_batch_bad_public_key<CS: CipherSuite>()
|
fn verifiable_batch_bad_public_key<CS: CipherSuite>() {
|
||||||
where
|
let mut rng = SysRng;
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
{
|
|
||||||
let mut rng = OsRng;
|
|
||||||
let mut inputs = vec![];
|
let mut inputs = vec![];
|
||||||
let mut client_states = vec![];
|
let mut client_states = vec![];
|
||||||
let mut client_messages = vec![];
|
let mut client_messages = vec![];
|
||||||
let num_iterations = 10;
|
let num_iterations = 10;
|
||||||
for _ in 0..num_iterations {
|
for _ in 0..num_iterations {
|
||||||
let mut input = [0u8; 32];
|
let mut input = [0u8; 32];
|
||||||
rng.fill_bytes(&mut input);
|
rng.try_fill_bytes(&mut input).unwrap();
|
||||||
let client_blind_result = VoprfClient::<CS>::blind(&input, &mut rng).unwrap();
|
let client_blind_result = VoprfClient::<CS>::blind(&input, &mut rng).unwrap();
|
||||||
inputs.push(input);
|
inputs.push(input);
|
||||||
client_states.push(client_blind_result.state);
|
client_states.push(client_blind_result.state);
|
||||||
@@ -718,31 +611,25 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let messages: Vec<_> = messages.collect();
|
let messages: Vec<_> = messages.collect();
|
||||||
let wrong_pk = {
|
let wrong_pk = {
|
||||||
let dst = GenericArray::from(STR_HASH_TO_GROUP)
|
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, Mode::Oprf);
|
||||||
.concat(create_context_string::<CS>(Mode::Oprf));
|
|
||||||
// Choose a group element that is unlikely to be the right public key
|
// Choose a group element that is unlikely to be the right public key
|
||||||
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst).unwrap()
|
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
|
||||||
};
|
};
|
||||||
let client_finalize_result =
|
let client_finalize_result =
|
||||||
VoprfClient::batch_finalize(&inputs, &client_states, &messages, &proof, wrong_pk);
|
VoprfClient::batch_finalize(&inputs, &client_states, &messages, &proof, wrong_pk);
|
||||||
assert!(client_finalize_result.is_err());
|
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 input = b"input";
|
||||||
let mut rng = OsRng;
|
let mut rng = SysRng;
|
||||||
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||||
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
|
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
|
||||||
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
|
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
|
||||||
let wrong_pk = {
|
let wrong_pk = {
|
||||||
let dst = GenericArray::from(STR_HASH_TO_GROUP)
|
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, Mode::Oprf);
|
||||||
.concat(create_context_string::<CS>(Mode::Oprf));
|
|
||||||
// Choose a group element that is unlikely to be the right public key
|
// Choose a group element that is unlikely to be the right public key
|
||||||
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst).unwrap()
|
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
|
||||||
};
|
};
|
||||||
let client_finalize_result = client_blind_result.state.finalize(
|
let client_finalize_result = client_blind_result.state.finalize(
|
||||||
input,
|
input,
|
||||||
@@ -753,13 +640,9 @@ mod tests {
|
|||||||
assert!(client_finalize_result.is_err());
|
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 input = b"input";
|
||||||
let mut rng = OsRng;
|
let mut rng = SysRng;
|
||||||
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||||
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
|
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
|
||||||
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
|
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
|
||||||
@@ -783,18 +666,12 @@ mod tests {
|
|||||||
// inputs
|
// inputs
|
||||||
let wrong_input = b"wrong input";
|
let wrong_input = b"wrong input";
|
||||||
let server_evaluate = server.evaluate(wrong_input).unwrap();
|
let server_evaluate = server.evaluate(wrong_input).unwrap();
|
||||||
assert!(client_finalize != server_evaluate);
|
assert_ne!(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 input = b"input";
|
||||||
let mut rng = OsRng;
|
let mut rng = SysRng;
|
||||||
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||||
|
|
||||||
let mut state = client_blind_result.state;
|
let mut state = client_blind_result.state;
|
||||||
@@ -806,17 +683,9 @@ mod tests {
|
|||||||
assert!(message.serialize().iter().all(|&x| x == 0));
|
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 input = b"input";
|
||||||
let mut rng = OsRng;
|
let mut rng = SysRng;
|
||||||
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||||
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
|
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
|
||||||
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
|
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
|
||||||
@@ -834,33 +703,13 @@ mod tests {
|
|||||||
assert!(proof.serialize().iter().all(|&x| x == 0));
|
assert!(proof.serialize().iter().all(|&x| x == 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
crate::tests::test_all_curves!(
|
||||||
fn test_functionality() -> Result<()> {
|
verifiable_retrieval,
|
||||||
use p256::NistP256;
|
verifiable_batch_retrieval,
|
||||||
|
verifiable_bad_public_key,
|
||||||
#[cfg(feature = "ristretto255")]
|
verifiable_batch_bad_public_key,
|
||||||
{
|
verifiable_server_evaluate,
|
||||||
use crate::Ristretto255;
|
zeroize_voprf_client,
|
||||||
|
zeroize_voprf_server,
|
||||||
verifiable_retrieval::<Ristretto255>();
|
);
|
||||||
verifiable_batch_retrieval::<Ristretto255>();
|
|
||||||
verifiable_bad_public_key::<Ristretto255>();
|
|
||||||
verifiable_batch_bad_public_key::<Ristretto255>();
|
|
||||||
verifiable_server_evaluate::<Ristretto255>();
|
|
||||||
|
|
||||||
zeroize_voprf_client::<Ristretto255>();
|
|
||||||
zeroize_voprf_server::<Ristretto255>();
|
|
||||||
}
|
|
||||||
|
|
||||||
verifiable_retrieval::<NistP256>();
|
|
||||||
verifiable_batch_retrieval::<NistP256>();
|
|
||||||
verifiable_bad_public_key::<NistP256>();
|
|
||||||
verifiable_batch_bad_public_key::<NistP256>();
|
|
||||||
verifiable_server_evaluate::<NistP256>();
|
|
||||||
|
|
||||||
zeroize_voprf_client::<NistP256>();
|
|
||||||
zeroize_voprf_server::<NistP256>();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user