Compare commits
84
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a7dc184ca | ||
|
|
f5b7e689e7 | ||
|
|
a22d46fd96 | ||
|
|
f23cdfab2d | ||
|
|
256ab7bc52 | ||
|
|
eb55e9f5b5 | ||
|
|
e944f9db3b | ||
|
|
f3f4fef0e9 | ||
|
|
23aa7813e7 | ||
|
|
0473d9db68 | ||
|
|
f0531f0812 | ||
|
|
c93884aca3 | ||
|
|
40769f7eca | ||
|
|
1b67086028 | ||
|
|
68cc7d3709 | ||
|
|
59e3fedb21 | ||
|
|
8da56845b8 | ||
|
|
c0162ec8d9 | ||
|
|
ee91c9776c | ||
|
|
0fdfdfdaee | ||
|
|
eafa134c94 | ||
|
|
209b957ae4 | ||
|
|
f79ebf9844 | ||
|
|
20a35da7ba | ||
|
|
4bd2cf466e | ||
|
|
829c6add0f | ||
|
|
8b895cc631 | ||
|
|
83eb78b232 | ||
|
|
5badeff8d2 | ||
|
|
40d81294db | ||
|
|
8363d26f6f | ||
|
|
5bce3e3206 | ||
|
|
2787151e1d | ||
|
|
0409db6f40 | ||
|
|
74eaebe446 | ||
|
|
c8de51672b | ||
|
|
daa8dc048f | ||
|
|
2a351ceb4d | ||
|
|
8f60a10b8d | ||
|
|
1691125b09 | ||
|
|
6913b5deaa | ||
|
|
2dc6a8b2c2 | ||
|
|
f670733165 | ||
|
|
6e16a99a87 | ||
|
|
4646fe4ad0 | ||
|
|
f26f5d2c57 | ||
|
|
dfa88efeca | ||
|
|
0b3544fc79 | ||
|
|
1a61401272 | ||
|
|
a366a14125 | ||
|
|
8277383fc3 | ||
|
|
1af4f470c3 | ||
|
|
f8e0600b22 | ||
|
|
9eee936140 | ||
|
|
b59b359aa3 | ||
|
|
b01b8ed409 | ||
|
|
16e072dcd4 | ||
|
|
652fd1d1d0 | ||
|
|
e7675437e6 | ||
|
|
d316ce4c8d | ||
|
|
1c5e965446 | ||
|
|
55ef981a3f | ||
|
|
b2f6d5eac8 | ||
|
|
5228474f05 | ||
|
|
6669a0c4e6 | ||
|
|
140f9e063d | ||
|
|
4700d4b725 | ||
|
|
b1b315f23c | ||
|
|
7613610859 | ||
|
|
d9dc5d0a13 | ||
|
|
1b3837a789 | ||
|
|
093a15f597 | ||
|
|
14830f1436 | ||
|
|
8457e8b900 | ||
|
|
de91fafdb3 | ||
|
|
5ac52388ff | ||
|
|
fab7528a69 | ||
|
|
c93600498e | ||
|
|
6fb4cad59c | ||
|
|
2d8780476a | ||
|
|
a3db6cd9d2 | ||
|
|
a1ab892bdb | ||
|
|
ad04f224af | ||
|
|
7be67b26de |
@@ -0,0 +1,7 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
//
|
||||
// This source code is dual-licensed under either the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
@@ -0,0 +1,12 @@
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
- package-ecosystem: cargo
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
+90
-25
@@ -4,27 +4,50 @@ on:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, repoened, synchronize]
|
||||
types: [opened, reopened, synchronize]
|
||||
|
||||
jobs:
|
||||
cargo-audit:
|
||||
name: Audit
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Cache cargo-audit
|
||||
uses: actions/cache@v5
|
||||
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@v6
|
||||
|
||||
- name: Run cargo audit
|
||||
run: cargo audit -D warnings
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
backend_feature:
|
||||
- u64_backend
|
||||
- u32_backend
|
||||
- p256,u64_backend
|
||||
- --features ristretto255-ciphersuite
|
||||
-
|
||||
frontend_feature:
|
||||
- serialize
|
||||
-
|
||||
- --features danger
|
||||
- --features serde
|
||||
toolchain:
|
||||
- stable
|
||||
- 1.51.0
|
||||
- 1.85.0
|
||||
name: test
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install ${{ matrix.toolchain }} toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
@@ -32,19 +55,30 @@ jobs:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Run cargo test
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --no-default-features --features ${{ matrix.backend_feature }}
|
||||
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 --features ${{ matrix.frontend_feature }},std --features ${{ matrix.backend_feature }}
|
||||
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 }}
|
||||
@@ -58,16 +92,17 @@ jobs:
|
||||
# for any no_std target
|
||||
- thumbv6m-none-eabi
|
||||
backend_feature:
|
||||
- u64_backend
|
||||
- u32_backend
|
||||
- p256,u64_backend
|
||||
-
|
||||
- --features ristretto255-ciphersuite
|
||||
frontend_feature:
|
||||
- serialize
|
||||
-
|
||||
- --features danger
|
||||
- --features serde
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: hecrj/setup-rust-action@v1
|
||||
- uses: actions/checkout@v6
|
||||
- uses: hecrj/setup-rust-action@v2
|
||||
- run: rustup target add ${{ matrix.target }}
|
||||
- run: cargo build --verbose --target=${{ matrix.target }} --no-default-features --features ${{ matrix.frontend_feature }} --features ${{ matrix.backend_feature }}
|
||||
- run: cargo build --verbose --target=${{ matrix.target }} --no-default-features ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }}
|
||||
|
||||
|
||||
clippy:
|
||||
@@ -75,7 +110,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install stable toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
@@ -83,32 +118,62 @@ jobs:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
components: clippy
|
||||
|
||||
- name: Run cargo clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
||||
args: --all-features --all-targets -- -D warnings
|
||||
|
||||
- name: Run cargo doc
|
||||
uses: actions-rs/cargo@v1
|
||||
env:
|
||||
RUSTDOCFLAGS: -D warnings
|
||||
with:
|
||||
command: doc
|
||||
args: --no-deps --document-private-items --features danger,std
|
||||
|
||||
|
||||
format:
|
||||
rustfmt:
|
||||
name: cargo fmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install stable toolchain
|
||||
- name: Install nightly toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
toolchain: nightly
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
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@v5
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/.crates.toml
|
||||
~/.cargo/.crates2.json
|
||||
~/.cargo/bin/taplo
|
||||
key: taplo
|
||||
|
||||
- name: Install Taplo
|
||||
run: cargo install taplo-cli --locked
|
||||
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Run Taplo
|
||||
run: taplo fmt --check
|
||||
|
||||
@@ -10,9 +10,10 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
rust: [stable]
|
||||
|
||||
steps:
|
||||
- uses: hecrj/setup-rust-action@v1
|
||||
- uses: hecrj/setup-rust-action@v2
|
||||
with:
|
||||
rust-version: ${{ matrix.rust }}
|
||||
- uses: actions/checkout@master
|
||||
|
||||
@@ -1,5 +1,71 @@
|
||||
# Changelog
|
||||
|
||||
## 0.6.0-pre.1 (April 6, 2026)
|
||||
* MSRV bumped to 1.85
|
||||
* Updated rand_core dependency to 0.9
|
||||
* Updated rand dependency to 0.9
|
||||
* Updated subtle dependency to 2.6
|
||||
* Fixed docs issue
|
||||
|
||||
## 0.6.0-pre.0 (November 8, 2025)
|
||||
* MSRV bumped to 1.83
|
||||
* Updated Ristretto255 random scalar generation
|
||||
* Updated generic-array to v1
|
||||
|
||||
## 0.5.0 (March 6, 2024)
|
||||
* Just a version bump from v0.5.0-pre.7
|
||||
|
||||
## 0.5.0-pre.7 (January 11, 2024)
|
||||
* Updated to be in sync with RFC 9497
|
||||
|
||||
## 0.5.0-pre.6 (July 24, 2023)
|
||||
* Updated curve25519-dalek dependency to 4
|
||||
|
||||
## 0.5.0-pre.5 (June 27, 2023)
|
||||
* Updated curve25519-dalek dependency to 4.0.0-rc.3
|
||||
|
||||
## 0.5.0-pre.4 (May 20, 2023)
|
||||
* Updated curve25519-dalek dependency to 4.0.0-rc.2
|
||||
|
||||
## 0.5.0-pre.3 (March 4, 2023)
|
||||
* Updated to be in sync with draft-irtf-cfrg-voprf-19
|
||||
* Increased MSRV to 1.65
|
||||
* Updated p256 dependency to v0.13
|
||||
* Added p384 tests
|
||||
|
||||
## 0.5.0-pre.2 (February 3, 2023)
|
||||
* Increased MSRV to 1.60
|
||||
* Updated p256 dependency to v0.12
|
||||
* Updated curve25519-dalek dependency to 4.0.0-rc.1
|
||||
|
||||
## 0.5.0-pre.1 (December 19, 2022)
|
||||
* Updated curve25519-dalek dependency to 4.0.0-pre.5
|
||||
|
||||
## 0.4.0 (September 15, 2022)
|
||||
* Updated to be in sync with draft-irtf-cfrg-voprf-11, with
|
||||
the addition of the POPRF mode
|
||||
* Adds the evaluate() function to the servers to calculate the output of the OPRF
|
||||
directly
|
||||
* Renames the former evaluate() function to blind_evaluate to match the spec
|
||||
* Fixes the order of parameters for PoprfClient::blind to align it with the
|
||||
other clients
|
||||
* Exposes the derive_key function under the "danger" feature
|
||||
* Added support for running the API without performing allocations
|
||||
* Revamped the way the Group trait was used, so as to be more easily
|
||||
extendable to other groups
|
||||
* Added common traits for each public-facing struct, including serde
|
||||
support
|
||||
|
||||
## 0.3.0 (October 25, 2021)
|
||||
|
||||
* Updated to be in sync with draft-irtf-cfrg-voprf-08
|
||||
|
||||
## 0.2.0 (October 18, 2021)
|
||||
|
||||
* Removed the CipherSuite interface
|
||||
* Added the "danger" feature for exposing internal functions
|
||||
* General improvements to the group interface
|
||||
|
||||
## 0.1.0 (September 29, 2021)
|
||||
|
||||
* Initial release
|
||||
|
||||
+2
-1
@@ -27,4 +27,5 @@ outlined on that page and do not file a public issue.
|
||||
|
||||
## License
|
||||
By contributing to voprf, you agree that your contributions will be
|
||||
licensed under the LICENSE file in the root directory of this source tree.
|
||||
licensed under both the LICENSE-MIT and LICENSE-APACHE files in the root
|
||||
directory of this source tree.
|
||||
|
||||
+55
-42
@@ -1,56 +1,69 @@
|
||||
[package]
|
||||
name = "voprf"
|
||||
version = "0.1.0"
|
||||
authors = ["Kevin Lewi <[email protected]>"]
|
||||
categories = ["no-std", "algorithms", "cryptography"]
|
||||
description = "An implementation of a verifiable oblivious pseudorandom function (VOPRF)"
|
||||
authors = ["Kevin Lewi <[email protected]>"]
|
||||
categories = ["no-std"]
|
||||
edition = "2021"
|
||||
keywords = ["oprf"]
|
||||
license = "MIT"
|
||||
edition = "2018"
|
||||
name = "voprf"
|
||||
readme = "README.md"
|
||||
resolver = "2"
|
||||
repository = "https://github.com/facebook/voprf/"
|
||||
rust-version = "1.85"
|
||||
version = "0.6.0-pre.1"
|
||||
|
||||
[features]
|
||||
default = ["u64_backend", "serialize"]
|
||||
p256 = ["num-bigint", "num-integer", "num-traits", "once_cell", "p256_"]
|
||||
std = ["curve25519-dalek/std", "getrandom", "rand/std", "rand/std_rng", "num-bigint/std", "num-integer/std", "num-traits/std"]
|
||||
u64_backend = ["curve25519-dalek/u64_backend"]
|
||||
u32_backend = ["curve25519-dalek/u32_backend"]
|
||||
serialize = ["serde", "base64", "generic-array/serde", "curve25519-dalek/serde"]
|
||||
alloc = []
|
||||
danger = []
|
||||
default = ["ristretto255-ciphersuite", "dep:serde"]
|
||||
ristretto255 = ["dep:curve25519-dalek"]
|
||||
ristretto255-ciphersuite = ["ristretto255", "dep:sha2"]
|
||||
serde = ["curve25519-dalek?/serde", "generic-array/serde", "dep:serde"]
|
||||
std = ["alloc"]
|
||||
|
||||
[dependencies]
|
||||
base64 = { version = "0.13", default-features = false, features = ["alloc"], optional = true }
|
||||
constant_time_eq = "0.1"
|
||||
curve25519-dalek = { version = "3", default-features = false }
|
||||
digest = "0.9"
|
||||
curve25519-dalek = { version = "4", default-features = false, features = [
|
||||
"rand_core",
|
||||
"zeroize",
|
||||
], optional = true }
|
||||
derive-where = { version = "1", features = ["zeroize-on-drop"] }
|
||||
digest = "0.10"
|
||||
displaydoc = { version = "0.2", default-features = false }
|
||||
generic-array = "0.14"
|
||||
getrandom = { version = "0.2", optional = true }
|
||||
hkdf = "0.11"
|
||||
hmac = "0.11"
|
||||
num-bigint = { version = "0.4", default-features = false, optional = true }
|
||||
num-integer = { version = "0.1", default-features = false, optional = true }
|
||||
num-traits = { version = "0.2", default-features = false, optional = true }
|
||||
once_cell = { version = "1", default-features = false, optional = true }
|
||||
p256_ = { package = "p256", version = "0.9", default-features = false, features = ["arithmetic", "zeroize"], optional = true }
|
||||
rand = { version = "0.8", default-features = false }
|
||||
serde = { version = "1", default-features = false, features = ["alloc", "derive"], optional = true }
|
||||
subtle = { version = "2.3", default-features = false }
|
||||
zeroize = { version = "1", features = ["zeroize_derive"] }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
getrandom = { version = "0.2", features = ["js"], optional = true }
|
||||
elliptic-curve = { version = "0.13", features = [
|
||||
"hash2curve",
|
||||
"sec1",
|
||||
"voprf",
|
||||
] }
|
||||
generic-array = "1"
|
||||
rand_core = { version = "0.9", default-features = false }
|
||||
serde = { version = "1", default-features = false, features = [
|
||||
"derive",
|
||||
], optional = true }
|
||||
sha2 = { version = "0.10", default-features = false, optional = true }
|
||||
subtle = { version = "2.6", default-features = false }
|
||||
zeroize = { version = "1.5", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
base64 = "0.13"
|
||||
bincode = "1"
|
||||
chacha20poly1305 = "0.8"
|
||||
criterion = "0.3"
|
||||
generic-array = { version = "1" }
|
||||
hex = "0.4"
|
||||
json = "0.12"
|
||||
lazy_static = "1"
|
||||
serde_json = "1"
|
||||
sha2 = "0.9"
|
||||
p256 = { version = "0.13", default-features = false, features = [
|
||||
"hash2curve",
|
||||
"voprf",
|
||||
] }
|
||||
p384 = { version = "0.13", default-features = false, features = [
|
||||
"hash2curve",
|
||||
"voprf",
|
||||
] }
|
||||
p521 = { version = "0.13.3", default-features = false, features = [
|
||||
"hash2curve",
|
||||
"voprf",
|
||||
] }
|
||||
proptest = "1"
|
||||
rand = "0.9"
|
||||
regex = "1"
|
||||
rustyline = "8"
|
||||
voprf = { path = "", default-features = false, features = ["std"] }
|
||||
serde_json = "1"
|
||||
sha2 = "0.10"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
targets = []
|
||||
|
||||
@@ -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.
|
||||
@@ -3,7 +3,7 @@ An implementation of a (verifiable) oblivious pseudorandom function (VOPRF)
|
||||
|
||||
A VOPRF is a verifiable oblivious pseudorandom function, a protocol between a client and a server. The regular (non-verifiable) OPRF is also supported in this implementation.
|
||||
|
||||
This implementation is based on the [Internet Draft for VOPRF](https://github.com/cfrg/draft-irtf-cfrg-voprf).
|
||||
This implementation is based on [RFC 9497](https://www.rfc-editor.org/rfc/rfc9497).
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
@@ -16,12 +16,12 @@ Installation
|
||||
Add the following line to the dependencies of your `Cargo.toml`:
|
||||
|
||||
```
|
||||
voprf = "0.1.0"
|
||||
voprf = "0.6.0-pre.1"
|
||||
```
|
||||
|
||||
### Minimum Supported Rust Version
|
||||
|
||||
Rust **1.51** or higher.
|
||||
Rust **1.85** or higher.
|
||||
|
||||
Contributors
|
||||
------------
|
||||
@@ -32,4 +32,6 @@ To learn more about contributing to this project, [see this document](./CONTRIBU
|
||||
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,8 @@
|
||||
format_code_in_doc_comments = true
|
||||
format_strings = true
|
||||
group_imports = "StdExternalCrate"
|
||||
imports_granularity = "Module"
|
||||
license_template_path = ".cargo/license.rs"
|
||||
newline_style = "Unix"
|
||||
unstable_features = true
|
||||
wrap_comments = true
|
||||
+42
-9
@@ -1,17 +1,50 @@
|
||||
// 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
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// This source code is dual-licensed under either the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
|
||||
//! Defines the CipherSuite trait to specify the underlying primitives for VOPRF
|
||||
|
||||
use digest::core_api::BlockSizeUser;
|
||||
use digest::{FixedOutput, HashMarker, OutputSizeUser};
|
||||
use elliptic_curve::VoprfParameters;
|
||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
|
||||
use generic_array::ArrayLength;
|
||||
|
||||
use crate::Group;
|
||||
|
||||
/// Configures the underlying primitives used in VOPRF
|
||||
pub trait CipherSuite {
|
||||
pub trait CipherSuite
|
||||
where
|
||||
<Self::Hash as OutputSizeUser>::OutputSize:
|
||||
ArrayLength + IsLess<U256> + IsLessOrEqual<<Self::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
/// The ciphersuite identifier as dictated by
|
||||
/// <https://www.rfc-editor.org/rfc/rfc9497>
|
||||
const ID: &'static str;
|
||||
|
||||
/// A finite cyclic group along with a point representation that allows some
|
||||
/// customization on how to hash an input to a curve point. See `group::Group`.
|
||||
type Group: crate::group::Group;
|
||||
/// The main hash function to use (for HKDF computations and hashing transcripts).
|
||||
type Hash: crate::hash::Hash;
|
||||
/// customization on how to hash an input to a curve point. See [`Group`].
|
||||
type Group: Group;
|
||||
|
||||
/// The main hash function to use (for HKDF computations and hashing
|
||||
/// transcripts).
|
||||
type Hash: BlockSizeUser + Default + FixedOutput + HashMarker;
|
||||
}
|
||||
|
||||
impl<T: VoprfParameters> CipherSuite for T
|
||||
where
|
||||
T: Group,
|
||||
T::Hash: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||
<T::Hash as OutputSizeUser>::OutputSize:
|
||||
ArrayLength + IsLess<U256> + IsLessOrEqual<<T::Hash as BlockSizeUser>::BlockSize>,
|
||||
{
|
||||
const ID: &'static str = T::ID;
|
||||
|
||||
type Group = T;
|
||||
|
||||
type Hash = T::Hash;
|
||||
}
|
||||
|
||||
+523
@@ -0,0 +1,523 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
//
|
||||
// This source code is dual-licensed under either the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
|
||||
//! Common functionality between multiple OPRF modes.
|
||||
|
||||
use core::convert::TryFrom;
|
||||
use core::ops::Add;
|
||||
|
||||
use derive_where::derive_where;
|
||||
use digest::{Digest, Output, OutputSizeUser};
|
||||
use generic_array::sequence::Concat;
|
||||
use generic_array::typenum::{IsLess, Unsigned, U2, U256, U9};
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use rand_core::{TryCryptoRng, TryRngCore};
|
||||
use subtle::ConstantTimeEq;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::serialization::serde::{Element, Scalar};
|
||||
use crate::{CipherSuite, Error, Group, InternalError, Result};
|
||||
|
||||
///////////////
|
||||
// Constants //
|
||||
// ========= //
|
||||
///////////////
|
||||
|
||||
pub(crate) const STR_FINALIZE: [u8; 8] = *b"Finalize";
|
||||
pub(crate) const STR_SEED: [u8; 5] = *b"Seed-";
|
||||
pub(crate) const STR_DERIVE_KEYPAIR: [u8; 13] = *b"DeriveKeyPair";
|
||||
pub(crate) const STR_COMPOSITE: [u8; 9] = *b"Composite";
|
||||
pub(crate) const STR_CHALLENGE: [u8; 9] = *b"Challenge";
|
||||
pub(crate) const STR_INFO: [u8; 4] = *b"Info";
|
||||
pub(crate) const STR_OPRF: [u8; 7] = *b"OPRFV1-";
|
||||
pub(crate) const STR_HASH_TO_SCALAR: [u8; 13] = *b"HashToScalar-";
|
||||
pub(crate) const STR_HASH_TO_GROUP: [u8; 12] = *b"HashToGroup-";
|
||||
|
||||
/// Determines the mode of operation (either base mode or verifiable mode). This
|
||||
/// is only used for custom implementations for [`Group`].
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Mode {
|
||||
/// Non-verifiable mode.
|
||||
Oprf,
|
||||
/// Verifiable mode.
|
||||
Voprf,
|
||||
/// Partially-oblivious mode.
|
||||
Poprf,
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
/// Mode as it is represented in a context string.
|
||||
pub fn to_u8(self) -> u8 {
|
||||
match self {
|
||||
Mode::Oprf => 0,
|
||||
Mode::Voprf => 1,
|
||||
Mode::Poprf => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
// High-level API Structs //
|
||||
// ====================== //
|
||||
////////////////////////////
|
||||
|
||||
/// The first client message sent from a client (either verifiable or not) to a
|
||||
/// server (either verifiable or not).
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Elem)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct BlindedElement<CS: CipherSuite>(
|
||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||
pub(crate) <CS::Group as Group>::Elem,
|
||||
);
|
||||
|
||||
/// The server's response to the [BlindedElement] message from a client (either
|
||||
/// verifiable or not) to a server (either verifiable or not).
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Elem)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct EvaluationElement<CS: CipherSuite>(
|
||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||
pub(crate) <CS::Group as Group>::Elem,
|
||||
);
|
||||
|
||||
/// Contains prepared [`EvaluationElement`]s by a server batch evaluate
|
||||
/// preparation.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Elem)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct PreparedEvaluationElement<CS: CipherSuite>(pub(crate) EvaluationElement<CS>);
|
||||
|
||||
/// A proof produced by a server that the OPRF output matches against a server
|
||||
/// public key.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct Proof<CS: CipherSuite> {
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) c_scalar: <CS::Group as Group>::Scalar,
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) s_scalar: <CS::Group as Group>::Scalar,
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// Proof Functions //
|
||||
// =============== //
|
||||
/////////////////////
|
||||
|
||||
/// Can only fail with [`Error::Batch`].
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
pub(crate) fn generate_proof<CS: CipherSuite, R: TryRngCore + TryCryptoRng>(
|
||||
rng: &mut R,
|
||||
k: <CS::Group as Group>::Scalar,
|
||||
a: <CS::Group as Group>::Elem,
|
||||
b: <CS::Group as Group>::Elem,
|
||||
cs: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
|
||||
ds: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
|
||||
mode: Mode,
|
||||
) -> Result<Proof<CS>> {
|
||||
// https://www.rfc-editor.org/rfc/rfc9497#section-2.2.1
|
||||
|
||||
let (m, z) = compute_composites::<CS, _, _>(Some(k), b, cs, ds, mode)?;
|
||||
|
||||
let r = CS::Group::random_scalar(rng)?;
|
||||
let t2 = a * &r;
|
||||
let t3 = m * &r;
|
||||
|
||||
// Bm = GG.SerializeElement(B)
|
||||
let bm = CS::Group::serialize_elem(b);
|
||||
// a0 = GG.SerializeElement(M)
|
||||
let a0 = CS::Group::serialize_elem(m);
|
||||
// a1 = GG.SerializeElement(Z)
|
||||
let a1 = CS::Group::serialize_elem(z);
|
||||
// a2 = GG.SerializeElement(t2)
|
||||
let a2 = CS::Group::serialize_elem(t2);
|
||||
// a3 = GG.SerializeElement(t3)
|
||||
let a3 = CS::Group::serialize_elem(t3);
|
||||
|
||||
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
|
||||
|
||||
// h2Input = I2OSP(len(Bm), 2) || Bm ||
|
||||
// I2OSP(len(a0), 2) || a0 ||
|
||||
// I2OSP(len(a1), 2) || a1 ||
|
||||
// I2OSP(len(a2), 2) || a2 ||
|
||||
// I2OSP(len(a3), 2) || a3 ||
|
||||
// "Challenge"
|
||||
let h2_input = [
|
||||
&elem_len,
|
||||
bm.as_slice(),
|
||||
&elem_len,
|
||||
&a0,
|
||||
&elem_len,
|
||||
&a1,
|
||||
&elem_len,
|
||||
&a2,
|
||||
&elem_len,
|
||||
&a3,
|
||||
&STR_CHALLENGE,
|
||||
];
|
||||
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, mode);
|
||||
// This can't fail, the size of the `input` is known.
|
||||
let c_scalar = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
|
||||
let s_scalar = r - &(c_scalar * &k);
|
||||
|
||||
Ok(Proof { c_scalar, s_scalar })
|
||||
}
|
||||
|
||||
/// Can only fail with [`Error::ProofVerification`] or [`Error::Batch`].
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
pub(crate) fn verify_proof<CS: CipherSuite>(
|
||||
a: <CS::Group as Group>::Elem,
|
||||
b: <CS::Group as Group>::Elem,
|
||||
cs: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
|
||||
ds: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
|
||||
proof: &Proof<CS>,
|
||||
mode: Mode,
|
||||
) -> Result<()> {
|
||||
// https://www.rfc-editor.org/rfc/rfc9497#section-2.2.2
|
||||
let (m, z) = compute_composites::<CS, _, _>(None, b, cs, ds, mode)?;
|
||||
let t2 = (a * &proof.s_scalar) + &(b * &proof.c_scalar);
|
||||
let t3 = (m * &proof.s_scalar) + &(z * &proof.c_scalar);
|
||||
|
||||
// Bm = GG.SerializeElement(B)
|
||||
let bm = CS::Group::serialize_elem(b);
|
||||
// a0 = GG.SerializeElement(M)
|
||||
let a0 = CS::Group::serialize_elem(m);
|
||||
// a1 = GG.SerializeElement(Z)
|
||||
let a1 = CS::Group::serialize_elem(z);
|
||||
// a2 = GG.SerializeElement(t2)
|
||||
let a2 = CS::Group::serialize_elem(t2);
|
||||
// a3 = GG.SerializeElement(t3)
|
||||
let a3 = CS::Group::serialize_elem(t3);
|
||||
|
||||
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
|
||||
|
||||
// h2Input = I2OSP(len(Bm), 2) || Bm ||
|
||||
// I2OSP(len(a0), 2) || a0 ||
|
||||
// I2OSP(len(a1), 2) || a1 ||
|
||||
// I2OSP(len(a2), 2) || a2 ||
|
||||
// I2OSP(len(a3), 2) || a3 ||
|
||||
// "Challenge"
|
||||
let h2_input = [
|
||||
&elem_len,
|
||||
bm.as_slice(),
|
||||
&elem_len,
|
||||
&a0,
|
||||
&elem_len,
|
||||
&a1,
|
||||
&elem_len,
|
||||
&a2,
|
||||
&elem_len,
|
||||
&a3,
|
||||
&STR_CHALLENGE,
|
||||
];
|
||||
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, mode);
|
||||
// This can't fail, the size of the `input` is known.
|
||||
let c = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
|
||||
|
||||
match c.ct_eq(&proof.c_scalar).into() {
|
||||
true => Ok(()),
|
||||
false => Err(Error::ProofVerification),
|
||||
}
|
||||
}
|
||||
|
||||
type ComputeCompositesResult<CS> = (
|
||||
<<CS as CipherSuite>::Group as Group>::Elem,
|
||||
<<CS as CipherSuite>::Group as Group>::Elem,
|
||||
);
|
||||
|
||||
/// Can only fail with [`Error::Batch`].
|
||||
fn compute_composites<
|
||||
CS: CipherSuite,
|
||||
IC: Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
||||
ID: Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
||||
>(
|
||||
k_option: Option<<CS::Group as Group>::Scalar>,
|
||||
b: <CS::Group as Group>::Elem,
|
||||
c_slice: IC,
|
||||
d_slice: ID,
|
||||
mode: Mode,
|
||||
) -> Result<ComputeCompositesResult<CS>> {
|
||||
// https://www.rfc-editor.org/rfc/rfc9497#section-2.2.1
|
||||
|
||||
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
|
||||
|
||||
if c_slice.len() != d_slice.len() {
|
||||
return Err(Error::Batch);
|
||||
}
|
||||
|
||||
let len = u16::try_from(c_slice.len()).map_err(|_| Error::Batch)?;
|
||||
|
||||
// seedDST = "Seed-" || contextString
|
||||
let seed_dst = Dst::new::<CS, _, _>(STR_SEED, mode);
|
||||
|
||||
// h1Input = I2OSP(len(Bm), 2) || Bm ||
|
||||
// I2OSP(len(seedDST), 2) || seedDST
|
||||
// seed = Hash(h1Input)
|
||||
let seed = CS::Hash::new()
|
||||
.chain_update(elem_len)
|
||||
.chain_update(CS::Group::serialize_elem(b))
|
||||
.chain_update(seed_dst.i2osp_2())
|
||||
.chain_update_multi(&seed_dst.as_dst())
|
||||
.finalize();
|
||||
let seed_len = i2osp_2_array::<<CS::Hash as OutputSizeUser>::OutputSize>();
|
||||
|
||||
let mut m = CS::Group::identity_elem();
|
||||
let mut z = CS::Group::identity_elem();
|
||||
|
||||
for (i, (c, d)) in (0..len).zip(c_slice.zip(d_slice)) {
|
||||
// Ci = GG.SerializeElement(Cs[i])
|
||||
let ci = CS::Group::serialize_elem(c);
|
||||
// Di = GG.SerializeElement(Ds[i])
|
||||
let di = CS::Group::serialize_elem(d);
|
||||
// h2Input = I2OSP(len(seed), 2) || seed || I2OSP(i, 2) ||
|
||||
// I2OSP(len(Ci), 2) || Ci ||
|
||||
// I2OSP(len(Di), 2) || Di ||
|
||||
// "Composite"
|
||||
let h2_input = [
|
||||
seed_len.as_slice(),
|
||||
&seed,
|
||||
&i.to_be_bytes(),
|
||||
&elem_len,
|
||||
&ci,
|
||||
&elem_len,
|
||||
&di,
|
||||
&STR_COMPOSITE,
|
||||
];
|
||||
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, mode);
|
||||
// This can't fail, the size of the `input` is known.
|
||||
let di = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
|
||||
m = c * &di + &m;
|
||||
z = match k_option {
|
||||
Some(_) => z,
|
||||
None => d * &di + &z,
|
||||
};
|
||||
}
|
||||
|
||||
z = match k_option {
|
||||
Some(k) => m * &k,
|
||||
None => z,
|
||||
};
|
||||
|
||||
Ok((m, z))
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// Inner Functions //
|
||||
// =============== //
|
||||
/////////////////////
|
||||
|
||||
/// Can only fail with [`Error::DeriveKeyPair`] and [`Error::Protocol`].
|
||||
pub(crate) fn derive_key_internal<CS: CipherSuite>(
|
||||
seed: &[u8],
|
||||
info: &[u8],
|
||||
mode: Mode,
|
||||
) -> Result<<CS::Group as Group>::Scalar, Error> {
|
||||
let dst = Dst::new::<CS, _, _>(STR_DERIVE_KEYPAIR, mode);
|
||||
|
||||
let info_len = i2osp_2(info.len()).map_err(|_| Error::DeriveKeyPair)?;
|
||||
|
||||
for counter in 0_u8..=u8::MAX {
|
||||
// deriveInput = seed || I2OSP(len(info), 2) || info
|
||||
// skS = G.HashToScalar(deriveInput || I2OSP(counter, 1), DST = "DeriveKeyPair"
|
||||
// || contextString)
|
||||
let sk_s = CS::Group::hash_to_scalar::<CS::Hash>(
|
||||
&[seed, &info_len, info, &counter.to_be_bytes()],
|
||||
&dst.as_dst(),
|
||||
)
|
||||
.map_err(|_| Error::DeriveKeyPair)?;
|
||||
|
||||
if !bool::from(CS::Group::is_zero_scalar(sk_s)) {
|
||||
return Ok(sk_s);
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::Protocol)
|
||||
}
|
||||
|
||||
/// Corresponds to DeriveKeyPair() function from the VOPRF specification.
|
||||
///
|
||||
/// # Errors
|
||||
/// - [`Error::DeriveKeyPair`] if the `input` and `seed` together are longer
|
||||
/// then `u16::MAX - 3`.
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
#[cfg(feature = "danger")]
|
||||
pub fn derive_key<CS: CipherSuite>(
|
||||
seed: &[u8],
|
||||
info: &[u8],
|
||||
mode: Mode,
|
||||
) -> Result<<CS::Group as Group>::Scalar, Error> {
|
||||
derive_key_internal::<CS>(seed, info, mode)
|
||||
}
|
||||
|
||||
type DeriveKeypairResult<CS> = (
|
||||
<<CS as CipherSuite>::Group as Group>::Scalar,
|
||||
<<CS as CipherSuite>::Group as Group>::Elem,
|
||||
);
|
||||
|
||||
/// Can only fail with [`Error::DeriveKeyPair`] and [`Error::Protocol`].
|
||||
pub(crate) fn derive_keypair<CS: CipherSuite>(
|
||||
seed: &[u8],
|
||||
info: &[u8],
|
||||
mode: Mode,
|
||||
) -> Result<DeriveKeypairResult<CS>, Error> {
|
||||
let sk_s = derive_key_internal::<CS>(seed, info, mode)?;
|
||||
let pk_s = CS::Group::base_elem() * &sk_s;
|
||||
|
||||
Ok((sk_s, pk_s))
|
||||
}
|
||||
|
||||
/// Inner function for blind that assumes that the blinding factor has already
|
||||
/// been chosen, and therefore takes it as input. Does not check if the blinding
|
||||
/// factor is non-zero.
|
||||
///
|
||||
/// Can only fail with [`Error::Input`].
|
||||
pub(crate) fn deterministic_blind_unchecked<CS: CipherSuite>(
|
||||
input: &[u8],
|
||||
blind: &<CS::Group as Group>::Scalar,
|
||||
mode: Mode,
|
||||
) -> Result<<CS::Group as Group>::Elem> {
|
||||
let hashed_point = hash_to_group::<CS>(input, mode)?;
|
||||
Ok(hashed_point * blind)
|
||||
}
|
||||
|
||||
/// Hashes `input` to a point on the curve
|
||||
pub(crate) fn hash_to_group<CS: CipherSuite>(
|
||||
input: &[u8],
|
||||
mode: Mode,
|
||||
) -> Result<<CS::Group as Group>::Elem> {
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, mode);
|
||||
CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).map_err(|_| Error::Input)
|
||||
}
|
||||
|
||||
/// Internal function that finalizes the hash input for OPRF, VOPRF & POPRF.
|
||||
/// Returned values can only fail with [`Error::Input`].
|
||||
pub(crate) fn server_evaluate_hash_input<CS: CipherSuite>(
|
||||
input: &[u8],
|
||||
info: Option<&[u8]>,
|
||||
issued_element: GenericArray<u8, <<CS as CipherSuite>::Group as Group>::ElemLen>,
|
||||
) -> Result<Output<CS::Hash>> {
|
||||
// OPRF & VOPRF
|
||||
// hashInput = I2OSP(len(input), 2) || input ||
|
||||
// I2OSP(len(issuedElement), 2) || issuedElement ||
|
||||
// "Finalize"
|
||||
// return Hash(hashInput)
|
||||
//
|
||||
// POPRF
|
||||
// hashInput = I2OSP(len(input), 2) || input ||
|
||||
// I2OSP(len(info), 2) || info ||
|
||||
// I2OSP(len(issuedElement), 2) || issuedElement ||
|
||||
// "Finalize"
|
||||
|
||||
let mut hash = CS::Hash::new()
|
||||
.chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?)
|
||||
.chain_update(input.as_ref());
|
||||
if let Some(info) = info {
|
||||
hash = hash
|
||||
.chain_update(i2osp_2(info.as_ref().len()).map_err(|_| Error::Input)?)
|
||||
.chain_update(info.as_ref());
|
||||
}
|
||||
Ok(hash
|
||||
.chain_update(i2osp_2(issued_element.as_slice().len()).map_err(|_| Error::Input)?)
|
||||
.chain_update(issued_element)
|
||||
.chain_update(STR_FINALIZE)
|
||||
.finalize())
|
||||
}
|
||||
|
||||
pub(crate) struct Dst<L: ArrayLength> {
|
||||
dst_1: GenericArray<u8, L>,
|
||||
dst_2: &'static str,
|
||||
}
|
||||
|
||||
impl<L: ArrayLength> Dst<L> {
|
||||
pub(crate) fn new<CS, T, TL>(par_1: T, mode: Mode) -> Self
|
||||
where
|
||||
CS: CipherSuite,
|
||||
T: Into<GenericArray<u8, TL>>,
|
||||
TL: ArrayLength + Add<U9, Output = L>,
|
||||
{
|
||||
let par_1 = par_1.into();
|
||||
// Generates the contextString parameter as defined in
|
||||
// <https://www.rfc-editor.org/rfc/rfc9497#section-3.1>
|
||||
let par_2 = GenericArray::from(STR_OPRF)
|
||||
.concat([mode.to_u8()].into())
|
||||
.concat([b'-'].into());
|
||||
|
||||
let 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.as_bytes()]
|
||||
}
|
||||
|
||||
pub(crate) fn i2osp_2(&self) -> [u8; 2] {
|
||||
u16::try_from(L::USIZE + self.dst_2.len())
|
||||
.unwrap()
|
||||
.to_be_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
trait DigestExt {
|
||||
fn chain_update_multi(self, data: &[&[u8]]) -> Self;
|
||||
}
|
||||
|
||||
impl<T> DigestExt for T
|
||||
where
|
||||
T: Digest,
|
||||
{
|
||||
fn chain_update_multi(mut self, datas: &[&[u8]]) -> Self {
|
||||
for data in datas {
|
||||
self.update(data)
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// Utility Functions //
|
||||
// ================= //
|
||||
///////////////////////
|
||||
|
||||
pub(crate) fn i2osp_2(input: usize) -> Result<[u8; 2], InternalError> {
|
||||
u16::try_from(input)
|
||||
.map(|input| input.to_be_bytes())
|
||||
.map_err(|_| InternalError::I2osp)
|
||||
}
|
||||
|
||||
pub(crate) fn i2osp_2_array<L: ArrayLength + IsLess<U256>>() -> GenericArray<u8, U2> {
|
||||
L::U16.to_be_bytes().into()
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
//
|
||||
// This source code is dual-licensed under either the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
|
||||
//! Errors which are produced during an execution of the protocol
|
||||
|
||||
/// [`Result`](core::result::Result) shorthand that uses [`Error`].
|
||||
pub type Result<T, E = Error> = core::result::Result<T, E>;
|
||||
|
||||
/// Represents an error in the manipulation of internal cryptographic data
|
||||
#[derive(Clone, Copy, Debug, displaydoc::Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub enum Error {
|
||||
/// Size of info is longer then [`u16::MAX`].
|
||||
Info,
|
||||
/// Size of input is empty or longer then [`u16::MAX`].
|
||||
Input,
|
||||
/// Size of info and seed together are longer then `u16::MAX - 3`.
|
||||
DeriveKeyPair,
|
||||
/// Failure to deserialize bytes
|
||||
Deserialization,
|
||||
/// Batched items are more then [`u16::MAX`] or length don't match.
|
||||
Batch,
|
||||
/// In verifiable mode, occurs when the proof failed to verify
|
||||
ProofVerification,
|
||||
/// The protocol has failed and can't be completed.
|
||||
Protocol,
|
||||
/// Random number generator failure.
|
||||
Rng,
|
||||
}
|
||||
|
||||
/// Only used to implement [`Group`](crate::Group).
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub enum InternalError {
|
||||
/// Size of input is empty or longer then [`u16::MAX`].
|
||||
Input,
|
||||
/// `input` is longer then [`u16::MAX`].
|
||||
I2osp,
|
||||
}
|
||||
|
||||
impl core::error::Error for Error {}
|
||||
@@ -1,60 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
//! A list of error types which are produced during an execution of the protocol
|
||||
use core::fmt::Debug;
|
||||
#[cfg(feature = "std")]
|
||||
use std::error::Error;
|
||||
|
||||
use displaydoc::Display;
|
||||
|
||||
/// Represents an error in the manipulation of internal cryptographic data
|
||||
#[derive(Clone, Display, Eq, Hash, PartialEq)]
|
||||
pub enum InternalError {
|
||||
/// Could not parse byte sequence for key
|
||||
InvalidByteSequence,
|
||||
/// Could not deserialize element, or deserialized to the identity element
|
||||
PointError,
|
||||
/// Computing the hash-to-curve function failed
|
||||
HashToCurveError,
|
||||
/// Failure to serialize or deserialize bytes
|
||||
SerializationError,
|
||||
/// Use of incompatible modes (base vs. verifiable)
|
||||
IncompatibleModeError,
|
||||
/**
|
||||
* Internal error thrown when different-lengthed slices are supplied
|
||||
* to the compute_composites() function.
|
||||
*/
|
||||
MismatchedLengthsForCompositeInputs,
|
||||
/// In verifiable mode, occurs when the proof failed to verify
|
||||
ProofVerificationError,
|
||||
/// Encountered insufficient bytes when attempting to deserialize
|
||||
SizeError,
|
||||
/// Encountered a zero scalar
|
||||
ZeroScalarError,
|
||||
}
|
||||
|
||||
impl Debug for InternalError {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
Self::InvalidByteSequence => f.debug_tuple("InvalidByteSequence").finish(),
|
||||
Self::PointError => f.debug_tuple("PointError").finish(),
|
||||
Self::HashToCurveError => f.debug_tuple("HashToCurveError").finish(),
|
||||
Self::SerializationError => f.debug_tuple("SerializationError").finish(),
|
||||
Self::IncompatibleModeError => f.debug_tuple("IncompatibleModeError").finish(),
|
||||
Self::MismatchedLengthsForCompositeInputs => f
|
||||
.debug_tuple("MismatchedLengthsForCompositeInputs")
|
||||
.finish(),
|
||||
Self::ProofVerificationError => f.debug_tuple("ProofVerificationError").finish(),
|
||||
Self::SizeError => f.debug_tuple("SizeError").finish(),
|
||||
Self::ZeroScalarError => f.debug_tuple("ZeroScalarError").finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Error for InternalError {}
|
||||
@@ -0,0 +1,165 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
//
|
||||
// This source code is dual-licensed under either the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
|
||||
use core::num::NonZeroU32;
|
||||
use core::ops::Add;
|
||||
|
||||
use digest::core_api::BlockSizeUser;
|
||||
use digest::{FixedOutput, HashMarker};
|
||||
use elliptic_curve::group::cofactor::CofactorGroup;
|
||||
use elliptic_curve::hash2curve::{ExpandMsgXmd, FromOkm, GroupDigest};
|
||||
use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint};
|
||||
use elliptic_curve::{
|
||||
AffinePoint, Field, FieldBytes, FieldBytesSize, Group as _, ProjectivePoint, PublicKey, Scalar,
|
||||
SecretKey,
|
||||
};
|
||||
use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256};
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use rand_core::{TryCryptoRng, TryRngCore};
|
||||
|
||||
use super::Group;
|
||||
use crate::{Error, InternalError, Result};
|
||||
|
||||
type ElemLen<C> = <ScalarLen<C> as ModulusSize>::CompressedPointSize;
|
||||
type ScalarLen<C> = FieldBytesSize<C>;
|
||||
|
||||
impl<C> Group for C
|
||||
where
|
||||
C: GroupDigest,
|
||||
ProjectivePoint<Self>: CofactorGroup + ToEncodedPoint<Self>,
|
||||
ScalarLen<Self>: ModulusSize,
|
||||
ScalarLen<Self>: ArrayLength,
|
||||
AffinePoint<Self>: FromEncodedPoint<Self> + ToEncodedPoint<Self>,
|
||||
Scalar<Self>: FromOkm,
|
||||
// `VoprfClientLen`, `PoprfClientLen`, `VoprfServerLen`, `PoprfServerLen`
|
||||
ScalarLen<Self>: Add<ElemLen<Self>>,
|
||||
Sum<ScalarLen<Self>, ElemLen<Self>>: ArrayLength,
|
||||
// `ProofLen`
|
||||
ScalarLen<Self>: Add<ScalarLen<Self>>,
|
||||
Sum<ScalarLen<Self>, ScalarLen<Self>>: ArrayLength,
|
||||
ElemLen<Self>: ArrayLength,
|
||||
{
|
||||
type Elem = ProjectivePoint<Self>;
|
||||
|
||||
type ElemLen = ElemLen<Self>;
|
||||
|
||||
type Scalar = Scalar<Self>;
|
||||
|
||||
type ScalarLen = ScalarLen<Self>;
|
||||
|
||||
// Implements the `hash_to_curve()` function from
|
||||
// https://www.rfc-editor.org/rfc/rfc9380.html#section-3
|
||||
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError>
|
||||
where
|
||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
||||
{
|
||||
Self::hash_from_bytes::<ExpandMsgXmd<H>>(input, dst).map_err(|_| InternalError::Input)
|
||||
}
|
||||
|
||||
// Implements the `HashToScalar()` function
|
||||
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
|
||||
where
|
||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
||||
{
|
||||
<Self as GroupDigest>::hash_to_scalar::<ExpandMsgXmd<H>>(input, dst)
|
||||
.map_err(|_| InternalError::Input)
|
||||
}
|
||||
|
||||
fn base_elem() -> Self::Elem {
|
||||
ProjectivePoint::<Self>::generator()
|
||||
}
|
||||
|
||||
fn identity_elem() -> Self::Elem {
|
||||
ProjectivePoint::<Self>::identity()
|
||||
}
|
||||
|
||||
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> {
|
||||
let bytes = elem.to_encoded_point(true);
|
||||
let bytes = bytes.as_bytes();
|
||||
let mut result = GenericArray::default();
|
||||
result[..bytes.len()].copy_from_slice(bytes);
|
||||
result
|
||||
}
|
||||
|
||||
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem> {
|
||||
PublicKey::<Self>::from_sec1_bytes(element_bits)
|
||||
.map(|public_key| public_key.to_projective())
|
||||
.map_err(|_| Error::Deserialization)
|
||||
}
|
||||
|
||||
fn random_scalar<R: TryRngCore + TryCryptoRng>(rng: &mut R) -> Result<Self::Scalar> {
|
||||
Ok(*SecretKey::<Self>::random(&mut CompatRng(rng)).to_nonzero_scalar())
|
||||
}
|
||||
|
||||
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar {
|
||||
Option::from(scalar.invert()).unwrap()
|
||||
}
|
||||
|
||||
fn is_zero_scalar(scalar: Self::Scalar) -> subtle::Choice {
|
||||
scalar.is_zero()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn zero_scalar() -> Self::Scalar {
|
||||
Scalar::<Self>::ZERO
|
||||
}
|
||||
|
||||
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
||||
let bytes: FieldBytes<Self> = scalar.into();
|
||||
let mut result = GenericArray::<u8, Self::ScalarLen>::default();
|
||||
result.as_mut_slice().copy_from_slice(bytes.as_ref());
|
||||
result
|
||||
}
|
||||
|
||||
fn deserialize_scalar(scalar_bits: &[u8]) -> Result<Self::Scalar> {
|
||||
SecretKey::<Self>::from_slice(scalar_bits)
|
||||
.map(|secret_key| *secret_key.to_nonzero_scalar())
|
||||
.map_err(|_| Error::Deserialization)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adapter allowing `rand_core 0.9` RNGs to satisfy the `elliptic_curve` 0.13
|
||||
/// requirement for `rand_core 0.6` traits.
|
||||
///
|
||||
/// TODO #150: Remove this adapter when `elliptic_curve` migrates to `rand_core
|
||||
/// 0.9`.
|
||||
struct CompatRng<'a, R>(&'a mut R);
|
||||
|
||||
impl<'a, R> elliptic_curve::rand_core::RngCore for CompatRng<'a, R>
|
||||
where
|
||||
R: TryRngCore,
|
||||
{
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.0.try_next_u32().expect("RNG failure")
|
||||
}
|
||||
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
self.0.try_next_u64().expect("RNG failure")
|
||||
}
|
||||
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
self.0
|
||||
.try_fill_bytes(dest)
|
||||
.expect("RNG failure while filling bytes");
|
||||
}
|
||||
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), elliptic_curve::rand_core::Error> {
|
||||
self.0.try_fill_bytes(dest).map_err(|_| compat_error())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R> elliptic_curve::rand_core::CryptoRng for CompatRng<'a, R> where R: TryCryptoRng {}
|
||||
|
||||
fn compat_error() -> elliptic_curve::rand_core::Error {
|
||||
let code = NonZeroU32::new(elliptic_curve::rand_core::Error::CUSTOM_START)
|
||||
.expect("CUSTOM_START must be non-zero");
|
||||
elliptic_curve::rand_core::Error::from(code)
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
use crate::errors::InternalError;
|
||||
use crate::hash::Hash;
|
||||
use crate::serialization::i2osp;
|
||||
use alloc::vec::Vec;
|
||||
use digest::{BlockInput, Digest};
|
||||
use generic_array::typenum::Unsigned;
|
||||
|
||||
// Computes ceil(x / y)
|
||||
fn div_ceil(x: usize, y: usize) -> usize {
|
||||
let additive = (x % y != 0) as usize;
|
||||
x / y + additive
|
||||
}
|
||||
|
||||
fn xor(x: &[u8], y: &[u8]) -> Result<Vec<u8>, InternalError> {
|
||||
if x.len() != y.len() {
|
||||
return Err(InternalError::HashToCurveError);
|
||||
}
|
||||
|
||||
Ok(x.iter().zip(y).map(|(&x1, &x2)| x1 ^ x2).collect())
|
||||
}
|
||||
|
||||
/// Corresponds to the expand_message_xmd() function defined in
|
||||
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt>
|
||||
pub fn expand_message_xmd<H: Hash>(
|
||||
msg: &[u8],
|
||||
dst: &[u8],
|
||||
len_in_bytes: usize,
|
||||
) -> Result<Vec<u8>, InternalError> {
|
||||
let b_in_bytes = <H as Digest>::OutputSize::USIZE;
|
||||
let r_in_bytes = <H as BlockInput>::BlockSize::USIZE;
|
||||
|
||||
let ell = div_ceil(len_in_bytes, b_in_bytes);
|
||||
if ell > 255 {
|
||||
return Err(InternalError::HashToCurveError);
|
||||
}
|
||||
let dst_prime = [dst, &i2osp(dst.len(), 1)?].concat();
|
||||
let z_pad = i2osp(0, r_in_bytes)?;
|
||||
let l_i_b_str = i2osp(len_in_bytes, 2)?;
|
||||
let msg_prime = [&z_pad, msg, &l_i_b_str, &i2osp(0, 1)?, &dst_prime].concat();
|
||||
|
||||
let mut b: Vec<Vec<u8>> = alloc::vec![H::digest(&msg_prime).to_vec()]; // b[0]
|
||||
|
||||
let mut h = H::new();
|
||||
h.update(&b[0]);
|
||||
h.update(&i2osp(1, 1)?);
|
||||
h.update(&dst_prime);
|
||||
b.push(h.finalize_reset().to_vec()); // b[1]
|
||||
|
||||
let mut uniform_bytes: Vec<u8> = Vec::new();
|
||||
uniform_bytes.extend_from_slice(&b[1]);
|
||||
|
||||
for i in 2..(ell + 1) {
|
||||
h.update(xor(&b[0], &b[i - 1])?);
|
||||
h.update(&i2osp(i, 1)?);
|
||||
h.update(&dst_prime);
|
||||
b.push(h.finalize_reset().to_vec()); // b[i]
|
||||
uniform_bytes.extend_from_slice(&b[i]);
|
||||
}
|
||||
|
||||
Ok(uniform_bytes[..len_in_bytes].to_vec())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
struct Params {
|
||||
msg: &'static str,
|
||||
len_in_bytes: usize,
|
||||
uniform_bytes: &'static str,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expand_message_xmd() {
|
||||
// Test vectors taken from Section K.1 of https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt
|
||||
let test_vectors: alloc::vec::Vec<Params> = alloc::vec![
|
||||
Params {
|
||||
msg: "",
|
||||
len_in_bytes: 0x20,
|
||||
uniform_bytes: "f659819a6473c1835b25ea59e3d38914c98b374f0970b7e4c\
|
||||
92181df928fca88",
|
||||
},
|
||||
Params {
|
||||
msg: "abc",
|
||||
len_in_bytes: 0x20,
|
||||
uniform_bytes: "1c38f7c211ef233367b2420d04798fa4698080a8901021a79\
|
||||
5a1151775fe4da7",
|
||||
},
|
||||
Params {
|
||||
msg: "abcdef0123456789",
|
||||
len_in_bytes: 0x20,
|
||||
uniform_bytes: "8f7e7b66791f0da0dbb5ec7c22ec637f79758c0a48170bfb7c4611bd304ece89",
|
||||
},
|
||||
Params {
|
||||
msg: "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
|
||||
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
|
||||
qqqqqqqqqqqqqqqqqqqqqqqqq",
|
||||
len_in_bytes: 0x20,
|
||||
uniform_bytes: "72d5aa5ec810370d1f0013c0df2f1d65699494ee2a39f72e\
|
||||
1716b1b964e1c642",
|
||||
},
|
||||
Params {
|
||||
msg: "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
len_in_bytes: 0x20,
|
||||
uniform_bytes: "3b8e704fc48336aca4c2a12195b720882f2162a4b7b13a9c\
|
||||
350db46f429b771b",
|
||||
},
|
||||
Params {
|
||||
msg: "",
|
||||
len_in_bytes: 0x80,
|
||||
uniform_bytes: "8bcffd1a3cae24cf9cd7ab85628fd111bb17e3739d3b53f8\
|
||||
9580d217aa79526f1708354a76a402d3569d6a9d19ef3de4d0b991\
|
||||
e4f54b9f20dcde9b95a66824cbdf6c1a963a1913d43fd7ac443a02\
|
||||
fc5d9d8d77e2071b86ab114a9f34150954a7531da568a1ea8c7608\
|
||||
61c0cde2005afc2c114042ee7b5848f5303f0611cf297f",
|
||||
},
|
||||
Params {
|
||||
msg: "abc",
|
||||
len_in_bytes: 0x80,
|
||||
uniform_bytes: "fe994ec51bdaa821598047b3121c149b364b178606d5e72b\
|
||||
fbb713933acc29c186f316baecf7ea22212f2496ef3f785a27e84a\
|
||||
40d8b299cec56032763eceeff4c61bd1fe65ed81decafff4a31d01\
|
||||
98619c0aa0c6c51fca15520789925e813dcfd318b542f879944127\
|
||||
1f4db9ee3b8092a7a2e8d5b75b73e28fb1ab6b4573c192",
|
||||
},
|
||||
Params {
|
||||
msg: "abcdef0123456789",
|
||||
len_in_bytes: 0x80,
|
||||
uniform_bytes: "c9ec7941811b1e19ce98e21db28d22259354d4d0643e3011\
|
||||
75e2f474e030d32694e9dd5520dde93f3600d8edad94e5c3649030\
|
||||
88a7228cc9eff685d7eaac50d5a5a8229d083b51de4ccc3733917f\
|
||||
4b9535a819b445814890b7029b5de805bf62b33a4dc7e24acdf2c9\
|
||||
24e9fe50d55a6b832c8c84c7f82474b34e48c6d43867be",
|
||||
},
|
||||
Params {
|
||||
msg: "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
|
||||
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
|
||||
qqqqqqqqqqqqqqqqqqqqqqqqq",
|
||||
len_in_bytes: 0x80,
|
||||
uniform_bytes: "48e256ddba722053ba462b2b93351fc966026e6d6db49318\
|
||||
9798181c5f3feea377b5a6f1d8368d7453faef715f9aecb078cd40\
|
||||
2cbd548c0e179c4ed1e4c7e5b048e0a39d31817b5b24f50db58bb3\
|
||||
720fe96ba53db947842120a068816ac05c159bb5266c63658b4f00\
|
||||
0cbf87b1209a225def8ef1dca917bcda79a1e42acd8069",
|
||||
},
|
||||
Params {
|
||||
msg: "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
len_in_bytes: 0x80,
|
||||
uniform_bytes: "396962db47f749ec3b5042ce2452b619607f27fd3939ece2\
|
||||
746a7614fb83a1d097f554df3927b084e55de92c7871430d6b95c2\
|
||||
a13896d8a33bc48587b1f66d21b128a1a8240d5b0c26dfe795a1a8\
|
||||
42a0807bb148b77c2ef82ed4b6c9f7fcb732e7f94466c8b51e52bf\
|
||||
378fba044a31f5cb44583a892f5969dcd73b3fa128816e",
|
||||
},
|
||||
];
|
||||
let dst = "QUUX-V01-CS02-with-expander";
|
||||
|
||||
for tv in test_vectors {
|
||||
let uniform_bytes = super::expand_message_xmd::<sha2::Sha256>(
|
||||
tv.msg.as_bytes(),
|
||||
dst.as_bytes(),
|
||||
tv.len_in_bytes,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(tv.uniform_bytes, hex::encode(uniform_bytes));
|
||||
}
|
||||
}
|
||||
}
|
||||
+98
-95
@@ -1,128 +1,131 @@
|
||||
// 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
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// This source code is dual-licensed under either the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
|
||||
//! Defines the Group trait to specify the underlying prime order group
|
||||
|
||||
mod expand;
|
||||
#[cfg(feature = "p256")]
|
||||
pub(crate) mod p256;
|
||||
mod elliptic_curve;
|
||||
#[cfg(feature = "ristretto255")]
|
||||
mod ristretto;
|
||||
|
||||
use crate::errors::InternalError;
|
||||
use crate::hash::Hash;
|
||||
use core::ops::{Add, Mul, Sub};
|
||||
|
||||
use digest::core_api::BlockSizeUser;
|
||||
use digest::{FixedOutput, HashMarker};
|
||||
use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256};
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use rand_core::{TryCryptoRng, TryRngCore};
|
||||
#[cfg(feature = "ristretto255")]
|
||||
pub use ristretto::Ristretto255;
|
||||
use subtle::{Choice, ConstantTimeEq};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::{InternalError, Result};
|
||||
|
||||
/// A prime-order subgroup of a base field (EC, prime-order field ...). This
|
||||
/// subgroup is noted additively — as in the draft RFC — in this trait.
|
||||
pub trait Group:
|
||||
Copy
|
||||
+ Sized
|
||||
+ for<'a> Mul<&'a <Self as Group>::Scalar, Output = Self>
|
||||
+ for<'a> Add<&'a Self, Output = Self>
|
||||
/// subgroup is noted additively — as in the RFC — in this trait.
|
||||
pub trait Group
|
||||
where
|
||||
// `VoprfClientLen`, `PoprfClientLen`, `VoprfServerLen`, `PoprfServerLen`
|
||||
Self::ScalarLen: Add<Self::ElemLen>,
|
||||
Sum<Self::ScalarLen, Self::ElemLen>: ArrayLength,
|
||||
// `ProofLen`
|
||||
Self::ScalarLen: Add<Self::ScalarLen>,
|
||||
Sum<Self::ScalarLen, Self::ScalarLen>: ArrayLength,
|
||||
{
|
||||
/// The ciphersuite identifier as dictated by
|
||||
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-05.txt>
|
||||
const SUITE_ID: usize;
|
||||
|
||||
/// transforms a password and domain separation tag (DST) into a curve point
|
||||
fn hash_to_curve<H: Hash>(msg: &[u8], dst: &[u8]) -> Result<Self, InternalError>;
|
||||
|
||||
/// Hashes a slice of pseudo-random bytes to a scalar
|
||||
fn hash_to_scalar<H: Hash>(input: &[u8], dst: &[u8]) -> Result<Self::Scalar, InternalError>;
|
||||
|
||||
/// The type of base field scalars
|
||||
type Scalar: Zeroize
|
||||
/// The type of group elements
|
||||
type Elem: ConstantTimeEq
|
||||
+ Copy
|
||||
+ for<'a> Add<&'a Self::Scalar, Output = Self::Scalar>
|
||||
+ for<'a> Sub<&'a Self::Scalar, Output = Self::Scalar>
|
||||
+ for<'a> Mul<&'a Self::Scalar, Output = Self::Scalar>;
|
||||
/// The byte length necessary to represent scalars
|
||||
type ScalarLen: ArrayLength<u8> + 'static;
|
||||
|
||||
/// Return a scalar from its fixed-length bytes representation, without
|
||||
/// checking if the scalar is zero.
|
||||
fn from_scalar_slice_unchecked(
|
||||
scalar_bits: &GenericArray<u8, Self::ScalarLen>,
|
||||
) -> Result<Self::Scalar, InternalError>;
|
||||
|
||||
/// Return a scalar from its fixed-length bytes representation. If the scalar
|
||||
/// is zero, then return an error.
|
||||
fn from_scalar_slice(
|
||||
scalar_bits: &GenericArray<u8, Self::ScalarLen>,
|
||||
) -> Result<Self::Scalar, InternalError> {
|
||||
let scalar = Self::from_scalar_slice_unchecked(scalar_bits)?;
|
||||
if Self::ct_equal_scalar(&scalar, &Self::scalar_zero()) {
|
||||
return Err(InternalError::ZeroScalarError);
|
||||
}
|
||||
Ok(scalar)
|
||||
}
|
||||
|
||||
/// picks a scalar at random
|
||||
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar;
|
||||
/// Serializes a scalar to bytes
|
||||
fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen>;
|
||||
/// The multiplicative inverse of this scalar
|
||||
fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar;
|
||||
+ Zeroize
|
||||
+ for<'a> Add<&'a Self::Elem, Output = Self::Elem>
|
||||
+ for<'a> Mul<&'a Self::Scalar, Output = Self::Elem>;
|
||||
|
||||
/// The byte length necessary to represent group elements
|
||||
type ElemLen: ArrayLength<u8> + 'static;
|
||||
type ElemLen: ArrayLength + 'static;
|
||||
|
||||
/// Return an element from its fixed-length bytes representation. This is
|
||||
/// the unchecked version, which does not check for deserializing the identity
|
||||
/// element
|
||||
fn from_element_slice_unchecked(
|
||||
element_bits: &GenericArray<u8, Self::ElemLen>,
|
||||
) -> Result<Self, InternalError>;
|
||||
/// The type of base field scalars
|
||||
type Scalar: ConstantTimeEq
|
||||
+ Copy
|
||||
+ Zeroize
|
||||
+ for<'a> Add<&'a Self::Scalar, Output = Self::Scalar>
|
||||
+ for<'a> Mul<&'a Self::Scalar, Output = Self::Scalar>
|
||||
+ for<'a> Sub<&'a Self::Scalar, Output = Self::Scalar>;
|
||||
|
||||
/// Return an element from its fixed-length bytes representation. If the element
|
||||
/// is the identity element, return an error.
|
||||
fn from_element_slice(
|
||||
element_bits: &GenericArray<u8, Self::ElemLen>,
|
||||
) -> Result<Self, InternalError> {
|
||||
let elem = Self::from_element_slice_unchecked(element_bits)?;
|
||||
/// The byte length necessary to represent scalars
|
||||
type ScalarLen: ArrayLength + 'static;
|
||||
|
||||
if Self::ct_equal(&elem, &<Self as Group>::identity()) {
|
||||
// found the identity element
|
||||
return Err(InternalError::PointError);
|
||||
}
|
||||
/// Transforms a password and domain separation tag (DST) into a curve point
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer
|
||||
/// then [`u16::MAX`].
|
||||
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError>
|
||||
where
|
||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>;
|
||||
|
||||
Ok(elem)
|
||||
/// Hashes a slice of pseudo-random bytes to a scalar
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer
|
||||
/// then [`u16::MAX`].
|
||||
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
|
||||
where
|
||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>;
|
||||
|
||||
/// Get the base point for the group
|
||||
fn base_elem() -> Self::Elem;
|
||||
|
||||
/// Returns the identity group element
|
||||
fn identity_elem() -> Self::Elem;
|
||||
|
||||
/// Returns `true` if the element is equal to the identity element
|
||||
fn is_identity_elem(elem: Self::Elem) -> Choice {
|
||||
Self::identity_elem().ct_eq(&elem)
|
||||
}
|
||||
|
||||
/// Serializes the `self` group element
|
||||
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen>;
|
||||
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen>;
|
||||
|
||||
/// Get the base point for the group
|
||||
fn base_point() -> Self;
|
||||
/// Return an element from its fixed-length bytes representation. If the
|
||||
/// element is the identity element, return an error.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Deserialization`](crate::Error::Deserialization) if the element
|
||||
/// is not a valid point on the group or the identity element.
|
||||
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem>;
|
||||
|
||||
/// Returns if the group element is equal to the identity (1)
|
||||
fn is_identity(&self) -> bool {
|
||||
self.ct_equal(&<Self as Group>::identity())
|
||||
}
|
||||
/// Picks a scalar at random.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Rng`](crate::Error::Rng) if the random number generator fails.
|
||||
fn random_scalar<R: TryRngCore + TryCryptoRng>(rng: &mut R) -> Result<Self::Scalar>;
|
||||
|
||||
/// Returns the identity group element
|
||||
fn identity() -> Self;
|
||||
/// The multiplicative inverse of this scalar
|
||||
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar;
|
||||
|
||||
/// Returns `true` if the scalar is zero.
|
||||
fn is_zero_scalar(scalar: Self::Scalar) -> Choice;
|
||||
|
||||
/// Returns the scalar representing zero
|
||||
fn scalar_zero() -> Self::Scalar;
|
||||
#[cfg(test)]
|
||||
fn zero_scalar() -> Self::Scalar;
|
||||
|
||||
/// Compares in constant time if the group elements are equal
|
||||
fn ct_equal(&self, other: &Self) -> bool;
|
||||
/// Serializes a scalar to bytes
|
||||
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen>;
|
||||
|
||||
/// Compares in constant time if the scalars are equal
|
||||
fn ct_equal_scalar(s1: &Self::Scalar, s2: &Self::Scalar) -> bool;
|
||||
|
||||
/// Set the contents of self to the identity value
|
||||
fn zeroize(&mut self) {
|
||||
*self = <Self as Group>::identity();
|
||||
}
|
||||
/// Return a scalar from its fixed-length bytes representation. If the
|
||||
/// scalar is zero or invalid, then return an error.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Deserialization`](crate::Error::Deserialization) if the scalar
|
||||
/// is not a valid point on the group or zero.
|
||||
fn deserialize_scalar(scalar_bits: &[u8]) -> Result<Self::Scalar>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,555 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
// Note: This group implementation of p256 is experimental for now,
|
||||
// until hash-to-curve or crypto-bigint are fully supported.
|
||||
|
||||
#![allow(
|
||||
clippy::borrow_interior_mutable_const,
|
||||
clippy::declare_interior_mutable_const
|
||||
)]
|
||||
|
||||
use super::Group;
|
||||
use crate::errors::InternalError;
|
||||
use crate::hash::Hash;
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub};
|
||||
use core::str::FromStr;
|
||||
use generic_array::typenum::{U32, U33};
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use num_bigint::{BigInt, Sign};
|
||||
use num_integer::Integer;
|
||||
use num_traits::{One, ToPrimitive, Zero};
|
||||
use once_cell::unsync::Lazy;
|
||||
use p256_::elliptic_curve::group::prime::PrimeCurveAffine;
|
||||
use p256_::elliptic_curve::group::GroupEncoding;
|
||||
use p256_::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint};
|
||||
use p256_::elliptic_curve::subtle::ConstantTimeEq;
|
||||
use p256_::elliptic_curve::Field;
|
||||
use p256_::{AffinePoint, EncodedPoint, ProjectivePoint};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
// `L: 48`
|
||||
pub const L: usize = 48;
|
||||
|
||||
impl Group for ProjectivePoint {
|
||||
const SUITE_ID: usize = 0x0003;
|
||||
|
||||
// Implements the `hash_to_curve()` function from
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
||||
fn hash_to_curve<H: Hash>(msg: &[u8], dst: &[u8]) -> Result<Self, InternalError> {
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.2
|
||||
// `p: 2^256 - 2^224 + 2^192 + 2^96 - 1`
|
||||
const P: Lazy<BigInt> = Lazy::new(|| {
|
||||
BigInt::from_str(
|
||||
"115792089210356248762697446949407573530086143415290314195533631308867097853951",
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
// `A: -3`
|
||||
const A: Lazy<BigInt> = Lazy::new(|| BigInt::from(-3));
|
||||
// `B: 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b`
|
||||
const B: Lazy<BigInt> = Lazy::new(|| {
|
||||
BigInt::parse_bytes(
|
||||
b"5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
|
||||
16,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
// `Z: -10`
|
||||
const Z: Lazy<BigInt> = Lazy::new(|| BigInt::from(-10));
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
||||
// `hash_to_curve` calls `hash_to_field` with a `count` of `2`
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
|
||||
// `hash_to_field` calls `expand_message` with a `len_in_bytes` of `count * L`
|
||||
let uniform_bytes = super::expand::expand_message_xmd::<H>(msg, dst, 2 * L)?;
|
||||
|
||||
// hash to curve
|
||||
let (q0x, q0y) = hash_to_curve_simple_swu(&uniform_bytes[..L], &A, &B, &P, &Z);
|
||||
let (q1x, q1y) = hash_to_curve_simple_swu(&uniform_bytes[L..], &A, &B, &P, &Z);
|
||||
|
||||
// convert to `p256` types
|
||||
let p0 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates(
|
||||
&q0x, &q0y, false,
|
||||
))
|
||||
.ok_or(InternalError::PointError)?
|
||||
.to_curve();
|
||||
let p1 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates(
|
||||
&q1x, &q1y, false,
|
||||
))
|
||||
.ok_or(InternalError::PointError)?;
|
||||
|
||||
Ok(p0 + p1)
|
||||
}
|
||||
|
||||
// Implements the `HashToScalar()` function from
|
||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.3
|
||||
fn hash_to_scalar<H: Hash>(input: &[u8], dst: &[u8]) -> Result<Self::Scalar, InternalError> {
|
||||
// https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf#[{%22num%22:211,%22gen%22:0},{%22name%22:%22XYZ%22},70,700,0]
|
||||
// P-256 `n` is defined as `115792089210356248762697446949407573529996955224135760342 422259061068512044369`
|
||||
const N: once_cell::unsync::Lazy<BigInt> = once_cell::unsync::Lazy::new(|| {
|
||||
BigInt::from_str(
|
||||
"115792089210356248762697446949407573529996955224135760342422259061068512044369",
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
|
||||
// `HashToScalar` is `hash_to_field`
|
||||
let uniform_bytes = super::expand::expand_message_xmd::<H>(input, dst, L)?;
|
||||
let mut bytes = BigInt::from_bytes_be(Sign::Plus, &uniform_bytes)
|
||||
.mod_floor(&N)
|
||||
.to_bytes_be()
|
||||
.1;
|
||||
bytes.resize(32, 0);
|
||||
|
||||
Ok(p256_::Scalar::from_bytes_reduced(GenericArray::from_slice(
|
||||
&bytes,
|
||||
)))
|
||||
}
|
||||
|
||||
type ElemLen = U33;
|
||||
type Scalar = p256_::Scalar;
|
||||
type ScalarLen = U32;
|
||||
|
||||
fn from_scalar_slice_unchecked(
|
||||
scalar_bits: &GenericArray<u8, Self::ScalarLen>,
|
||||
) -> Result<Self::Scalar, InternalError> {
|
||||
Ok(Self::Scalar::from_bytes_reduced(scalar_bits))
|
||||
}
|
||||
|
||||
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
|
||||
Self::Scalar::random(rng)
|
||||
}
|
||||
|
||||
fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
||||
scalar.into()
|
||||
}
|
||||
|
||||
fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar {
|
||||
scalar.invert().unwrap_or(Self::Scalar::zero())
|
||||
}
|
||||
|
||||
fn from_element_slice_unchecked(
|
||||
element_bits: &GenericArray<u8, Self::ElemLen>,
|
||||
) -> Result<Self, InternalError> {
|
||||
Option::from(Self::from_bytes(element_bits)).ok_or(InternalError::PointError)
|
||||
}
|
||||
|
||||
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
|
||||
let mut bytes = self.to_affine().to_encoded_point(true).as_bytes().to_vec();
|
||||
bytes.resize(33, 0);
|
||||
*GenericArray::from_slice(&bytes)
|
||||
}
|
||||
|
||||
fn base_point() -> Self {
|
||||
Self::generator()
|
||||
}
|
||||
|
||||
fn identity() -> Self {
|
||||
Self::identity()
|
||||
}
|
||||
|
||||
fn scalar_zero() -> Self::Scalar {
|
||||
Self::Scalar::zero()
|
||||
}
|
||||
|
||||
fn ct_equal(&self, other: &Self) -> bool {
|
||||
self.ct_eq(other).into()
|
||||
}
|
||||
|
||||
fn ct_equal_scalar(s1: &Self::Scalar, s2: &Self::Scalar) -> bool {
|
||||
s1.ct_eq(s2).into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the hash_to_curve_simple_swu() function defined in
|
||||
/// <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-F.2>
|
||||
///
|
||||
/// `cmov`, `mod_floor` and `modpow` needs to be made constant-time, which
|
||||
/// will be supported after crypto-bigint is no longer experimental. See
|
||||
/// https://github.com/novifinancial/voprf/issues/13 for more context.
|
||||
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
fn hash_to_curve_simple_swu<N: ArrayLength<u8>>(
|
||||
u: &[u8],
|
||||
a: &BigInt,
|
||||
b: &BigInt,
|
||||
p: &BigInt,
|
||||
z: &BigInt,
|
||||
) -> (GenericArray<u8, N>, GenericArray<u8, N>) {
|
||||
#[derive(Clone)]
|
||||
struct Field<'a>(&'a BigInt);
|
||||
|
||||
impl<'a> Field<'a> {
|
||||
fn new(p: &'a BigInt) -> Self {
|
||||
Self(p)
|
||||
}
|
||||
|
||||
fn element(&'a self, number: &BigInt) -> FieldElement<'a> {
|
||||
FieldElement {
|
||||
number: number.mod_floor(self.0),
|
||||
f: self,
|
||||
}
|
||||
}
|
||||
|
||||
fn one(&'a self) -> FieldElement<'a> {
|
||||
self.element(&BigInt::one())
|
||||
}
|
||||
|
||||
/// See <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-4>
|
||||
fn inv0(&'a self, number: &FieldElement<'a>) -> FieldElement<'a> {
|
||||
number.pow_internal(&(self.0 - 2))
|
||||
}
|
||||
}
|
||||
|
||||
/// Finite field arithmetic
|
||||
#[derive(Clone)]
|
||||
struct FieldElement<'a> {
|
||||
number: BigInt,
|
||||
f: &'a Field<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Add for FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
&self + &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Add for &FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
self.f.element(&(&self.number + &rhs.number))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sub for &FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
self.f.element(&(&self.number - &rhs.number))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Neg for FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
fn neg(self) -> Self::Output {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Neg for &FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
fn neg(self) -> Self::Output {
|
||||
self.f.element(&-&self.number)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul for FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
&self * &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul<&Self> for FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
fn mul(self, rhs: &Self) -> Self::Output {
|
||||
&self * rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul<FieldElement<'a>> for &FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
fn mul(self, rhs: FieldElement<'a>) -> Self::Output {
|
||||
self * &rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul for &FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
self.f.element(&(&self.number * &rhs.number))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Div<&Self> for FieldElement<'a> {
|
||||
type Output = FieldElement<'a>;
|
||||
|
||||
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||
fn div(self, rhs: &Self) -> Self::Output {
|
||||
self * rhs.f.inv0(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FieldElement<'a> {
|
||||
fn square(&self) -> Self {
|
||||
self * self
|
||||
}
|
||||
|
||||
fn pow_internal(&self, exponent: &BigInt) -> Self {
|
||||
let exponent = exponent.mod_floor(&(self.f.0 - 1));
|
||||
self.f.element(&self.number.modpow(&exponent, self.f.0))
|
||||
}
|
||||
|
||||
/// Corresponds to the sqrt_3mod4() function defined in
|
||||
/// <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-I.1>
|
||||
fn sqrt(&self) -> Self {
|
||||
// constant
|
||||
let c1 = (self.f.0 + 1) >> 2;
|
||||
|
||||
self.pow_internal(&c1)
|
||||
}
|
||||
|
||||
/// Corresponds to the sgn0_m_eq_1() function defined in
|
||||
/// <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-4.1>
|
||||
fn sgn0(&self) -> i32 {
|
||||
(&self.number % 2_usize).to_i32().unwrap()
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
self.number.is_zero()
|
||||
}
|
||||
|
||||
/// Corresponds to the is_square() function defined in
|
||||
/// <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-4>
|
||||
fn is_square(&self) -> bool {
|
||||
// constant
|
||||
let exponent = (self.f.0 - 1) >> 1;
|
||||
|
||||
let result = self.pow_internal(&exponent);
|
||||
result.is_zero() || result.number.is_one()
|
||||
}
|
||||
|
||||
fn to_bytes<N: ArrayLength<u8>>(&self) -> GenericArray<u8, N> {
|
||||
let val = self.number.mod_floor(self.f.0).to_bytes_be().1;
|
||||
let mut bytes = alloc::vec![0u8; 32 - val.len()];
|
||||
bytes.extend_from_slice(&val);
|
||||
GenericArray::clone_from_slice(&bytes)
|
||||
}
|
||||
}
|
||||
|
||||
fn cmov<'a>(x: &FieldElement<'a>, y: &FieldElement<'a>, b: bool) -> FieldElement<'a> {
|
||||
if b {
|
||||
y.clone()
|
||||
} else {
|
||||
x.clone()
|
||||
}
|
||||
}
|
||||
|
||||
let f = Field::new(p);
|
||||
let a = f.element(a);
|
||||
let b = f.element(b);
|
||||
let z = f.element(z);
|
||||
let u = f.element(&BigInt::from_bytes_be(Sign::Plus, u));
|
||||
|
||||
// Constants:
|
||||
// 1. c1 = -B / A
|
||||
let c1 = -&b / &a;
|
||||
// 2. c2 = -1 / Z
|
||||
let c2 = -f.one() / &z;
|
||||
|
||||
// Steps:
|
||||
// 1. tv1 = Z * u^2
|
||||
let tv1 = z * u.square();
|
||||
// 2. tv2 = tv1^2
|
||||
let mut tv2 = tv1.square();
|
||||
// 3. x1 = tv1 + tv2
|
||||
let mut x1 = &tv1 + &tv2;
|
||||
// 4. x1 = inv0(x1)
|
||||
x1 = f.inv0(&x1);
|
||||
// 5. e1 = x1 == 0
|
||||
let e1 = x1.is_zero();
|
||||
// 6. x1 = x1 + 1
|
||||
x1 = x1 + f.one();
|
||||
// 7. x1 = CMOV(x1, c2, e1) # If (tv1 + tv2) == 0, set x1 = -1 / Z
|
||||
x1 = cmov(&x1, &c2, e1);
|
||||
// 8. x1 = x1 * c1 # x1 = (-B / A) * (1 + (1 / (Z^2 * u^4 + Z * u^2)))
|
||||
x1 = x1 * c1;
|
||||
// 9. gx1 = x1^2
|
||||
let mut gx1 = x1.square();
|
||||
// 10. gx1 = gx1 + A
|
||||
gx1 = gx1 + a;
|
||||
// 11. gx1 = gx1 * x1
|
||||
gx1 = gx1 * &x1;
|
||||
// 12. gx1 = gx1 + B # gx1 = g(x1) = x1^3 + A * x1 + B
|
||||
gx1 = gx1 + b;
|
||||
// 13. x2 = tv1 * x1 # x2 = Z * u^2 * x1
|
||||
let x2 = &tv1 * &x1;
|
||||
// 14. tv2 = tv1 * tv2
|
||||
tv2 = tv1 * tv2;
|
||||
// 15. gx2 = gx1 * tv2 # gx2 = (Z * u^2)^3 * gx1
|
||||
let gx2 = &gx1 * tv2;
|
||||
// 16. e2 = is_square(gx1)
|
||||
let e2 = gx1.is_square();
|
||||
// 17. x = CMOV(x2, x1, e2) # If is_square(gx1), x = x1, else x = x2
|
||||
let x = cmov(&x2, &x1, e2);
|
||||
// 18. y2 = CMOV(gx2, gx1, e2) # If is_square(gx1), y2 = gx1, else y2 = gx2
|
||||
let y2 = cmov(&gx2, &gx1, e2);
|
||||
// 19. y = sqrt(y2)
|
||||
let mut y = y2.sqrt();
|
||||
// 20. e3 = sgn0(u) == sgn0(y) # Fix sign of y
|
||||
let e3 = u.sgn0() == y.sgn0();
|
||||
// 21. y = CMOV(-y, y, e3)
|
||||
y = cmov(&-&y, &y, e3);
|
||||
// 22. return (x, y)
|
||||
(x.to_bytes(), y.to_bytes())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
struct Params {
|
||||
msg: &'static str,
|
||||
px: &'static str,
|
||||
py: &'static str,
|
||||
u0: &'static str,
|
||||
u1: &'static str,
|
||||
q0x: &'static str,
|
||||
q0y: &'static str,
|
||||
q1x: &'static str,
|
||||
q1y: &'static str,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hash_to_curve_simple_swu() {
|
||||
const P: Lazy<BigInt> = Lazy::new(|| {
|
||||
BigInt::from_str(
|
||||
"115792089210356248762697446949407573530086143415290314195533631308867097853951",
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
const A: Lazy<BigInt> = Lazy::new(|| BigInt::from(-3));
|
||||
const B: Lazy<BigInt> = Lazy::new(|| {
|
||||
BigInt::parse_bytes(
|
||||
b"5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
|
||||
16,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
const Z: Lazy<BigInt> = Lazy::new(|| BigInt::from(-10));
|
||||
|
||||
// Test vectors taken from https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-J.1.1
|
||||
let test_vectors = alloc::vec![
|
||||
Params {
|
||||
msg: "",
|
||||
px: "2c15230b26dbc6fc9a37051158c95b79656e17a1a920b11394ca91c44247d3e4",
|
||||
py: "8a7a74985cc5c776cdfe4b1f19884970453912e9d31528c060be9ab5c43e8415",
|
||||
u0: "ad5342c66a6dd0ff080df1da0ea1c04b96e0330dd89406465eeba11582515009",
|
||||
u1: "8c0f1d43204bd6f6ea70ae8013070a1518b43873bcd850aafa0a9e220e2eea5a",
|
||||
q0x: "ab640a12220d3ff283510ff3f4b1953d09fad35795140b1c5d64f313967934d5",
|
||||
q0y: "dccb558863804a881d4fff3455716c836cef230e5209594ddd33d85c565b19b1",
|
||||
q1x: "51cce63c50d972a6e51c61334f0f4875c9ac1cd2d3238412f84e31da7d980ef5",
|
||||
q1y: "b45d1a36d00ad90e5ec7840a60a4de411917fbe7c82c3949a6e699e5a1b66aac",
|
||||
},
|
||||
Params {
|
||||
msg: "abc",
|
||||
px: "0bb8b87485551aa43ed54f009230450b492fead5f1cc91658775dac4a3388a0f",
|
||||
py: "5c41b3d0731a27a7b14bc0bf0ccded2d8751f83493404c84a88e71ffd424212e",
|
||||
u0: "afe47f2ea2b10465cc26ac403194dfb68b7f5ee865cda61e9f3e07a537220af1",
|
||||
u1: "379a27833b0bfe6f7bdca08e1e83c760bf9a338ab335542704edcd69ce9e46e0",
|
||||
q0x: "5219ad0ddef3cc49b714145e91b2f7de6ce0a7a7dc7406c7726c7e373c58cb48",
|
||||
q0y: "7950144e52d30acbec7b624c203b1996c99617d0b61c2442354301b191d93ecf",
|
||||
q1x: "019b7cb4efcfeaf39f738fe638e31d375ad6837f58a852d032ff60c69ee3875f",
|
||||
q1y: "589a62d2b22357fed5449bc38065b760095ebe6aeac84b01156ee4252715446e",
|
||||
},
|
||||
Params {
|
||||
msg: "abcdef0123456789",
|
||||
px: "65038ac8f2b1def042a5df0b33b1f4eca6bff7cb0f9c6c1526811864e544ed80",
|
||||
py: "cad44d40a656e7aff4002a8de287abc8ae0482b5ae825822bb870d6df9b56ca3",
|
||||
u0: "0fad9d125a9477d55cf9357105b0eb3a5c4259809bf87180aa01d651f53d312c",
|
||||
u1: "b68597377392cd3419d8fcc7d7660948c8403b19ea78bbca4b133c9d2196c0fb",
|
||||
q0x: "a17bdf2965eb88074bc01157e644ed409dac97cfcf0c61c998ed0fa45e79e4a2",
|
||||
q0y: "4f1bc80c70d411a3cc1d67aeae6e726f0f311639fee560c7f5a664554e3c9c2e",
|
||||
q1x: "7da48bb67225c1a17d452c983798113f47e438e4202219dd0715f8419b274d66",
|
||||
q1y: "b765696b2913e36db3016c47edb99e24b1da30e761a8a3215dc0ec4d8f96e6f9",
|
||||
},
|
||||
Params {
|
||||
msg: "q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
|
||||
qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
|
||||
qqqqqqqqqqqqqqqqqqqqqqqqq",
|
||||
px: "4be61ee205094282ba8a2042bcb48d88dfbb609301c49aa8b078533dc65a0b5d",
|
||||
py: "98f8df449a072c4721d241a3b1236d3caccba603f916ca680f4539d2bfb3c29e",
|
||||
u0: "3bbc30446f39a7befad080f4d5f32ed116b9534626993d2cc5033f6f8d805919",
|
||||
u1: "76bb02db019ca9d3c1e02f0c17f8baf617bbdae5c393a81d9ce11e3be1bf1d33",
|
||||
q0x: "c76aaa823aeadeb3f356909cb08f97eee46ecb157c1f56699b5efebddf0e6398",
|
||||
q0y: "776a6f45f528a0e8d289a4be12c4fab80762386ec644abf2bffb9b627e4352b1",
|
||||
q1x: "418ac3d85a5ccc4ea8dec14f750a3a9ec8b85176c95a7022f391826794eb5a75",
|
||||
q1y: "fd6604f69e9d9d2b74b072d14ea13050db72c932815523305cb9e807cc900aff",
|
||||
},
|
||||
Params {
|
||||
msg: "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
px: "457ae2981f70ca85d8e24c308b14db22f3e3862c5ea0f652ca38b5e49cd64bc5",
|
||||
py: "ecb9f0eadc9aeed232dabc53235368c1394c78de05dd96893eefa62b0f4757dc",
|
||||
u0: "4ebc95a6e839b1ae3c63b847798e85cb3c12d3817ec6ebc10af6ee51adb29fec",
|
||||
u1: "4e21af88e22ea80156aff790750121035b3eefaa96b425a8716e0d20b4e269ee",
|
||||
q0x: "d88b989ee9d1295df413d4456c5c850b8b2fb0f5402cc5c4c7e815412e926db8",
|
||||
q0y: "bb4a1edeff506cf16def96afff41b16fc74f6dbd55c2210e5b8f011ba32f4f40",
|
||||
q1x: "a281e34e628f3a4d2a53fa87ff973537d68ad4fbc28d3be5e8d9f6a2571c5a4b",
|
||||
q1y: "f6ed88a7aab56a488100e6f1174fa9810b47db13e86be999644922961206e184",
|
||||
},
|
||||
];
|
||||
let dst = "QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_";
|
||||
|
||||
for tv in test_vectors {
|
||||
let uniform_bytes = super::super::expand::expand_message_xmd::<sha2::Sha256>(
|
||||
tv.msg.as_bytes(),
|
||||
dst.as_bytes(),
|
||||
96,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let u0 = BigInt::from_bytes_be(Sign::Plus, &uniform_bytes[..48]).mod_floor(&P);
|
||||
let u1 = BigInt::from_bytes_be(Sign::Plus, &uniform_bytes[48..]).mod_floor(&P);
|
||||
|
||||
assert_eq!(BigInt::parse_bytes(tv.u0.as_bytes(), 16).unwrap(), u0);
|
||||
assert_eq!(BigInt::parse_bytes(tv.u1.as_bytes(), 16).unwrap(), u1);
|
||||
|
||||
let (q0x, q0y) = super::hash_to_curve_simple_swu(&u0.to_bytes_be().1, &A, &B, &P, &Z);
|
||||
let (q1x, q1y) = super::hash_to_curve_simple_swu(&u1.to_bytes_be().1, &A, &B, &P, &Z);
|
||||
|
||||
assert_eq!(tv.q0x, hex::encode(q0x));
|
||||
assert_eq!(tv.q0y, hex::encode(q0y));
|
||||
assert_eq!(tv.q1x, hex::encode(q1x));
|
||||
assert_eq!(tv.q1y, hex::encode(q1y));
|
||||
|
||||
let p0 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates(
|
||||
&q0x, &q0y, false,
|
||||
))
|
||||
.unwrap()
|
||||
.to_curve();
|
||||
let p1 = AffinePoint::from_encoded_point(&EncodedPoint::from_affine_coordinates(
|
||||
&q1x, &q1y, false,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
let p = (p0 + p1).to_encoded_point(false);
|
||||
|
||||
assert_eq!(tv.px, hex::encode(p.x().unwrap()));
|
||||
assert_eq!(tv.py, hex::encode(p.y().unwrap()));
|
||||
}
|
||||
}
|
||||
}
|
||||
+109
-101
@@ -1,126 +1,134 @@
|
||||
// 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
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// This source code is dual-licensed under either the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
|
||||
use super::Group;
|
||||
use crate::errors::InternalError;
|
||||
use crate::hash::Hash;
|
||||
use core::convert::TryInto;
|
||||
use curve25519_dalek::{
|
||||
constants::RISTRETTO_BASEPOINT_POINT,
|
||||
ristretto::{CompressedRistretto, RistrettoPoint},
|
||||
scalar::Scalar,
|
||||
traits::Identity,
|
||||
};
|
||||
use generic_array::{typenum::U32, GenericArray};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
|
||||
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
use curve25519_dalek::traits::Identity;
|
||||
use digest::core_api::BlockSizeUser;
|
||||
use digest::{FixedOutput, HashMarker};
|
||||
use elliptic_curve::hash2curve::{ExpandMsg, ExpandMsgXmd, Expander};
|
||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256, U32, U64};
|
||||
use generic_array::GenericArray;
|
||||
use rand_core::{TryCryptoRng, TryRngCore};
|
||||
use subtle::ConstantTimeEq;
|
||||
|
||||
/// The implementation of such a subgroup for Ristretto
|
||||
impl Group for RistrettoPoint {
|
||||
const SUITE_ID: usize = 0x0001;
|
||||
use super::Group;
|
||||
use crate::{Error, InternalError, Result};
|
||||
|
||||
/// [`Group`] implementation for Ristretto255.
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct Ristretto255;
|
||||
|
||||
#[cfg(feature = "ristretto255-ciphersuite")]
|
||||
impl crate::CipherSuite for Ristretto255 {
|
||||
const ID: &'static str = "ristretto255-SHA512";
|
||||
|
||||
type Group = Ristretto255;
|
||||
|
||||
type Hash = sha2::Sha512;
|
||||
}
|
||||
|
||||
impl Group for Ristretto255 {
|
||||
type Elem = RistrettoPoint;
|
||||
|
||||
type ElemLen = U32;
|
||||
|
||||
type Scalar = Scalar;
|
||||
|
||||
type ScalarLen = U32;
|
||||
|
||||
// Implements the `hash_to_ristretto255()` function from
|
||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt
|
||||
fn hash_to_curve<H: Hash>(msg: &[u8], dst: &[u8]) -> Result<Self, InternalError> {
|
||||
let uniform_bytes = super::expand::expand_message_xmd::<H>(msg, dst, 64)?;
|
||||
// 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>,
|
||||
{
|
||||
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
|
||||
.as_slice()
|
||||
.try_into()
|
||||
.map_err(|_| InternalError::HashToCurveError)?,
|
||||
))
|
||||
Ok(RistrettoPoint::from_uniform_bytes(&uniform_bytes.into()))
|
||||
}
|
||||
|
||||
// Implements the `HashToScalar()` function from
|
||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.1
|
||||
fn hash_to_scalar<H: Hash>(input: &[u8], dst: &[u8]) -> Result<Self::Scalar, InternalError> {
|
||||
let uniform_bytes = super::expand::expand_message_xmd::<H>(input, dst, 64)?;
|
||||
|
||||
Ok(Scalar::from_bytes_mod_order_wide(
|
||||
uniform_bytes
|
||||
.as_slice()
|
||||
.try_into()
|
||||
.map_err(|_| InternalError::HashToCurveError)?,
|
||||
))
|
||||
}
|
||||
|
||||
type Scalar = Scalar;
|
||||
type ScalarLen = U32;
|
||||
fn from_scalar_slice_unchecked(
|
||||
scalar_bits: &GenericArray<u8, Self::ScalarLen>,
|
||||
) -> Result<Self::Scalar, InternalError> {
|
||||
Ok(Scalar::from_bytes_mod_order(*scalar_bits.as_ref()))
|
||||
}
|
||||
|
||||
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
|
||||
loop {
|
||||
let scalar = {
|
||||
#[cfg(not(test))]
|
||||
// https://www.rfc-editor.org/rfc/rfc9497#section-4.1
|
||||
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
|
||||
where
|
||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
||||
{
|
||||
let mut scalar_bytes = [0u8; 64];
|
||||
rng.fill_bytes(&mut scalar_bytes);
|
||||
Scalar::from_bytes_mod_order_wide(&scalar_bytes)
|
||||
let mut uniform_bytes = GenericArray::<_, U64>::default();
|
||||
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()))
|
||||
}
|
||||
|
||||
// Tests need an exact conversion from bytes to scalar, sampling only 32 bytes from rng
|
||||
#[cfg(test)]
|
||||
{
|
||||
let mut scalar_bytes = [0u8; 32];
|
||||
rng.fill_bytes(&mut scalar_bytes);
|
||||
Scalar::from_bytes_mod_order(scalar_bytes)
|
||||
}
|
||||
};
|
||||
|
||||
if scalar != Scalar::zero() {
|
||||
break scalar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn scalar_as_bytes(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
||||
scalar.to_bytes().into()
|
||||
}
|
||||
|
||||
fn scalar_invert(scalar: &Self::Scalar) -> Self::Scalar {
|
||||
scalar.invert()
|
||||
}
|
||||
|
||||
// The byte length necessary to represent group elements
|
||||
type ElemLen = U32;
|
||||
fn from_element_slice_unchecked(
|
||||
element_bits: &GenericArray<u8, Self::ElemLen>,
|
||||
) -> Result<Self, InternalError> {
|
||||
CompressedRistretto::from_slice(element_bits)
|
||||
.decompress()
|
||||
.ok_or(InternalError::PointError)
|
||||
}
|
||||
// serialization of a group element
|
||||
fn to_arr(&self) -> GenericArray<u8, Self::ElemLen> {
|
||||
self.compress().to_bytes().into()
|
||||
}
|
||||
|
||||
fn base_point() -> Self {
|
||||
fn base_elem() -> Self::Elem {
|
||||
RISTRETTO_BASEPOINT_POINT
|
||||
}
|
||||
|
||||
fn identity() -> Self {
|
||||
<Self as Identity>::identity()
|
||||
fn identity_elem() -> Self::Elem {
|
||||
RistrettoPoint::identity()
|
||||
}
|
||||
|
||||
fn scalar_zero() -> Self::Scalar {
|
||||
Self::Scalar::zero()
|
||||
// serialization of a group element
|
||||
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> {
|
||||
elem.compress().to_bytes().into()
|
||||
}
|
||||
|
||||
fn ct_equal(&self, other: &Self) -> bool {
|
||||
ConstantTimeEq::ct_eq(self, other).into()
|
||||
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem> {
|
||||
CompressedRistretto::from_slice(element_bits)
|
||||
.map_err(|_| Error::Deserialization)?
|
||||
.decompress()
|
||||
.filter(|point| point != &RistrettoPoint::identity())
|
||||
.ok_or(Error::Deserialization)
|
||||
}
|
||||
|
||||
fn ct_equal_scalar(s1: &Self::Scalar, s2: &Self::Scalar) -> bool {
|
||||
ConstantTimeEq::ct_eq(s1, s2).into()
|
||||
fn random_scalar<R: TryRngCore + TryCryptoRng>(rng: &mut R) -> Result<Self::Scalar> {
|
||||
loop {
|
||||
let mut scalar_bytes = [0u8; 32];
|
||||
rng.try_fill_bytes(&mut scalar_bytes)
|
||||
.map_err(|_| Error::Rng)?;
|
||||
|
||||
if let Ok(scalar) = Self::deserialize_scalar(&scalar_bytes) {
|
||||
break Ok(scalar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar {
|
||||
scalar.invert()
|
||||
}
|
||||
|
||||
fn is_zero_scalar(scalar: Self::Scalar) -> subtle::Choice {
|
||||
scalar.ct_eq(&Scalar::ZERO)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn zero_scalar() -> Self::Scalar {
|
||||
Scalar::ZERO
|
||||
}
|
||||
|
||||
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
||||
scalar.to_bytes().into()
|
||||
}
|
||||
|
||||
fn deserialize_scalar(scalar_bits: &[u8]) -> Result<Self::Scalar> {
|
||||
scalar_bits
|
||||
.try_into()
|
||||
.ok()
|
||||
.and_then(|bytes| Scalar::from_canonical_bytes(bytes).into())
|
||||
.filter(|scalar| scalar != &Scalar::ZERO)
|
||||
.ok_or(Error::Deserialization)
|
||||
}
|
||||
}
|
||||
|
||||
+33
-32
@@ -1,57 +1,58 @@
|
||||
// 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
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// This source code is dual-licensed under either the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
|
||||
//! Includes a series of tests for the group implementations
|
||||
|
||||
use crate::errors::InternalError;
|
||||
use crate::group::Group;
|
||||
use crate::CipherSuite;
|
||||
use crate::{Error, Group, Result};
|
||||
|
||||
// Test that the deserialization of a group element should throw an error
|
||||
// if the identity element can be deserialized properly
|
||||
// Test that the deserialization of a group element should throw an error if the
|
||||
// identity element can be deserialized properly
|
||||
|
||||
#[test]
|
||||
fn test_group_properties() -> Result<(), InternalError> {
|
||||
use crate::tests::Ristretto255Sha512;
|
||||
fn test_group_properties() -> Result<()> {
|
||||
use p256::NistP256;
|
||||
use p384::NistP384;
|
||||
use p521::NistP521;
|
||||
|
||||
test_identity_element_error::<Ristretto255Sha512>()?;
|
||||
test_zero_scalar_error::<Ristretto255Sha512>()?;
|
||||
|
||||
#[cfg(feature = "p256")]
|
||||
#[cfg(feature = "ristretto255")]
|
||||
{
|
||||
use crate::tests::P256Sha256;
|
||||
use crate::Ristretto255;
|
||||
|
||||
test_identity_element_error::<P256Sha256>()?;
|
||||
test_zero_scalar_error::<P256Sha256>()?;
|
||||
test_identity_element_error::<Ristretto255>()?;
|
||||
test_zero_scalar_error::<Ristretto255>()?;
|
||||
}
|
||||
|
||||
test_identity_element_error::<NistP256>()?;
|
||||
test_zero_scalar_error::<NistP256>()?;
|
||||
|
||||
test_identity_element_error::<NistP384>()?;
|
||||
test_zero_scalar_error::<NistP384>()?;
|
||||
|
||||
test_identity_element_error::<NistP521>()?;
|
||||
test_zero_scalar_error::<NistP521>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Checks that the identity element cannot be deserialized
|
||||
fn test_identity_element_error<CS: CipherSuite>() -> Result<(), InternalError> {
|
||||
let identity = CS::Group::identity();
|
||||
let result = CS::Group::from_element_slice(&identity.to_arr());
|
||||
assert!(match result {
|
||||
Err(InternalError::PointError) => true,
|
||||
_ => false,
|
||||
});
|
||||
fn test_identity_element_error<G: Group>() -> Result<()> {
|
||||
let identity = G::identity_elem();
|
||||
let result = G::deserialize_elem(&G::serialize_elem(identity));
|
||||
assert!(matches!(result, Err(Error::Deserialization)));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Checks that the zero scalar cannot be deserialized
|
||||
fn test_zero_scalar_error<CS: CipherSuite>() -> Result<(), InternalError> {
|
||||
let zero_scalar = CS::Group::scalar_zero();
|
||||
let result = CS::Group::from_scalar_slice(&CS::Group::scalar_as_bytes(zero_scalar));
|
||||
assert!(match result {
|
||||
Err(InternalError::ZeroScalarError) => true,
|
||||
_ => false,
|
||||
});
|
||||
fn test_zero_scalar_error<G: Group>() -> Result<()> {
|
||||
let zero_scalar = G::zero_scalar();
|
||||
let result = G::deserialize_scalar(&G::serialize_scalar(zero_scalar));
|
||||
assert!(matches!(result, Err(Error::Deserialization)));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
//! A convenience trait for digest bounds used throughout the library
|
||||
|
||||
use digest::{BlockInput, FixedOutput, Reset, Update};
|
||||
|
||||
/// Trait inheriting the requirements from digest::Digest for compatibility with HKDF and HMAC
|
||||
// Associated types could be simplified when they are made as defaults:
|
||||
// https://github.com/rust-lang/rust/issues/29661
|
||||
pub trait Hash: Update + BlockInput + FixedOutput + Reset + Default + Clone {}
|
||||
|
||||
impl<T: Update + BlockInput + FixedOutput + Reset + Default + Clone> Hash for T {}
|
||||
-203
@@ -1,203 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
macro_rules! impl_debug_eq_hash_for {
|
||||
(struct $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:ident$(, $field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => {
|
||||
impl$(<$($gen$(: $bound)?),+>)? core::fmt::Debug for $name$(<$($gen),+>)?
|
||||
$(where $($type: core::fmt::Debug,)+)?
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("$name")
|
||||
.field("$field1", &self.$field1)
|
||||
$(.field("$field2", &self.$field2))*
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl$(<$($gen$(: $bound)?),+>)? Eq for $name$(<$($gen),+>)?
|
||||
$(where $($type: Eq,)+)?
|
||||
{}
|
||||
|
||||
impl$(<$($gen$(: $bound)?),+>)? PartialEq for $name$(<$($gen),+>)?
|
||||
$(where $($type: PartialEq,)+)?
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
PartialEq::eq(&self.$field1, &other.$field1)
|
||||
$(&& PartialEq::eq(&self.$field2, &other.$field2))*
|
||||
}
|
||||
}
|
||||
|
||||
impl$(<$($gen$(: $bound)?),+>)? core::hash::Hash for $name$(<$($gen),+>)?
|
||||
$(where $($type: core::hash::Hash,)+)?
|
||||
{
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
core::hash::Hash::hash(&self.$field1, state);
|
||||
$(core::hash::Hash::hash(&self.$field2, state);)*
|
||||
}
|
||||
}
|
||||
};
|
||||
(tuple $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:tt$(, $field2:tt)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => {
|
||||
impl$(<$($gen$(: $bound)?),+>)? core::fmt::Debug for $name$(<$($gen),+>)?
|
||||
$(where $($type: core::fmt::Debug,)+)?
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_tuple("$name")
|
||||
.field(&self.$field1)
|
||||
$(.field(&self.$field2))*
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl$(<$($gen$(: $bound)?),+>)? Eq for $name$(<$($gen),+>)?
|
||||
$(where $($type: Eq,)+)?
|
||||
{}
|
||||
|
||||
impl$(<$($gen$(: $bound)?),+>)? PartialEq for $name$(<$($gen),+>)?
|
||||
$(where $($type: PartialEq,)+)?
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
PartialEq::eq(&self.$field1, &other.$field1)
|
||||
$(&& PartialEq::eq(&self.$field2, &other.$field2))*
|
||||
}
|
||||
}
|
||||
|
||||
impl$(<$($gen$(: $bound)?),+>)? core::hash::Hash for $name$(<$($gen),+>)?
|
||||
$(where $($type: core::hash::Hash,)+)?
|
||||
{
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
core::hash::Hash::hash(&self.$field1, state);
|
||||
$(core::hash::Hash::hash(&self.$field2, state);)*
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_clone_for {
|
||||
(struct $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:ident$(, $field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => {
|
||||
impl$(<$($gen$(: $bound)?),+>)? Clone for $name$(<$($gen),+>)?
|
||||
$(where $($type: Clone,)+)?
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
$field1: self.$field1.clone(),
|
||||
$($field2: self.$field2.clone(),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
(tuple $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:tt$(, $field2:tt)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => {
|
||||
impl$(<$($gen$(: $bound)?),+>)? Clone for $name$(<$($gen),+>)?
|
||||
$(where $($type: Clone,)+)?
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self(
|
||||
self.$field1.clone(),
|
||||
$(self.$field2.clone(),)*
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_zeroize_on_drop_for {
|
||||
(struct $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:ident$(, $field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => {
|
||||
impl$(<$($gen$(: $bound)?),+>)? zeroize::Zeroize for $name$(<$($gen),+>)?
|
||||
{
|
||||
fn zeroize(&mut self) {
|
||||
self.$field1.zeroize();
|
||||
$(self.$field2.zeroize();)*
|
||||
}
|
||||
}
|
||||
|
||||
impl$(<$($gen$(: $bound)?),+>)? Drop for $name$(<$($gen),+>)?
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
#[allow(unused_imports)]
|
||||
use zeroize::Zeroize;
|
||||
self.$field1.zeroize();
|
||||
$(self.$field2.zeroize();)*
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Inner macro used for deriving `serde`'s `Serialize` and `Deserialize` traits.
|
||||
macro_rules! impl_serialize_and_deserialize_for {
|
||||
($t:ident) => {
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<CS: CipherSuite> serde::Serialize for $t<CS> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
if serializer.is_human_readable() {
|
||||
serializer.serialize_str(&base64::encode(&self.serialize()))
|
||||
} else {
|
||||
serializer.serialize_bytes(&self.serialize())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'de, CS: CipherSuite> serde::Deserialize<'de> for $t<CS> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
if deserializer.is_human_readable() {
|
||||
let s = <&str>::deserialize(deserializer)?;
|
||||
$t::<CS>::deserialize(&base64::decode(s).map_err(serde::de::Error::custom)?)
|
||||
.map_err(serde::de::Error::custom)
|
||||
} else {
|
||||
struct ByteVisitor<CS: CipherSuite> {
|
||||
marker: core::marker::PhantomData<CS>,
|
||||
}
|
||||
impl<'de, CS: CipherSuite> serde::de::Visitor<'de> for ByteVisitor<CS> {
|
||||
type Value = $t<CS>;
|
||||
fn expecting(
|
||||
&self,
|
||||
formatter: &mut core::fmt::Formatter,
|
||||
) -> core::fmt::Result {
|
||||
formatter.write_str(core::concat!(
|
||||
"the byte representation of a ",
|
||||
core::stringify!($t)
|
||||
))
|
||||
}
|
||||
|
||||
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
$t::<CS>::deserialize(value).map_err(|_| {
|
||||
serde::de::Error::invalid_value(
|
||||
serde::de::Unexpected::Bytes(value),
|
||||
&core::concat!(
|
||||
"invalid byte sequence for ",
|
||||
core::stringify!($t)
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
deserializer.deserialize_bytes(ByteVisitor::<CS> {
|
||||
marker: core::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Convenience macro for implementing all of the above traits
|
||||
macro_rules! impl_traits_for {
|
||||
(struct $name:ident$(<$($gen:ident$(: $bound:tt)?),+$(,)?>)?, [$field1:ident$(, $field2:ident)*$(,)?]$(, )?$([$($type:ty),+$(,)?]$(,)?)?) => {
|
||||
impl_debug_eq_hash_for!(struct $name$(<$($gen$(: $bound)?),+>)?, [$field1$(, $field2)*], $([$($type),+])?);
|
||||
impl_clone_for!(struct $name$(<$($gen$(: $bound)?),+>)?, [$field1$(, $field2)*], $([$($type),+])?);
|
||||
impl_zeroize_on_drop_for!(struct $name$(<$($gen$(: $bound)?),+>)?, [$field1$(, $field2)*], $([$($type),+])?);
|
||||
impl_serialize_and_deserialize_for!($name);
|
||||
}
|
||||
}
|
||||
+398
-300
@@ -1,317 +1,352 @@
|
||||
// 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
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// This source code is dual-licensed under either the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
|
||||
//! An implementation of a verifiable oblivious pseudorandom function (VOPRF)
|
||||
//!
|
||||
//! Note: This implementation is in sync with
|
||||
//! [draft-irtf-cfrg-voprf-07](https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html),
|
||||
//! but this specification is subject to change, until the final version
|
||||
//! published by the IETF.
|
||||
//! [RFC 9497](https://www.rfc-editor.org/rfc/rfc9497).
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! A verifiable oblivious pseudorandom function is a protocol that is
|
||||
//! evaluated between a client and a server. They must first agree on a
|
||||
//! collection of primitives to be kept consistent throughout protocol
|
||||
//! execution. These include:
|
||||
//! - a finite cyclic group along with a point representation, and
|
||||
//! - a hashing function.
|
||||
//! A verifiable oblivious pseudorandom function is a protocol that is evaluated
|
||||
//! between a client and a server. They must first agree on a finite cyclic
|
||||
//! group along with a point representation.
|
||||
//!
|
||||
//! We will use the following choices in this example:
|
||||
//! We will use the following choice in this example:
|
||||
//!
|
||||
//! ```
|
||||
//! use voprf::CipherSuite;
|
||||
//! struct Default;
|
||||
//! impl CipherSuite for Default {
|
||||
//! type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! type Hash = sha2::Sha512;
|
||||
//! }
|
||||
//! ```ignore
|
||||
//! type CipherSuite = voprf::Ristretto255;
|
||||
//! ```
|
||||
//!
|
||||
//! ## Modes of Operation
|
||||
//!
|
||||
//! VOPRF can be used in two modes:
|
||||
//! - [Base Mode](#base-mode), which corresponds to a normal OPRF evaluation with no
|
||||
//! support for the verification of the OPRF outputs
|
||||
//! - [Verifiable Mode](#verifiable-mode), which corresponds to an OPRF evaluation where
|
||||
//! the outputs can be verified against a server public key
|
||||
//! VOPRF can be used in three modes:
|
||||
//! - [Base Mode](#base-mode), which corresponds to a normal OPRF evaluation
|
||||
//! with no support for the verification of the OPRF outputs
|
||||
//! - [Verifiable Mode](#verifiable-mode), which corresponds to an OPRF
|
||||
//! evaluation where the outputs can be verified against a server public key
|
||||
//! (VOPRF)
|
||||
//! - [Partially Oblivious Verifiable Mode](#metadata), which corresponds to a
|
||||
//! VOPRF, where a public input can be supplied to the PRF computation
|
||||
//!
|
||||
//! In either mode, the protocol begins with a client blinding, followed by
|
||||
//! a server evaluation, and finishes with a client finalization.
|
||||
//! In all of these modes, the protocol begins with a client blinding, followed
|
||||
//! by a server evaluation, and finishes with a client finalization and server
|
||||
//! evaluation.
|
||||
//!
|
||||
//! ## Base Mode
|
||||
//!
|
||||
//! In base mode, a [NonVerifiableClient] interacts with a
|
||||
//! [NonVerifiableServer] to compute the output of the VOPRF.
|
||||
//! In base mode, an [OprfClient] interacts with an [OprfServer] to compute the
|
||||
//! output of the OPRF.
|
||||
//!
|
||||
//! ### Server Setup
|
||||
//!
|
||||
//! The protocol begins with a setup phase, in which the server must run
|
||||
//! [NonVerifiableServer::new()] to produce an instance of itself. This
|
||||
//! instance must be persisted on the server and used for online
|
||||
//! client evaluations.
|
||||
//! [OprfServer::new()] to produce an instance of itself. This instance must be
|
||||
//! persisted on the server and used for online client evaluations.
|
||||
//!
|
||||
//! ```
|
||||
//! # use voprf::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # }
|
||||
//! use voprf::NonVerifiableServer;
|
||||
//! use rand::{rngs::OsRng, RngCore};
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! use rand::rngs::OsRng;
|
||||
//! use rand::RngCore;
|
||||
//! use voprf::OprfServer;
|
||||
//!
|
||||
//! let mut server_rng = OsRng;
|
||||
//! let server = NonVerifiableServer::<Default>::new(&mut server_rng)
|
||||
//! .expect("Unable to construct server");
|
||||
//! let server = OprfServer::<CipherSuite>::new(&mut server_rng);
|
||||
//! ```
|
||||
//!
|
||||
//! ### Client Blinding
|
||||
//!
|
||||
//! In the first step, the client chooses an input, and runs
|
||||
//! [NonVerifiableClient::blind] to produce a [NonVerifiableClientBlindResult],
|
||||
//! which consists of a [BlindedElement] to be sent to the server and a
|
||||
//! [NonVerifiableClient] which must be persisted on the client for the final
|
||||
//! step of the VOPRF protocol.
|
||||
//! In the first step, the client chooses an input, and runs [OprfClient::blind]
|
||||
//! to produce an [OprfClientBlindResult], which consists of a [BlindedElement]
|
||||
//! to be sent to the server and an [OprfClient] which must be persisted on the
|
||||
//! client for the final step of the VOPRF protocol.
|
||||
//!
|
||||
//! ```
|
||||
//! # use voprf::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # }
|
||||
//! use voprf::NonVerifiableClient;
|
||||
//! use rand::{rngs::OsRng, RngCore};
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! use rand::rngs::OsRng;
|
||||
//! use rand::RngCore;
|
||||
//! use voprf::OprfClient;
|
||||
//!
|
||||
//! let mut client_rng = OsRng;
|
||||
//! let client_blind_result = NonVerifiableClient::<Default>::blind(
|
||||
//! b"input",
|
||||
//! &mut client_rng,
|
||||
//! ).expect("Unable to construct client");
|
||||
//! let client_blind_result = OprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
|
||||
//! .expect("Unable to construct client");
|
||||
//! ```
|
||||
//!
|
||||
//! ### Server Evaluation
|
||||
//! ### Server Blind Evaluation
|
||||
//!
|
||||
//! In the second step, the server takes as input the message from
|
||||
//! [NonVerifiableClient::blind] (a [BlindedElement]), and runs
|
||||
//! [NonVerifiableServer::evaluate] to produce a
|
||||
//! [NonVerifiableServerEvaluateResult], which consists of an
|
||||
//! [EvaluationElement] to be sent to the client.
|
||||
//! [OprfClient::blind] (a [BlindedElement]), and runs
|
||||
//! [OprfServer::blind_evaluate] to produce [EvaluationElement] to be sent to
|
||||
//! the client.
|
||||
//!
|
||||
//! ```
|
||||
//! # use voprf::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # }
|
||||
//! # use voprf::NonVerifiableClient;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf::OprfClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_blind_result = NonVerifiableClient::<Default>::blind(
|
||||
//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
|
||||
//! # b"input",
|
||||
//! # &mut client_rng,
|
||||
//! # ).expect("Unable to construct client");
|
||||
//! # use voprf::NonVerifiableServer;
|
||||
//! # use voprf::OprfServer;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! # let server = NonVerifiableServer::<Default>::new(&mut server_rng)
|
||||
//! # .expect("Unable to construct server");
|
||||
//! use voprf::Metadata;
|
||||
//! let server_evaluate_result = server.evaluate(
|
||||
//! client_blind_result.message,
|
||||
//! &Metadata::none(),
|
||||
//! ).expect("Unable to perform server evaluate");
|
||||
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! let server_evaluate_result = server.blind_evaluate(&client_blind_result.message);
|
||||
//! ```
|
||||
//!
|
||||
//! ### Client Finalization
|
||||
//!
|
||||
//! In the final step, the client takes as input the message from
|
||||
//! [NonVerifiableServer::evaluate] (an [EvaluationElement]), and runs
|
||||
//! [NonVerifiableClient::finalize] to produce a
|
||||
//! [NonVerifiableClientFinalizeResult], which consists of an
|
||||
//! output for the protocol.
|
||||
//! In the final step on the client side, the client takes as input the message
|
||||
//! from [OprfServer::evaluate] (an [EvaluationElement]), and runs
|
||||
//! [OprfClient::finalize] to produce an output for the protocol.
|
||||
//!
|
||||
//! ```
|
||||
//! # use voprf::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # }
|
||||
//! # use voprf::NonVerifiableClient;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf::OprfClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_blind_result = NonVerifiableClient::<Default>::blind(
|
||||
//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
|
||||
//! # b"input",
|
||||
//! # &mut client_rng,
|
||||
//! # ).expect("Unable to construct client");
|
||||
//! # use voprf::NonVerifiableServer;
|
||||
//! # use voprf::OprfServer;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! # let server = NonVerifiableServer::<Default>::new(&mut server_rng)
|
||||
//! # .expect("Unable to construct server");
|
||||
//! # let server_evaluate_result = server.evaluate(
|
||||
//! # client_blind_result.message,
|
||||
//! # &Metadata::none(),
|
||||
//! # ).expect("Unable to perform server evaluate");
|
||||
//! use voprf::Metadata;
|
||||
//! let client_finalize_result = client_blind_result.state.finalize(
|
||||
//! server_evaluate_result.message,
|
||||
//! &Metadata::none(),
|
||||
//! ).expect("Unable to perform client finalization");
|
||||
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! # let message = server.blind_evaluate(&client_blind_result.message);
|
||||
//! let client_finalize_result = client_blind_result
|
||||
//! .state
|
||||
//! .finalize(b"input", &message)
|
||||
//! .expect("Unable to perform client finalization");
|
||||
//!
|
||||
//! println!("VOPRF output: {:?}", client_finalize_result.output.to_vec());
|
||||
//! println!("VOPRF output: {:?}", client_finalize_result.to_vec());
|
||||
//! ```
|
||||
//!
|
||||
//! ### Server Evaluation
|
||||
//!
|
||||
//! Optionally, if the server has direct access to the PRF input, then it need
|
||||
//! not perform the oblivious computation and can simply run
|
||||
//! [OprfServer::evaluate] to generate an output which matches the output
|
||||
//! produced by an execution of the oblivious protocol on the same input and
|
||||
//! key.
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf::OprfClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
|
||||
//! # b"input",
|
||||
//! # &mut client_rng,
|
||||
//! # ).expect("Unable to construct client");
|
||||
//! # use voprf::OprfServer;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! # let message = server.blind_evaluate(&client_blind_result.message);
|
||||
//! let client_finalize_result = client_blind_result
|
||||
//! .state
|
||||
//! .finalize(b"input", &message)
|
||||
//! .expect("Unable to perform client finalization");
|
||||
//!
|
||||
//! let server_evaluate_result = server
|
||||
//! .evaluate(b"input")
|
||||
//! .expect("Unable to perform the server evaluation");
|
||||
//!
|
||||
//! assert_eq!(client_finalize_result, server_evaluate_result);
|
||||
//! ```
|
||||
//!
|
||||
//! ## Verifiable Mode
|
||||
//!
|
||||
//! In verifiable mode, a [VerifiableClient] interacts with a
|
||||
//! [VerifiableServer] to compute the output of the VOPRF. In order to
|
||||
//! verify the server's computation, the client checks a server-generated
|
||||
//! proof against the server's public key. If the proof fails to verify,
|
||||
//! then the client does not receive an output.
|
||||
//! In verifiable mode, a [VoprfClient] interacts with a [VoprfServer] to
|
||||
//! compute the output of the VOPRF. In order to verify the server's
|
||||
//! computation, the client checks a server-generated proof against the server's
|
||||
//! public key. If the proof fails to verify, then the client does not receive
|
||||
//! an output.
|
||||
//!
|
||||
//! In batch mode, a single proof can be used for multiple VOPRF evaluations.
|
||||
//! See [the batching section](#batching)
|
||||
//! for more details on how to perform batch evaluations.
|
||||
//! See [the batching section](#batching) for more details on how to perform
|
||||
//! batch evaluations.
|
||||
//!
|
||||
//! ### Server Setup
|
||||
//!
|
||||
//! The protocol begins with a setup phase, in which the server must run
|
||||
//! [VerifiableServer::new()] to produce an instance of itself. This
|
||||
//! instance must be persisted on the server and used for online
|
||||
//! client evaluations.
|
||||
//! [VoprfServer::new()] to produce an instance of itself. This instance must be
|
||||
//! persisted on the server and used for online client evaluations.
|
||||
//!
|
||||
//! ```
|
||||
//! # use voprf::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # }
|
||||
//! use voprf::VerifiableServer;
|
||||
//! use rand::{rngs::OsRng, RngCore};
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! use rand::rngs::OsRng;
|
||||
//! use rand::RngCore;
|
||||
//! use voprf::VoprfServer;
|
||||
//!
|
||||
//! let mut server_rng = OsRng;
|
||||
//! let server = VerifiableServer::<Default>::new(&mut server_rng)
|
||||
//! .expect("Unable to construct server");
|
||||
//! let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//!
|
||||
//! // To be sent to the client
|
||||
//! println!("Server public key: {:?}", server.get_public_key());
|
||||
//! ```
|
||||
//!
|
||||
//! The public key should be sent to the client, since the client will
|
||||
//! need it in the final step of the protocol in order to complete
|
||||
//! the evaluation of the VOPRF.
|
||||
//! The public key should be sent to the client, since the client will need it
|
||||
//! in the final step of the protocol in order to complete the evaluation of the
|
||||
//! VOPRF.
|
||||
//!
|
||||
//! ### Client Blinding
|
||||
//!
|
||||
//! In the first step, the client chooses an input, and runs
|
||||
//! [VerifiableClient::blind] to produce a [VerifiableClientBlindResult],
|
||||
//! which consists of a [BlindedElement] to be sent to the server and a
|
||||
//! [VerifiableClient] which must be persisted on the client for the final
|
||||
//! step of the VOPRF protocol.
|
||||
//! [VoprfClient::blind] to produce a [VoprfClientBlindResult], which consists
|
||||
//! of a [BlindedElement] to be sent to the server and a [VoprfClient] which
|
||||
//! must be persisted on the client for the final step of the VOPRF protocol.
|
||||
//!
|
||||
//! ```
|
||||
//! # use voprf::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # }
|
||||
//! use voprf::VerifiableClient;
|
||||
//! use rand::{rngs::OsRng, RngCore};
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! use rand::rngs::OsRng;
|
||||
//! use rand::RngCore;
|
||||
//! use voprf::VoprfClient;
|
||||
//!
|
||||
//! let mut client_rng = OsRng;
|
||||
//! let client_blind_result = VerifiableClient::<Default>::blind(
|
||||
//! b"input",
|
||||
//! &mut client_rng,
|
||||
//! ).expect("Unable to construct client");
|
||||
//! let client_blind_result = VoprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
|
||||
//! .expect("Unable to construct client");
|
||||
//! ```
|
||||
//!
|
||||
//! ### Server Evaluation
|
||||
//! ### Server Blind Evaluation
|
||||
//!
|
||||
//! In the second step, the server takes as input the message from
|
||||
//! [VerifiableClient::blind] (a [BlindedElement]), and runs
|
||||
//! [VerifiableServer::evaluate] to produce a
|
||||
//! [VerifiableServerEvaluateResult], which consists of an
|
||||
//! [EvaluationElement] to be sent to the client along with a proof.
|
||||
//! [VoprfClient::blind] (a [BlindedElement]), and runs
|
||||
//! [VoprfServer::blind_evaluate] to produce a [VoprfServerEvaluateResult],
|
||||
//! which consists of an [EvaluationElement] to be sent to the client along with
|
||||
//! a proof.
|
||||
//!
|
||||
//! ```
|
||||
//! # use voprf::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # }
|
||||
//! # use voprf::VerifiableClient;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf::{VoprfServerEvaluateResult, VoprfClient};
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_blind_result = VerifiableClient::<Default>::blind(
|
||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
|
||||
//! # b"input",
|
||||
//! # &mut client_rng,
|
||||
//! # ).expect("Unable to construct client");
|
||||
//! # use voprf::VerifiableServer;
|
||||
//! # use voprf::VoprfServer;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! # let server = VerifiableServer::<Default>::new(&mut server_rng)
|
||||
//! # .expect("Unable to construct server");
|
||||
//! use voprf::Metadata;
|
||||
//! let server_evaluate_result = server.evaluate(
|
||||
//! &mut server_rng,
|
||||
//! client_blind_result.message,
|
||||
//! &Metadata::none(),
|
||||
//! ).expect("Unable to perform server evaluate");
|
||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! let VoprfServerEvaluateResult { message, proof } =
|
||||
//! server.blind_evaluate(&mut server_rng, &client_blind_result.message);
|
||||
//! ```
|
||||
//!
|
||||
//! ### Client Finalization
|
||||
//!
|
||||
//! In the final step, the client takes as input the message from
|
||||
//! [VerifiableServer::evaluate] (an [EvaluationElement]),
|
||||
//! the proof, and the server's public key, and runs
|
||||
//! [VerifiableClient::finalize] to produce a
|
||||
//! [VerifiableClientFinalizeResult], which consists of an
|
||||
//! output for the protocol.
|
||||
//! [VoprfServer::blind_evaluate] (an [EvaluationElement]), the proof, and the
|
||||
//! server's public key, and runs [VoprfClient::finalize] to produce an output
|
||||
//! for the protocol.
|
||||
//!
|
||||
//! ```
|
||||
//! # use voprf::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # }
|
||||
//! # use voprf::VerifiableClient;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf::VoprfClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_blind_result = VerifiableClient::<Default>::blind(
|
||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
|
||||
//! # b"input",
|
||||
//! # &mut client_rng,
|
||||
//! # ).expect("Unable to construct client");
|
||||
//! # use voprf::VerifiableServer;
|
||||
//! # use voprf::VoprfServer;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! # let server = VerifiableServer::<Default>::new(&mut server_rng)
|
||||
//! # .expect("Unable to construct server");
|
||||
//! # let server_evaluate_result = server.evaluate(
|
||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! # let server_evaluate_result = server.blind_evaluate(
|
||||
//! # &mut server_rng,
|
||||
//! # client_blind_result.message,
|
||||
//! # &Metadata::none(),
|
||||
//! # ).expect("Unable to perform server evaluate");
|
||||
//! use voprf::Metadata;
|
||||
//! let client_finalize_result = client_blind_result.state.finalize(
|
||||
//! server_evaluate_result.message,
|
||||
//! server_evaluate_result.proof,
|
||||
//! # &client_blind_result.message,
|
||||
//! # );
|
||||
//! let client_finalize_result = client_blind_result
|
||||
//! .state
|
||||
//! .finalize(
|
||||
//! b"input",
|
||||
//! &server_evaluate_result.message,
|
||||
//! &server_evaluate_result.proof,
|
||||
//! server.get_public_key(),
|
||||
//! &Metadata::none(),
|
||||
//! ).expect("Unable to perform client finalization");
|
||||
//! )
|
||||
//! .expect("Unable to perform client finalization");
|
||||
//!
|
||||
//! println!("VOPRF output: {:?}", client_finalize_result.output.to_vec());
|
||||
//! println!("VOPRF output: {:?}", client_finalize_result.to_vec());
|
||||
//! ```
|
||||
//!
|
||||
//! ### Server Evaluation
|
||||
//!
|
||||
//! Optionally, if the server has direct access to the PRF input, then it need
|
||||
//! not perform the oblivious computation and can simply run
|
||||
//! [VoprfServer::evaluate] to generate an output which matches the output
|
||||
//! produced by an execution of the oblivious protocol on the same input and
|
||||
//! key.
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf::VoprfClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
|
||||
//! # b"input",
|
||||
//! # &mut client_rng,
|
||||
//! # ).expect("Unable to construct client");
|
||||
//! # use voprf::VoprfServer;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! # let server_evaluate_result = server.blind_evaluate(
|
||||
//! # &mut server_rng,
|
||||
//! # &client_blind_result.message,
|
||||
//! # );
|
||||
//! let client_finalize_result = client_blind_result
|
||||
//! .state
|
||||
//! .finalize(
|
||||
//! b"input",
|
||||
//! &server_evaluate_result.message,
|
||||
//! &server_evaluate_result.proof,
|
||||
//! server.get_public_key(),
|
||||
//! )
|
||||
//! .expect("Unable to perform client finalization");
|
||||
//!
|
||||
//! let server_evaluate_result = server
|
||||
//! .evaluate(b"input")
|
||||
//! .expect("Unable to perform the server evaluation");
|
||||
//!
|
||||
//! assert_eq!(client_finalize_result, server_evaluate_result);
|
||||
//! ```
|
||||
//!
|
||||
//! # Advanced Usage
|
||||
@@ -322,167 +357,210 @@
|
||||
//!
|
||||
//! ## Batching
|
||||
//!
|
||||
//! It is sometimes desirable to generate only a single, constant-size
|
||||
//! proof for an unbounded number of VOPRF evaluations (on arbitrary inputs).
|
||||
//! [VerifiableClient] and [VerifiableServer] support a batch API for
|
||||
//! handling this case. In the following example, we show how to use
|
||||
//! the batch API to produce a single proof for 10 parallel
|
||||
//! VOPRF evaluations.
|
||||
//! It is sometimes desirable to generate only a single, constant-size proof for
|
||||
//! an unbounded number of VOPRF evaluations (on arbitrary inputs).
|
||||
//! [VoprfClient] and [VoprfServer] support a batch API for handling this case.
|
||||
//! In the following example, we show how to use the batch API to produce a
|
||||
//! single proof for 10 parallel VOPRF evaluations.
|
||||
//!
|
||||
//! First, the client produces 10 blindings, storing their resulting
|
||||
//! states and messages:
|
||||
//! First, the client produces 10 blindings, storing their resulting states and
|
||||
//! messages:
|
||||
//!
|
||||
//! ```
|
||||
//! # use voprf::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # }
|
||||
//! # use voprf::VerifiableClient;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf::VoprfClient;
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! let mut client_rng = OsRng;
|
||||
//! let mut client_states = vec![];
|
||||
//! let mut client_messages = vec![];
|
||||
//! for _ in 0..10 {
|
||||
//! let client_blind_result = VerifiableClient::<Default>::blind(
|
||||
//! b"input",
|
||||
//! &mut client_rng,
|
||||
//! ).expect("Unable to construct client");
|
||||
//! let client_blind_result = VoprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
|
||||
//! .expect("Unable to construct client");
|
||||
//! client_states.push(client_blind_result.state);
|
||||
//! client_messages.push(client_blind_result.message);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Next, the server calls the [VerifiableServer::batch_evaluate]
|
||||
//! function on a set of client messages, to produce a corresponding
|
||||
//! set of messages to be returned to the client (returned in the same order),
|
||||
//! along with a single proof:
|
||||
//! Next, the server calls the [VoprfServer::batch_blind_evaluate_prepare] and
|
||||
//! [VoprfServer::batch_blind_evaluate_finish] function on a set of client
|
||||
//! messages, to produce a corresponding set of messages to be returned to the
|
||||
//! client (returned in the same order), along with a single proof:
|
||||
//!
|
||||
//! ```
|
||||
//! # use voprf::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # }
|
||||
//! # use voprf::VerifiableClient;
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf::{VoprfServerBatchEvaluateFinishResult, VoprfClient};
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let mut client_states = vec![];
|
||||
//! # let mut client_messages = vec![];
|
||||
//! # for _ in 0..10 {
|
||||
//! # let client_blind_result = VerifiableClient::<Default>::blind(
|
||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
|
||||
//! # b"input",
|
||||
//! # &mut client_rng,
|
||||
//! # ).expect("Unable to construct client");
|
||||
//! # client_states.push(client_blind_result.state);
|
||||
//! # client_messages.push(client_blind_result.message);
|
||||
//! # }
|
||||
//! # use voprf::Metadata;
|
||||
//! # use voprf::VerifiableServer;
|
||||
//! # use voprf::VoprfServer;
|
||||
//! let mut server_rng = OsRng;
|
||||
//! # let server = VerifiableServer::<Default>::new(&mut server_rng)
|
||||
//! # .expect("Unable to construct server");
|
||||
//! let server_batch_evaluate_result = server.batch_evaluate(
|
||||
//! &mut server_rng,
|
||||
//! &client_messages,
|
||||
//! &Metadata::none(),
|
||||
//! ).expect("Unable to perform server batch evaluate");
|
||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! let prepared_evaluation_elements = server.batch_blind_evaluate_prepare(client_messages.iter());
|
||||
//! let prepared_elements: Vec<_> = prepared_evaluation_elements.collect();
|
||||
//! let VoprfServerBatchEvaluateFinishResult { messages, proof } = server
|
||||
//! .batch_blind_evaluate_finish(&mut server_rng, client_messages.iter(), &prepared_elements)
|
||||
//! .expect("Unable to perform server batch evaluate");
|
||||
//! let messages: Vec<_> = messages.collect();
|
||||
//! ```
|
||||
//!
|
||||
//! Then, the client calls [VerifiableClient::batch_finalize] on
|
||||
//! the client states saved from the first step, along with the messages
|
||||
//! returned by the server (constructing a [BatchFinalizeInput]), along with the
|
||||
//! server's proof, in order to produce a vector of outputs if the proof
|
||||
//! verifies correctly.
|
||||
//! If `alloc` is available, `VoprfServer::batch_blind_evaluate` can be called
|
||||
//! to avoid having to collect output manually:
|
||||
//!
|
||||
//! ```
|
||||
//! # use voprf::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type Hash = sha2::Sha512;
|
||||
//! # }
|
||||
//! # use voprf::VerifiableClient;
|
||||
//! # #[cfg(feature = "alloc")] {
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf::{VoprfServerBatchEvaluateResult, VoprfClient};
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let mut client_states = vec![];
|
||||
//! # let mut client_messages = vec![];
|
||||
//! # for _ in 0..10 {
|
||||
//! # let client_blind_result = VerifiableClient::<Default>::blind(
|
||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
|
||||
//! # b"input",
|
||||
//! # &mut client_rng,
|
||||
//! # ).expect("Unable to construct client");
|
||||
//! # client_states.push(client_blind_result.state);
|
||||
//! # client_messages.push(client_blind_result.message);
|
||||
//! # }
|
||||
//! # use voprf::Metadata;
|
||||
//! # use voprf::VerifiableServer;
|
||||
//! use voprf::BatchFinalizeInput;
|
||||
//! # use voprf::VoprfServer;
|
||||
//! let mut server_rng = OsRng;
|
||||
//! # let server = VerifiableServer::<Default>::new(&mut server_rng)
|
||||
//! # .expect("Unable to construct server");
|
||||
//! # let server_batch_evaluate_result = server.batch_evaluate(
|
||||
//! # &mut server_rng,
|
||||
//! # &client_messages,
|
||||
//! # &Metadata::none(),
|
||||
//! # ).expect("Unable to perform server batch evaluate");
|
||||
//! let batch_finalize_input = BatchFinalizeInput::new(
|
||||
//! client_states,
|
||||
//! server_batch_evaluate_result.messages,
|
||||
//! );
|
||||
//! let client_batch_finalize_result = VerifiableClient::batch_finalize(
|
||||
//! batch_finalize_input,
|
||||
//! server_batch_evaluate_result.proof,
|
||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! let VoprfServerBatchEvaluateResult { messages, proof } = server
|
||||
//! .batch_blind_evaluate(&mut server_rng, &client_messages)
|
||||
//! .expect("Unable to perform server batch evaluate");
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! Then, the client calls [VoprfClient::batch_finalize] on the client states
|
||||
//! saved from the first step, along with the messages returned by the server,
|
||||
//! along with the server's proof, in order to produce a vector of outputs if
|
||||
//! the proof verifies correctly.
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "alloc")] {
|
||||
//! # #[cfg(feature = "ristretto255")]
|
||||
//! # type CipherSuite = voprf::Ristretto255;
|
||||
//! # #[cfg(not(feature = "ristretto255"))]
|
||||
//! # type CipherSuite = p256::NistP256;
|
||||
//! # use voprf::{VoprfServerBatchEvaluateResult, VoprfClient};
|
||||
//! # use rand::{rngs::OsRng, RngCore};
|
||||
//! #
|
||||
//! # let mut client_rng = OsRng;
|
||||
//! # let mut client_states = vec![];
|
||||
//! # let mut client_messages = vec![];
|
||||
//! # for _ in 0..10 {
|
||||
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
|
||||
//! # b"input",
|
||||
//! # &mut client_rng,
|
||||
//! # ).expect("Unable to construct client");
|
||||
//! # client_states.push(client_blind_result.state);
|
||||
//! # client_messages.push(client_blind_result.message);
|
||||
//! # }
|
||||
//! # use voprf::VoprfServer;
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
|
||||
//! # let VoprfServerBatchEvaluateResult { messages, proof } = server
|
||||
//! # .batch_blind_evaluate(&mut server_rng, &client_messages)
|
||||
//! # .expect("Unable to perform server batch evaluate");
|
||||
//! let client_batch_finalize_result = VoprfClient::batch_finalize(
|
||||
//! &[b"input"; 10],
|
||||
//! &client_states,
|
||||
//! &messages,
|
||||
//! &proof,
|
||||
//! server.get_public_key(),
|
||||
//! &Metadata::none(),
|
||||
//! ).expect("Unable to perform client batch finalization");
|
||||
//! )
|
||||
//! .expect("Unable to perform client batch finalization")
|
||||
//! .collect::<Vec<_>>();
|
||||
//!
|
||||
//! println!("VOPRF batch outputs: {:?}", client_batch_finalize_result.outputs);
|
||||
//! println!("VOPRF batch outputs: {:?}", client_batch_finalize_result);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Metadata
|
||||
//!
|
||||
//! The optional metadata parameter included in the protocol allows clients and
|
||||
//! servers (of either mode) to cryptographically bind additional data to the
|
||||
//! VOPRF output. This metadata is known to both parties at the start of the protocol,
|
||||
//! and is inserted under the server's evaluate step and the client's finalize step.
|
||||
//! This metadata can be constructed with some type of higher-level domain separation
|
||||
//! to avoid cross-protocol attacks or related issues.
|
||||
//! The optional metadata parameter included in the POPRF mode allows clients
|
||||
//! and servers to cryptographically bind additional data to the VOPRF output.
|
||||
//! This metadata is known to both parties at the start of the protocol, and is
|
||||
//! inserted under the server's blind evaluate step and the client's finalize
|
||||
//! step. This metadata can be constructed with some type of higher-level domain
|
||||
//! separation to avoid cross-protocol attacks or related issues.
|
||||
//!
|
||||
//! The default metadata simply consists of the empty vector of bytes, but a custom
|
||||
//! metadata can be specified, for example, by: `Metadata(b"custom metadata")`.
|
||||
//! The API for POPRF mode is similar to VOPRF mode, except that a [PoprfServer]
|
||||
//! and [PoprfClient] are used, and that each of the functions accept an
|
||||
//! additional (and optional) info parameter which represents the public input.
|
||||
//! See
|
||||
//! <https://www.rfc-editor.org/rfc/rfc9497#name-poprf-public-input>
|
||||
//! for more detailed information on how this public input should be used.
|
||||
//!
|
||||
//! # Features
|
||||
//!
|
||||
//! - The `p256` feature enables using p256 as the underlying group for the [CipherSuite] choice.
|
||||
//! Note that this is currently an experimental feature ⚠️, and is not yet ready for production use.
|
||||
//! - The `alloc` feature requires Rust's `alloc` crate and enables batching
|
||||
//! VOPRF evaluations.
|
||||
//!
|
||||
//! - The `serialize` feature, enabled by default, provides convenience functions for serializing and deserializing with
|
||||
//! [serde](https://serde.rs/).
|
||||
//! - The `serde` feature, enabled by default, provides convenience functions
|
||||
//! for serializing and deserializing with [serde](https://serde.rs/).
|
||||
//!
|
||||
//! - The `u32_backend` and `u64_backend` features are re-exported from
|
||||
//! [curve25519-dalek](https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features) and allow for selecting
|
||||
//! the corresponding backend for the curve arithmetic used. The `u64_backend` feature is included as the default.
|
||||
//! - The `danger` feature, disabled by default, exposes functions for setting
|
||||
//! and getting internal values not available in the default API. These
|
||||
//! functions are intended for use in by higher-level cryptographic protocols
|
||||
//! that need access to these raw values and are able to perform the necessary
|
||||
//! validations on them (such as being valid group elements).
|
||||
//!
|
||||
//! - The `ristretto255-ciphersuite` features enables using [`Ristretto255`] as
|
||||
//! a [`CipherSuite`].
|
||||
//!
|
||||
//! - The `ristretto255` feature enables using [`Ristretto255`] as the
|
||||
//! underlying group for the [Group] choice. To select a specific backend see
|
||||
//! the [curve25519-dalek] documentation.
|
||||
//!
|
||||
//! [curve25519-dalek]:
|
||||
//! (https://docs.rs/curve25519-dalek/4.0.0-pre.5/curve25519_dalek/index.html#backends)
|
||||
|
||||
#![cfg_attr(not(feature = "bench"), deny(missing_docs))]
|
||||
#![deny(unsafe_code)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![no_std]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(not(test), deny(unsafe_code))]
|
||||
#![warn(
|
||||
clippy::cargo,
|
||||
clippy::missing_errors_doc,
|
||||
missing_debug_implementations,
|
||||
missing_docs
|
||||
)]
|
||||
#![allow(clippy::multiple_crate_versions)]
|
||||
|
||||
#[cfg(any(feature = "alloc", test))]
|
||||
extern crate alloc;
|
||||
|
||||
#[macro_use]
|
||||
mod impls;
|
||||
#[macro_use]
|
||||
mod serialization;
|
||||
#[cfg(feature = "std")]
|
||||
extern crate std;
|
||||
|
||||
mod ciphersuite;
|
||||
pub mod errors;
|
||||
pub mod group;
|
||||
pub mod hash;
|
||||
mod common;
|
||||
mod error;
|
||||
mod group;
|
||||
mod oprf;
|
||||
mod poprf;
|
||||
mod serialization;
|
||||
mod voprf;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -490,12 +568,32 @@ mod tests;
|
||||
|
||||
// Exports
|
||||
|
||||
pub use rand;
|
||||
|
||||
pub use crate::ciphersuite::CipherSuite;
|
||||
pub use crate::voprf::{
|
||||
BatchFinalizeInput, BlindedElement, EvaluationElement, Metadata, NonVerifiableClient,
|
||||
NonVerifiableClientBlindResult, NonVerifiableClientFinalizeResult, NonVerifiableServer,
|
||||
NonVerifiableServerEvaluateResult, VerifiableClient, VerifiableClientBlindResult,
|
||||
VerifiableClientFinalizeResult, VerifiableServer, VerifiableServerEvaluateResult,
|
||||
#[cfg(feature = "danger")]
|
||||
pub use crate::common::derive_key;
|
||||
pub use crate::common::{
|
||||
BlindedElement, EvaluationElement, Mode, PreparedEvaluationElement, Proof,
|
||||
};
|
||||
pub use crate::error::{Error, InternalError, Result};
|
||||
pub use crate::group::Group;
|
||||
#[cfg(feature = "ristretto255")]
|
||||
pub use crate::group::Ristretto255;
|
||||
pub use crate::oprf::{OprfClient, OprfClientBlindResult, OprfServer};
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use crate::poprf::PoprfServerBatchEvaluateResult;
|
||||
pub use crate::poprf::{
|
||||
PoprfClient, PoprfClientBatchFinalizeResult, PoprfPreparedTweak, PoprfServer,
|
||||
PoprfServerBatchEvaluateFinishResult, PoprfServerBatchEvaluateFinishedMessages,
|
||||
PoprfServerBatchEvaluatePrepareResult, PoprfServerBatchEvaluatePreparedEvaluationElements,
|
||||
};
|
||||
pub use crate::serialization::{
|
||||
BlindedElementLen, EvaluationElementLen, OprfClientLen, OprfServerLen, PoprfClientLen,
|
||||
PoprfServerLen, ProofLen, VoprfClientLen, VoprfServerLen,
|
||||
};
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use crate::voprf::VoprfServerBatchEvaluateResult;
|
||||
pub use crate::voprf::{
|
||||
VoprfClient, VoprfClientBatchFinalizeResult, VoprfClientBlindResult, VoprfServer,
|
||||
VoprfServerBatchEvaluateFinishResult, VoprfServerBatchEvaluateFinishedMessages,
|
||||
VoprfServerBatchEvaluatePreparedEvaluationElements, VoprfServerEvaluateResult,
|
||||
};
|
||||
|
||||
+419
@@ -0,0 +1,419 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
//
|
||||
// This source code is dual-licensed under either the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
|
||||
//! Contains the main OPRF API
|
||||
|
||||
use core::iter::{self, Map};
|
||||
|
||||
use derive_where::derive_where;
|
||||
use digest::{Digest, Output};
|
||||
use generic_array::typenum::Unsigned;
|
||||
use generic_array::GenericArray;
|
||||
use rand_core::{TryCryptoRng, TryRngCore};
|
||||
|
||||
use crate::common::{
|
||||
derive_key_internal, deterministic_blind_unchecked, hash_to_group, i2osp_2,
|
||||
server_evaluate_hash_input, BlindedElement, EvaluationElement, Mode, STR_FINALIZE,
|
||||
};
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::serialization::serde::Scalar;
|
||||
use crate::{CipherSuite, Error, Group, Result};
|
||||
|
||||
///////////////
|
||||
// Constants //
|
||||
// ========= //
|
||||
///////////////
|
||||
|
||||
////////////////////////////
|
||||
// High-level API Structs //
|
||||
// ====================== //
|
||||
////////////////////////////
|
||||
|
||||
/// A client which engages with a [OprfServer] in base mode, meaning
|
||||
/// that the OPRF outputs are not verifiable.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct OprfClient<CS: CipherSuite> {
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) blind: <CS::Group as Group>::Scalar,
|
||||
}
|
||||
|
||||
/// A server which engages with a [OprfClient] in base mode, meaning
|
||||
/// that the OPRF outputs are not verifiable.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct OprfServer<CS: CipherSuite> {
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) sk: <CS::Group as Group>::Scalar,
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// API Implementations //
|
||||
// =================== //
|
||||
/////////////////////////
|
||||
|
||||
impl<CS: CipherSuite> OprfClient<CS> {
|
||||
/// Computes the first step for the multiplicative blinding version of
|
||||
/// DH-OPRF.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`].
|
||||
pub fn blind<R: TryRngCore + TryCryptoRng>(
|
||||
input: &[u8],
|
||||
blinding_factor_rng: &mut R,
|
||||
) -> Result<OprfClientBlindResult<CS>> {
|
||||
let blind = CS::Group::random_scalar(blinding_factor_rng)?;
|
||||
Self::deterministic_blind_unchecked_inner(input, blind)
|
||||
}
|
||||
|
||||
/// Computes the first step for the multiplicative blinding version of
|
||||
/// DH-OPRF, taking a blinding factor scalar as input instead of sampling
|
||||
/// from an RNG.
|
||||
///
|
||||
/// # Caution
|
||||
///
|
||||
/// This should be used with caution, since it does not perform any checks
|
||||
/// on the validity of the blinding factor!
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`].
|
||||
#[cfg(any(feature = "danger", test))]
|
||||
pub fn deterministic_blind_unchecked(
|
||||
input: &[u8],
|
||||
blind: <CS::Group as Group>::Scalar,
|
||||
) -> Result<OprfClientBlindResult<CS>> {
|
||||
Self::deterministic_blind_unchecked_inner(input, blind)
|
||||
}
|
||||
|
||||
/// Can only fail with [`Error::Input`].
|
||||
fn deterministic_blind_unchecked_inner(
|
||||
input: &[u8],
|
||||
blind: <CS::Group as Group>::Scalar,
|
||||
) -> Result<OprfClientBlindResult<CS>> {
|
||||
let blinded_element = deterministic_blind_unchecked::<CS>(input, &blind, Mode::Oprf)?;
|
||||
Ok(OprfClientBlindResult {
|
||||
state: Self { blind },
|
||||
message: BlindedElement(blinded_element),
|
||||
})
|
||||
}
|
||||
|
||||
/// Computes the third step for the multiplicative blinding version of
|
||||
/// DH-OPRF, in which the client unblinds the server's message.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`].
|
||||
pub fn finalize(
|
||||
&self,
|
||||
input: &[u8],
|
||||
evaluation_element: &EvaluationElement<CS>,
|
||||
) -> Result<Output<CS::Hash>> {
|
||||
let unblinded_element = evaluation_element.0 * &CS::Group::invert_scalar(self.blind);
|
||||
let mut outputs =
|
||||
finalize_after_unblind::<CS, _, _>(iter::once((input, unblinded_element)), &[]);
|
||||
outputs.next().unwrap()
|
||||
}
|
||||
|
||||
/// Only used for test functions
|
||||
#[cfg(test)]
|
||||
pub fn from_blind(blind: <CS::Group as Group>::Scalar) -> Self {
|
||||
Self { blind }
|
||||
}
|
||||
|
||||
/// Exposes the blind group element
|
||||
#[cfg(feature = "danger")]
|
||||
pub fn get_blind(&self) -> <CS::Group as Group>::Scalar {
|
||||
self.blind
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> OprfServer<CS> {
|
||||
/// Produces a new instance of a [OprfServer] using a supplied RNG
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
pub fn new<R: TryRngCore + TryCryptoRng>(rng: &mut R) -> Result<Self> {
|
||||
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
|
||||
rng.try_fill_bytes(&mut seed).map_err(|_| Error::Protocol)?;
|
||||
Self::new_from_seed(&seed, &[])
|
||||
}
|
||||
|
||||
/// Produces a new instance of a [OprfServer] using a supplied set
|
||||
/// of bytes to represent the server's private key
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Deserialization`] if the private key is not a valid point on
|
||||
/// the group or zero.
|
||||
pub fn new_with_key(private_key_bytes: &[u8]) -> Result<Self> {
|
||||
let sk = CS::Group::deserialize_scalar(private_key_bytes)?;
|
||||
Ok(Self { sk })
|
||||
}
|
||||
|
||||
/// Produces a new instance of a [OprfServer] using a supplied set
|
||||
/// of bytes which are used as a seed to derive the server's private key.
|
||||
///
|
||||
/// Corresponds to DeriveKeyPair() function from the VOPRF specification.
|
||||
///
|
||||
/// # Errors
|
||||
/// - [`Error::DeriveKeyPair`] if the `input` and `seed` together are longer
|
||||
/// then `u16::MAX - 3`.
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
pub fn new_from_seed(seed: &[u8], info: &[u8]) -> Result<Self> {
|
||||
let sk = derive_key_internal::<CS>(seed, info, Mode::Oprf)?;
|
||||
Ok(Self { sk })
|
||||
}
|
||||
|
||||
/// Only used for tests
|
||||
#[cfg(test)]
|
||||
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
|
||||
self.sk
|
||||
}
|
||||
|
||||
/// Computes the second step for the multiplicative blinding version of
|
||||
/// DH-OPRF. This message is sent from the server (who holds the OPRF key)
|
||||
/// to the client.
|
||||
pub fn blind_evaluate(&self, blinded_element: &BlindedElement<CS>) -> EvaluationElement<CS> {
|
||||
EvaluationElement(blinded_element.0 * &self.sk)
|
||||
}
|
||||
|
||||
/// Computes the output of the OPRF on the server side
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Input`] if the `input` is longer then [`u16::MAX`].
|
||||
pub fn evaluate(&self, input: &[u8]) -> Result<Output<<CS as CipherSuite>::Hash>> {
|
||||
let input_element = hash_to_group::<CS>(input, Mode::Oprf)?;
|
||||
if CS::Group::is_identity_elem(input_element).into() {
|
||||
return Err(Error::Input);
|
||||
};
|
||||
let evaluated_element = input_element * &self.sk;
|
||||
|
||||
let issued_element = CS::Group::serialize_elem(evaluated_element);
|
||||
|
||||
server_evaluate_hash_input::<CS>(input, None, issued_element)
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// Convenience Structs //
|
||||
//==================== //
|
||||
/////////////////////////
|
||||
|
||||
/// Contains the fields that are returned by a non-verifiable client blind
|
||||
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||
pub struct OprfClientBlindResult<CS: CipherSuite> {
|
||||
/// The state to be persisted on the client
|
||||
pub state: OprfClient<CS>,
|
||||
/// The message to send to the server
|
||||
pub message: BlindedElement<CS>,
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// 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<'a, CS, I, IE> {
|
||||
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 //
|
||||
// ===== //
|
||||
///////////
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::ptr;
|
||||
|
||||
use rand::rngs::OsRng;
|
||||
use rand::TryRngCore;
|
||||
|
||||
use super::*;
|
||||
use crate::common::{Dst, STR_HASH_TO_GROUP};
|
||||
use crate::Group;
|
||||
|
||||
fn prf<CS: CipherSuite>(
|
||||
input: &[u8],
|
||||
key: <CS::Group as Group>::Scalar,
|
||||
info: &[u8],
|
||||
mode: Mode,
|
||||
) -> Output<CS::Hash> {
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, mode);
|
||||
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)), info)
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn base_retrieval<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
let server = OprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let message = server.blind_evaluate(&client_blind_result.message);
|
||||
let client_finalize_result = client_blind_result.state.finalize(input, &message).unwrap();
|
||||
let res2 = prf::<CS>(input, server.get_private_key(), &[], Mode::Oprf);
|
||||
assert_eq!(client_finalize_result, res2);
|
||||
}
|
||||
|
||||
fn base_inversion_unsalted<CS: CipherSuite>() {
|
||||
let mut rng = OsRng;
|
||||
let mut input = [0u8; 64];
|
||||
rng.try_fill_bytes(&mut input).unwrap();
|
||||
let client_blind_result = OprfClient::<CS>::blind(&input, &mut rng).unwrap();
|
||||
let client_finalize_result = client_blind_result
|
||||
.state
|
||||
.finalize(&input, &EvaluationElement(client_blind_result.message.0))
|
||||
.unwrap();
|
||||
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, Mode::Oprf);
|
||||
let point = CS::Group::hash_to_curve::<CS::Hash>(&[&input], &dst.as_dst()).unwrap();
|
||||
let res2 = finalize_after_unblind::<CS, _, _>(iter::once((input.as_ref(), point)), &[])
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(client_finalize_result, res2);
|
||||
}
|
||||
|
||||
fn server_evaluate<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
let server = OprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let server_result = server.blind_evaluate(&client_blind_result.message);
|
||||
|
||||
let client_finalize = client_blind_result
|
||||
.state
|
||||
.finalize(input, &server_result)
|
||||
.unwrap();
|
||||
|
||||
// We expect the outputs from client and server to be equal given an identical
|
||||
// input
|
||||
let server_evaluate = server.evaluate(input).unwrap();
|
||||
assert_eq!(client_finalize, server_evaluate);
|
||||
|
||||
// We expect the outputs from client and server to be different given different
|
||||
// inputs
|
||||
let wrong_input = b"wrong input";
|
||||
let server_evaluate = server.evaluate(wrong_input).unwrap();
|
||||
assert!(client_finalize != server_evaluate);
|
||||
}
|
||||
|
||||
fn zeroize_oprf_client<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
|
||||
let mut state = client_blind_result.state;
|
||||
unsafe { ptr::drop_in_place(&mut state) };
|
||||
assert!(state.serialize().iter().all(|&x| x == 0));
|
||||
|
||||
let mut message = client_blind_result.message;
|
||||
unsafe { ptr::drop_in_place(&mut message) };
|
||||
assert!(message.serialize().iter().all(|&x| x == 0));
|
||||
}
|
||||
|
||||
fn zeroize_oprf_server<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
let server = OprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let mut message = server.blind_evaluate(&client_blind_result.message);
|
||||
|
||||
let mut state = server;
|
||||
unsafe { ptr::drop_in_place(&mut state) };
|
||||
assert!(state.serialize().iter().all(|&x| x == 0));
|
||||
|
||||
unsafe { ptr::drop_in_place(&mut message) };
|
||||
assert!(message.serialize().iter().all(|&x| x == 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_functionality() -> Result<()> {
|
||||
use p256::NistP256;
|
||||
use p384::NistP384;
|
||||
use p521::NistP521;
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
{
|
||||
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>();
|
||||
|
||||
base_retrieval::<NistP384>();
|
||||
base_inversion_unsalted::<NistP384>();
|
||||
server_evaluate::<NistP384>();
|
||||
|
||||
zeroize_oprf_client::<NistP384>();
|
||||
zeroize_oprf_server::<NistP384>();
|
||||
|
||||
base_retrieval::<NistP521>();
|
||||
base_inversion_unsalted::<NistP521>();
|
||||
server_evaluate::<NistP521>();
|
||||
|
||||
zeroize_oprf_client::<NistP521>();
|
||||
zeroize_oprf_server::<NistP521>();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+921
@@ -0,0 +1,921 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
//
|
||||
// This source code is dual-licensed under either the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
|
||||
//! Contains the main POPRF API
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::vec::Vec;
|
||||
use core::iter::{self, Map, Repeat, Zip};
|
||||
|
||||
use derive_where::derive_where;
|
||||
use digest::{Digest, Output, OutputSizeUser};
|
||||
use generic_array::typenum::Unsigned;
|
||||
use generic_array::{ArrayLength, GenericArray};
|
||||
use rand_core::{TryCryptoRng, TryRngCore};
|
||||
|
||||
use crate::common::{
|
||||
derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2,
|
||||
server_evaluate_hash_input, verify_proof, BlindedElement, Dst, EvaluationElement, Mode,
|
||||
PreparedEvaluationElement, Proof, STR_FINALIZE, STR_HASH_TO_SCALAR, STR_INFO,
|
||||
};
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::serialization::serde::{Element, Scalar};
|
||||
use crate::{CipherSuite, Error, Group, Result};
|
||||
|
||||
////////////////////////////
|
||||
// High-level API Structs //
|
||||
// ====================== //
|
||||
////////////////////////////
|
||||
|
||||
/// A client which engages with a [PoprfServer] in verifiable mode, meaning
|
||||
/// that the OPRF outputs can be checked against a server public key.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct PoprfClient<CS: CipherSuite> {
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) blind: <CS::Group as Group>::Scalar,
|
||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||
pub(crate) blinded_element: <CS::Group as Group>::Elem,
|
||||
}
|
||||
|
||||
/// A server which engages with a [PoprfClient] in verifiable mode, meaning
|
||||
/// that the OPRF outputs can be checked against a server public key.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct PoprfServer<CS: CipherSuite> {
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
pub(crate) sk: <CS::Group as Group>::Scalar,
|
||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||
pub(crate) pk: <CS::Group as Group>::Elem,
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// API Implementations //
|
||||
// =================== //
|
||||
/////////////////////////
|
||||
|
||||
impl<CS: CipherSuite> PoprfClient<CS> {
|
||||
/// Computes the first step for the multiplicative blinding version of
|
||||
/// DH-OPRF.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`].
|
||||
pub fn blind<R: TryRngCore + TryCryptoRng>(
|
||||
input: &[u8],
|
||||
blinding_factor_rng: &mut R,
|
||||
) -> Result<PoprfClientBlindResult<CS>> {
|
||||
let blind = CS::Group::random_scalar(blinding_factor_rng)?;
|
||||
Self::deterministic_blind_unchecked_inner(input, blind)
|
||||
}
|
||||
|
||||
/// Computes the first step for the multiplicative blinding version of
|
||||
/// DH-OPRF, taking a blinding factor scalar as input instead of sampling
|
||||
/// from an RNG.
|
||||
///
|
||||
/// # Caution
|
||||
///
|
||||
/// This should be used with caution, since it does not perform any checks
|
||||
/// on the validity of the blinding factor!
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`].
|
||||
#[cfg(any(feature = "danger", test))]
|
||||
pub fn deterministic_blind_unchecked(
|
||||
input: &[u8],
|
||||
blind: <CS::Group as Group>::Scalar,
|
||||
) -> Result<PoprfClientBlindResult<CS>> {
|
||||
Self::deterministic_blind_unchecked_inner(input, blind)
|
||||
}
|
||||
|
||||
/// Can only fail with [`Error::Input`].
|
||||
fn deterministic_blind_unchecked_inner(
|
||||
input: &[u8],
|
||||
blind: <CS::Group as Group>::Scalar,
|
||||
) -> Result<PoprfClientBlindResult<CS>> {
|
||||
let blinded_element = deterministic_blind_unchecked::<CS>(input, &blind, Mode::Poprf)?;
|
||||
Ok(PoprfClientBlindResult {
|
||||
state: Self {
|
||||
blind,
|
||||
blinded_element,
|
||||
},
|
||||
message: BlindedElement(blinded_element),
|
||||
})
|
||||
}
|
||||
|
||||
/// Computes the third step for the multiplicative blinding version of
|
||||
/// DH-OPRF, in which the client unblinds the server's message.
|
||||
///
|
||||
/// # Errors
|
||||
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
|
||||
/// - [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`].
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
/// - [`Error::ProofVerification`] if the `proof` failed to verify.
|
||||
pub fn finalize(
|
||||
&self,
|
||||
input: &[u8],
|
||||
evaluation_element: &EvaluationElement<CS>,
|
||||
proof: &Proof<CS>,
|
||||
pk: <CS::Group as Group>::Elem,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<Output<CS::Hash>>
|
||||
where
|
||||
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArrayLength,
|
||||
{
|
||||
let clients = core::array::from_ref(self);
|
||||
let messages = core::array::from_ref(evaluation_element);
|
||||
|
||||
let mut batch_result =
|
||||
Self::batch_finalize(iter::once(input), clients, messages, proof, pk, info)?;
|
||||
batch_result.next().unwrap()
|
||||
}
|
||||
|
||||
/// Allows for batching of the finalization of multiple [PoprfClient]
|
||||
/// and [EvaluationElement] pairs
|
||||
///
|
||||
/// # Errors
|
||||
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
/// - [`Error::Batch`] if the number of `inputs`, `clients` and `messages`
|
||||
/// don't match or is longer than [`u16::MAX`].
|
||||
/// - [`Error::ProofVerification`] if the `proof` failed to verify.
|
||||
///
|
||||
/// The resulting messages can each fail individually with [`Error::Input`]
|
||||
/// if the `input` is empty or longer than [`u16::MAX`].
|
||||
pub fn batch_finalize<'a, II: 'a + Iterator<Item = &'a [u8]> + ExactSizeIterator, IC, IM>(
|
||||
inputs: II,
|
||||
clients: &'a IC,
|
||||
messages: &'a IM,
|
||||
proof: &Proof<CS>,
|
||||
pk: <CS::Group as Group>::Elem,
|
||||
info: Option<&'a [u8]>,
|
||||
) -> Result<PoprfClientBatchFinalizeResult<'a, CS, II, IC, IM>>
|
||||
where
|
||||
CS: 'a,
|
||||
&'a IC: 'a + IntoIterator<Item = &'a PoprfClient<CS>>,
|
||||
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
|
||||
<&'a IM as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArrayLength,
|
||||
{
|
||||
let unblinded_elements = poprf_unblind(clients, messages, pk, proof, info)?;
|
||||
|
||||
finalize_after_unblind::<'a, CS, _, _>(unblinded_elements, inputs, info)
|
||||
}
|
||||
|
||||
/// Only used for test functions
|
||||
#[cfg(test)]
|
||||
pub fn get_blind(&self) -> <CS::Group as Group>::Scalar {
|
||||
self.blind
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> PoprfServer<CS> {
|
||||
/// Produces a new instance of a [PoprfServer] using a supplied RNG
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
pub fn new<R: TryRngCore + TryCryptoRng>(rng: &mut R) -> Result<Self> {
|
||||
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
|
||||
rng.try_fill_bytes(&mut seed).map_err(|_| Error::Protocol)?;
|
||||
|
||||
Self::new_from_seed(&seed, &[])
|
||||
}
|
||||
|
||||
/// Produces a new instance of a [PoprfServer] using a supplied set of
|
||||
/// bytes to represent the server's private key
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Deserialization`] if the private key is not a valid point on
|
||||
/// the group or zero.
|
||||
pub fn new_with_key(key: &[u8]) -> Result<Self> {
|
||||
let sk = CS::Group::deserialize_scalar(key)?;
|
||||
let pk = CS::Group::base_elem() * &sk;
|
||||
Ok(Self { sk, pk })
|
||||
}
|
||||
|
||||
/// Produces a new instance of a [PoprfServer] using a supplied set of
|
||||
/// bytes which are used as a seed to derive the server's private key.
|
||||
///
|
||||
/// Corresponds to DeriveKeyPair() function from the VOPRF specification.
|
||||
///
|
||||
/// # Errors
|
||||
/// - [`Error::DeriveKeyPair`] if the `input` and `seed` together are longer
|
||||
/// then `u16::MAX - 3`.
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
pub fn new_from_seed(seed: &[u8], info: &[u8]) -> Result<Self> {
|
||||
let (sk, pk) = derive_keypair::<CS>(seed, info, Mode::Poprf)?;
|
||||
Ok(Self { sk, pk })
|
||||
}
|
||||
|
||||
/// Only used for tests
|
||||
#[cfg(test)]
|
||||
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
|
||||
self.sk
|
||||
}
|
||||
|
||||
/// Computes the second step for the multiplicative blinding version of
|
||||
/// DH-OPRF. This message is sent from the server (who holds the OPRF key)
|
||||
/// to the client.
|
||||
///
|
||||
/// # Errors
|
||||
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
pub fn blind_evaluate<R: TryRngCore + TryCryptoRng>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
blinded_element: &BlindedElement<CS>,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<PoprfServerEvaluateResult<CS>> {
|
||||
let PoprfServerBatchEvaluatePrepareResult {
|
||||
mut prepared_evaluation_elements,
|
||||
prepared_tweak,
|
||||
} = self.batch_blind_evaluate_prepare(iter::once(blinded_element), info)?;
|
||||
|
||||
let prepared_evaluation_element = prepared_evaluation_elements.next().unwrap();
|
||||
let prepared_evaluation_elements = core::array::from_ref(&prepared_evaluation_element);
|
||||
|
||||
let PoprfServerBatchEvaluateFinishResult {
|
||||
mut messages,
|
||||
proof,
|
||||
} = Self::batch_blind_evaluate_finish(
|
||||
rng,
|
||||
iter::once(blinded_element),
|
||||
prepared_evaluation_elements,
|
||||
&prepared_tweak,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Ok(PoprfServerEvaluateResult {
|
||||
message: messages.next().unwrap(),
|
||||
proof,
|
||||
})
|
||||
}
|
||||
|
||||
/// Allows for batching of the evaluation of multiple [BlindedElement]
|
||||
/// messages from a [PoprfClient]
|
||||
///
|
||||
/// # Errors
|
||||
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
#[cfg(feature = "alloc")]
|
||||
pub fn batch_blind_evaluate<'a, R: TryRngCore + TryCryptoRng, IE>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
blinded_elements: &'a IE,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<PoprfServerBatchEvaluateResult<CS>>
|
||||
where
|
||||
CS: 'a,
|
||||
&'a IE: 'a + IntoIterator<Item = &'a BlindedElement<CS>>,
|
||||
<&'a IE as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let PoprfServerBatchEvaluatePrepareResult {
|
||||
prepared_evaluation_elements,
|
||||
prepared_tweak,
|
||||
} = self.batch_blind_evaluate_prepare(blinded_elements.into_iter(), info)?;
|
||||
|
||||
let prepared_evaluation_elements: Vec<_> = prepared_evaluation_elements.collect();
|
||||
|
||||
// This can't fail because we know the size of the inputs.
|
||||
let PoprfServerBatchEvaluateFinishResult { messages, proof } =
|
||||
Self::batch_blind_evaluate_finish::<_, _, Vec<_>>(
|
||||
rng,
|
||||
blinded_elements.into_iter(),
|
||||
&prepared_evaluation_elements,
|
||||
&prepared_tweak,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let messages: Vec<_> = messages.collect();
|
||||
|
||||
Ok(PoprfServerBatchEvaluateResult { messages, proof })
|
||||
}
|
||||
|
||||
/// Alternative version of `batch_blind_evaluate` without
|
||||
/// memory allocation. Returned [`PreparedEvaluationElement`] have to
|
||||
/// be [`collect`](Iterator::collect)ed and passed into
|
||||
/// [`batch_blind_evaluate_finish`](Self::batch_blind_evaluate_finish).
|
||||
///
|
||||
/// # Errors
|
||||
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
|
||||
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||
pub fn batch_blind_evaluate_prepare<'a, I: Iterator<Item = &'a BlindedElement<CS>>>(
|
||||
&self,
|
||||
blinded_elements: I,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<PoprfServerBatchEvaluatePrepareResult<CS, I>>
|
||||
where
|
||||
CS: 'a,
|
||||
{
|
||||
let tweak = compute_tweak::<CS>(self.sk, info)?;
|
||||
|
||||
Ok(PoprfServerBatchEvaluatePrepareResult {
|
||||
prepared_evaluation_elements: blinded_elements.zip(iter::repeat(tweak)).map(
|
||||
|(blinded_element, tweak)| {
|
||||
PreparedEvaluationElement(EvaluationElement(
|
||||
blinded_element.0 * &CS::Group::invert_scalar(tweak),
|
||||
))
|
||||
},
|
||||
),
|
||||
prepared_tweak: PoprfPreparedTweak(tweak),
|
||||
})
|
||||
}
|
||||
|
||||
/// See [`batch_blind_evaluate_prepare`](Self::batch_blind_evaluate_prepare)
|
||||
/// for more details.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Batch`] if the number of `blinded_elements` and
|
||||
/// `prepared_evaluation_elements` don't match or is longer then
|
||||
/// [`u16::MAX`]
|
||||
pub fn batch_blind_evaluate_finish<
|
||||
'a,
|
||||
'b,
|
||||
R: TryRngCore + TryCryptoRng,
|
||||
IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator,
|
||||
IE,
|
||||
>(
|
||||
rng: &mut R,
|
||||
blinded_elements: IB,
|
||||
prepared_evaluation_elements: &'b IE,
|
||||
prepared_tweak: &PoprfPreparedTweak<CS>,
|
||||
) -> Result<PoprfServerBatchEvaluateFinishResult<'b, CS, IE>>
|
||||
where
|
||||
CS: 'a,
|
||||
&'b IE: IntoIterator<Item = &'b PreparedEvaluationElement<CS>>,
|
||||
<&'b IE as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let g = CS::Group::base_elem();
|
||||
let tweak = prepared_tweak.0;
|
||||
let tweaked_key = g * &tweak;
|
||||
|
||||
let proof = generate_proof(
|
||||
rng,
|
||||
tweak,
|
||||
g,
|
||||
tweaked_key,
|
||||
prepared_evaluation_elements
|
||||
.into_iter()
|
||||
.map(|element| element.0 .0),
|
||||
blinded_elements.map(|element| element.0),
|
||||
Mode::Poprf,
|
||||
)?;
|
||||
|
||||
let messages = prepared_evaluation_elements.into_iter().map(<fn(
|
||||
&PreparedEvaluationElement<CS>,
|
||||
) -> _>::from(
|
||||
|element| EvaluationElement(element.0 .0),
|
||||
));
|
||||
|
||||
Ok(PoprfServerBatchEvaluateFinishResult { messages, proof })
|
||||
}
|
||||
|
||||
/// Computes the output of the VOPRF on the server side
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Input`] if the `input` is longer then [`u16::MAX`].
|
||||
pub fn evaluate(
|
||||
&self,
|
||||
input: &[u8],
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<Output<<CS as CipherSuite>::Hash>> {
|
||||
let input_element = hash_to_group::<CS>(input, Mode::Poprf)?;
|
||||
if CS::Group::is_identity_elem(input_element).into() {
|
||||
return Err(Error::Input);
|
||||
};
|
||||
|
||||
let tweak = compute_tweak::<CS>(self.sk, info)?;
|
||||
|
||||
let evaluated_element = input_element * &CS::Group::invert_scalar(tweak);
|
||||
|
||||
let issued_element = CS::Group::serialize_elem(evaluated_element);
|
||||
|
||||
server_evaluate_hash_input::<CS>(input, info, issued_element)
|
||||
}
|
||||
|
||||
/// Retrieves the server's public key
|
||||
pub fn get_public_key(&self) -> <CS::Group as Group>::Elem {
|
||||
self.pk
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> BlindedElement<CS> {
|
||||
/// Creates a [BlindedElement] from a raw group element.
|
||||
///
|
||||
/// # Caution
|
||||
///
|
||||
/// This should be used with caution, since it does not perform any checks
|
||||
/// on the validity of the value itself!
|
||||
#[cfg(feature = "danger")]
|
||||
pub fn from_value_unchecked(value: <CS::Group as Group>::Elem) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
|
||||
/// Exposes the internal value
|
||||
#[cfg(feature = "danger")]
|
||||
pub fn value(&self) -> <CS::Group as Group>::Elem {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> EvaluationElement<CS> {
|
||||
/// Creates an [EvaluationElement] from a raw group element.
|
||||
///
|
||||
/// # Caution
|
||||
///
|
||||
/// This should be used with caution, since it does not perform any checks
|
||||
/// on the validity of the value itself!
|
||||
#[cfg(feature = "danger")]
|
||||
pub fn from_value_unchecked(value: <CS::Group as Group>::Elem) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
|
||||
/// Exposes the internal value
|
||||
#[cfg(feature = "danger")]
|
||||
pub fn value(&self) -> <CS::Group as Group>::Elem {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// Convenience Structs //
|
||||
//==================== //
|
||||
/////////////////////////
|
||||
|
||||
/// Contains the fields that are returned by a verifiable client blind
|
||||
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||
pub struct PoprfClientBlindResult<CS: CipherSuite> {
|
||||
/// The state to be persisted on the client
|
||||
pub state: PoprfClient<CS>,
|
||||
/// The message to send to the server
|
||||
pub message: BlindedElement<CS>,
|
||||
}
|
||||
|
||||
/// Concrete return type for [`PoprfClient::batch_finalize`].
|
||||
pub type PoprfClientBatchFinalizeResult<'a, CS, II, IC, IM> =
|
||||
FinalizeAfterUnblindResult<'a, CS, PoprfUnblindResult<'a, CS, IC, IM>, II>;
|
||||
|
||||
/// Contains the fields that are returned by a verifiable server evaluate
|
||||
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||
pub struct PoprfServerEvaluateResult<CS: CipherSuite> {
|
||||
/// The message to send to the client
|
||||
pub message: EvaluationElement<CS>,
|
||||
/// The proof for the client to verify
|
||||
pub proof: Proof<CS>,
|
||||
}
|
||||
|
||||
/// Contains the fields that are returned by a verifiable server batch evaluate
|
||||
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
|
||||
#[cfg(feature = "alloc")]
|
||||
pub struct PoprfServerBatchEvaluateResult<CS: CipherSuite> {
|
||||
/// The messages to send to the client
|
||||
pub messages: Vec<EvaluationElement<CS>>,
|
||||
/// The proof for the client to verify
|
||||
pub proof: Proof<CS>,
|
||||
}
|
||||
|
||||
/// Concrete type of [`EvaluationElement`]s in
|
||||
/// [`PoprfServerBatchEvaluatePrepareResult`].
|
||||
pub type PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I> = Map<
|
||||
Zip<I, Repeat<<<CS as CipherSuite>::Group as Group>::Scalar>>,
|
||||
fn(
|
||||
(
|
||||
&BlindedElement<CS>,
|
||||
<<CS as CipherSuite>::Group as Group>::Scalar,
|
||||
),
|
||||
) -> PreparedEvaluationElement<CS>,
|
||||
>;
|
||||
|
||||
/// Prepared tweak by a partially verifiable server batch evaluate prepare.
|
||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::Group as Group>::Scalar)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(bound = "")
|
||||
)]
|
||||
pub struct PoprfPreparedTweak<CS: CipherSuite>(
|
||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||
<CS::Group as Group>::Scalar,
|
||||
);
|
||||
|
||||
/// Contains the fields that are returned by a partially verifiable server batch
|
||||
/// evaluate prepare
|
||||
#[derive_where(Debug; I, <CS::Group as Group>::Scalar)]
|
||||
pub struct PoprfServerBatchEvaluatePrepareResult<CS: CipherSuite, I> {
|
||||
/// Prepared [`EvaluationElement`].
|
||||
pub prepared_evaluation_elements: PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I>,
|
||||
/// Prepared tweak.
|
||||
pub prepared_tweak: PoprfPreparedTweak<CS>,
|
||||
}
|
||||
|
||||
/// Concrete type of [`EvaluationElement`]s in
|
||||
/// [`PoprfServerBatchEvaluateFinishResult`].
|
||||
pub type PoprfServerBatchEvaluateFinishedMessages<'a, CS, I> = Map<
|
||||
<&'a I as IntoIterator>::IntoIter,
|
||||
fn(&PreparedEvaluationElement<CS>) -> EvaluationElement<CS>,
|
||||
>;
|
||||
|
||||
/// Contains the fields that are returned by a verifiable server batch evaluate
|
||||
/// finish.
|
||||
#[derive_where(Debug; <&'a I as IntoIterator>::IntoIter, <CS::Group as Group>::Scalar)]
|
||||
pub struct PoprfServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I>
|
||||
where
|
||||
&'a I: IntoIterator<Item = &'a PreparedEvaluationElement<CS>>,
|
||||
{
|
||||
/// The [`EvaluationElement`]s to send to the client
|
||||
pub messages: PoprfServerBatchEvaluateFinishedMessages<'a, CS, I>,
|
||||
/// The proof for the client to verify
|
||||
pub proof: Proof<CS>,
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// Inner functions //
|
||||
// =============== //
|
||||
/////////////////////
|
||||
|
||||
/// Inner function for POPRF blind. Computes the tweaked key from the server
|
||||
/// public key and info.
|
||||
///
|
||||
/// Can only fail with [`Error::Info`] or [`Error::Protocol`]
|
||||
fn compute_tweaked_key<CS: CipherSuite>(
|
||||
pk: <CS::Group as Group>::Elem,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<<CS::Group as Group>::Elem> {
|
||||
// None for info is treated the same as empty bytes
|
||||
let info = info.unwrap_or_default();
|
||||
|
||||
// framedInfo = "Info" || I2OSP(len(info), 2) || info
|
||||
// m = G.HashToScalar(framedInfo)
|
||||
// T = G.ScalarBaseMult(m)
|
||||
// tweakedKey = T + pkS
|
||||
// if tweakedKey == G.Identity():
|
||||
// raise InvalidInputError
|
||||
let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
|
||||
let framed_info = [STR_INFO.as_slice(), &info_len, info];
|
||||
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, Mode::Poprf);
|
||||
// This can't fail, the size of the `input` is known.
|
||||
let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst.as_dst()).unwrap();
|
||||
|
||||
let t = CS::Group::base_elem() * &m;
|
||||
let tweaked_key = t + &pk;
|
||||
|
||||
// Check if resulting element
|
||||
match bool::from(CS::Group::is_identity_elem(tweaked_key)) {
|
||||
true => Err(Error::Protocol),
|
||||
false => Ok(tweaked_key),
|
||||
}
|
||||
}
|
||||
|
||||
/// Inner function for POPRF evaluate. Computes the tweak from the server
|
||||
/// private key and info.
|
||||
///
|
||||
/// Can only fail with [`Error::Info`] and [`Error::Protocol`].
|
||||
fn compute_tweak<CS: CipherSuite>(
|
||||
sk: <CS::Group as Group>::Scalar,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<<CS::Group as Group>::Scalar> {
|
||||
// None for info is treated the same as empty bytes
|
||||
let info = info.unwrap_or_default();
|
||||
|
||||
// framedInfo = "Info" || I2OSP(len(info), 2) || info
|
||||
// m = G.HashToScalar(framedInfo)
|
||||
// t = skS + m
|
||||
// if t == 0:
|
||||
// raise InverseError
|
||||
let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
|
||||
let framed_info = [STR_INFO.as_slice(), &info_len, info];
|
||||
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, Mode::Poprf);
|
||||
// This can't fail, the size of the `input` is known.
|
||||
let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst.as_dst()).unwrap();
|
||||
|
||||
let t = sk + &m;
|
||||
|
||||
// Check if resulting element is equal to zero
|
||||
match bool::from(CS::Group::is_zero_scalar(t)) {
|
||||
true => Err(Error::Protocol),
|
||||
false => Ok(t),
|
||||
}
|
||||
}
|
||||
|
||||
type PoprfUnblindResult<'a, CS, IC, IM> = Map<
|
||||
Zip<
|
||||
Map<
|
||||
<&'a IC as IntoIterator>::IntoIter,
|
||||
fn(&PoprfClient<CS>) -> <<CS as CipherSuite>::Group as Group>::Scalar,
|
||||
>,
|
||||
<&'a IM as IntoIterator>::IntoIter,
|
||||
>,
|
||||
fn(
|
||||
(
|
||||
<<CS as CipherSuite>::Group as Group>::Scalar,
|
||||
&'a EvaluationElement<CS>,
|
||||
),
|
||||
) -> <<CS as CipherSuite>::Group as Group>::Elem,
|
||||
>;
|
||||
|
||||
/// Can only fail with [`Error::Info`], [`Error::Protocol`], [`Error::Batch] or
|
||||
/// [`Error::ProofVerification`].
|
||||
fn poprf_unblind<'a, CS: 'a + CipherSuite, IC, IM>(
|
||||
clients: &'a IC,
|
||||
messages: &'a IM,
|
||||
pk: <CS::Group as Group>::Elem,
|
||||
proof: &Proof<CS>,
|
||||
info: Option<&[u8]>,
|
||||
) -> Result<PoprfUnblindResult<'a, CS, IC, IM>>
|
||||
where
|
||||
&'a IC: 'a + IntoIterator<Item = &'a PoprfClient<CS>>,
|
||||
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
|
||||
<&'a IM as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let info = info.unwrap_or_default();
|
||||
let tweaked_key = compute_tweaked_key::<CS>(pk, Some(info))?;
|
||||
|
||||
let g = CS::Group::base_elem();
|
||||
|
||||
let blinds = clients
|
||||
.into_iter()
|
||||
// Convert to `fn` pointer to make a return type possible.
|
||||
.map(<fn(&PoprfClient<CS>) -> _>::from(|x| x.blind));
|
||||
let evaluation_elements = messages.into_iter().map(|element| element.0);
|
||||
let blinded_elements = clients.into_iter().map(|client| client.blinded_element);
|
||||
|
||||
verify_proof(
|
||||
g,
|
||||
tweaked_key,
|
||||
evaluation_elements,
|
||||
blinded_elements,
|
||||
proof,
|
||||
Mode::Poprf,
|
||||
)?;
|
||||
|
||||
Ok(blinds
|
||||
.zip(messages)
|
||||
.map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
|
||||
}
|
||||
|
||||
type FinalizeAfterUnblindResult<'a, CS, IE, II> = Map<
|
||||
Zip<Zip<IE, II>, Repeat<&'a [u8]>>,
|
||||
fn(
|
||||
((<<CS as CipherSuite>::Group as Group>::Elem, &[u8]), &[u8]),
|
||||
) -> Result<Output<<CS as CipherSuite>::Hash>>,
|
||||
>;
|
||||
|
||||
/// Can only fail with [`Error::Batch`] and returned values can only fail with
|
||||
/// [`Error::Info`] or [`Error::Input`] individually.
|
||||
fn finalize_after_unblind<
|
||||
'a,
|
||||
CS: CipherSuite,
|
||||
IE: 'a + Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
||||
II: 'a + Iterator<Item = &'a [u8]> + ExactSizeIterator,
|
||||
>(
|
||||
unblinded_elements: IE,
|
||||
inputs: II,
|
||||
info: Option<&'a [u8]>,
|
||||
) -> Result<FinalizeAfterUnblindResult<'a, CS, IE, II>>
|
||||
where
|
||||
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArrayLength,
|
||||
{
|
||||
if unblinded_elements.len() != inputs.len() {
|
||||
return Err(Error::Batch);
|
||||
}
|
||||
|
||||
let info = info.unwrap_or_default();
|
||||
|
||||
Ok(unblinded_elements.zip(inputs).zip(iter::repeat(info)).map(
|
||||
|((unblinded_element, input), info)| {
|
||||
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
|
||||
|
||||
// hashInput = I2OSP(len(input), 2) || input ||
|
||||
// I2OSP(len(info), 2) || info ||
|
||||
// I2OSP(len(unblindedElement), 2) || unblindedElement ||
|
||||
// "Finalize"
|
||||
// return Hash(hashInput)
|
||||
let output = CS::Hash::new()
|
||||
.chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?)
|
||||
.chain_update(input.as_ref())
|
||||
.chain_update(i2osp_2(info.as_ref().len()).map_err(|_| Error::Info)?)
|
||||
.chain_update(info.as_ref())
|
||||
.chain_update(elem_len)
|
||||
.chain_update(CS::Group::serialize_elem(unblinded_element))
|
||||
.chain_update(STR_FINALIZE)
|
||||
.finalize();
|
||||
|
||||
Ok(output)
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
///////////
|
||||
// Tests //
|
||||
// ===== //
|
||||
///////////
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::ptr;
|
||||
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use super::*;
|
||||
use crate::common::STR_HASH_TO_GROUP;
|
||||
use crate::Group;
|
||||
|
||||
fn prf<CS: CipherSuite>(
|
||||
input: &[u8],
|
||||
key: <CS::Group as Group>::Scalar,
|
||||
info: &[u8],
|
||||
mode: Mode,
|
||||
) -> Output<CS::Hash> {
|
||||
let t = compute_tweak::<CS>(key, Some(info)).unwrap();
|
||||
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, mode);
|
||||
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
|
||||
|
||||
// evaluatedElement = G.ScalarInverse(t) * blindedElement
|
||||
let res = point * &CS::Group::invert_scalar(t);
|
||||
|
||||
finalize_after_unblind::<CS, _, _>(iter::once(res), iter::once(input), Some(info))
|
||||
.unwrap()
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn verifiable_retrieval<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
let info = b"info";
|
||||
let mut rng = OsRng;
|
||||
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
let server_result = server
|
||||
.blind_evaluate(&mut rng, &client_blind_result.message, Some(info))
|
||||
.unwrap();
|
||||
let client_finalize_result = client_blind_result
|
||||
.state
|
||||
.finalize(
|
||||
input,
|
||||
&server_result.message,
|
||||
&server_result.proof,
|
||||
server.get_public_key(),
|
||||
Some(info),
|
||||
)
|
||||
.unwrap();
|
||||
let res2 = prf::<CS>(input, server.get_private_key(), info, Mode::Poprf);
|
||||
assert_eq!(client_finalize_result, res2);
|
||||
}
|
||||
|
||||
fn verifiable_bad_public_key<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
let info = b"info";
|
||||
let mut rng = OsRng;
|
||||
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
let server_result = server
|
||||
.blind_evaluate(&mut rng, &client_blind_result.message, Some(info))
|
||||
.unwrap();
|
||||
let wrong_pk = {
|
||||
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, Mode::Oprf);
|
||||
// Choose a group element that is unlikely to be the right public key
|
||||
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
|
||||
};
|
||||
let client_finalize_result = client_blind_result.state.finalize(
|
||||
input,
|
||||
&server_result.message,
|
||||
&server_result.proof,
|
||||
wrong_pk,
|
||||
Some(info),
|
||||
);
|
||||
assert!(client_finalize_result.is_err());
|
||||
}
|
||||
|
||||
fn verifiable_server_evaluate<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
let info = Some(b"info".as_slice());
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let server_result = server
|
||||
.blind_evaluate(&mut rng, &client_blind_result.message, info)
|
||||
.unwrap();
|
||||
|
||||
let client_finalize = client_blind_result
|
||||
.state
|
||||
.finalize(
|
||||
input,
|
||||
&server_result.message,
|
||||
&server_result.proof,
|
||||
server.get_public_key(),
|
||||
info,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// We expect the outputs from client and server to be equal given an identical
|
||||
// input
|
||||
let server_evaluate = server.evaluate(input, info).unwrap();
|
||||
assert_eq!(client_finalize, server_evaluate);
|
||||
|
||||
// We expect the outputs from client and server to be different given different
|
||||
// inputs
|
||||
let wrong_input = b"wrong input";
|
||||
let server_evaluate = server.evaluate(wrong_input, info).unwrap();
|
||||
assert!(client_finalize != server_evaluate);
|
||||
}
|
||||
|
||||
fn zeroize_verifiable_client<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
let mut rng = OsRng;
|
||||
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
|
||||
let mut state = client_blind_result.state;
|
||||
unsafe { ptr::drop_in_place(&mut state) };
|
||||
assert!(state.serialize().iter().all(|&x| x == 0));
|
||||
|
||||
let mut message = client_blind_result.message;
|
||||
unsafe { ptr::drop_in_place(&mut message) };
|
||||
assert!(message.serialize().iter().all(|&x| x == 0));
|
||||
}
|
||||
|
||||
fn zeroize_verifiable_server<CS: CipherSuite>() {
|
||||
let input = b"input";
|
||||
let info = b"info";
|
||||
let mut rng = OsRng;
|
||||
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
|
||||
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
|
||||
let server_result = server
|
||||
.blind_evaluate(&mut rng, &client_blind_result.message, Some(info))
|
||||
.unwrap();
|
||||
|
||||
let mut state = server;
|
||||
unsafe { ptr::drop_in_place(&mut state) };
|
||||
assert!(state.serialize().iter().all(|&x| x == 0));
|
||||
|
||||
let mut message = server_result.message;
|
||||
unsafe { ptr::drop_in_place(&mut message) };
|
||||
assert!(message.serialize().iter().all(|&x| x == 0));
|
||||
|
||||
let mut proof = server_result.proof;
|
||||
unsafe { ptr::drop_in_place(&mut proof) };
|
||||
assert!(proof.serialize().iter().all(|&x| x == 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_functionality() -> Result<()> {
|
||||
use p256::NistP256;
|
||||
use p384::NistP384;
|
||||
use p521::NistP521;
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
{
|
||||
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>();
|
||||
|
||||
verifiable_retrieval::<NistP384>();
|
||||
verifiable_bad_public_key::<NistP384>();
|
||||
verifiable_server_evaluate::<NistP384>();
|
||||
|
||||
zeroize_verifiable_client::<NistP384>();
|
||||
zeroize_verifiable_server::<NistP384>();
|
||||
|
||||
verifiable_retrieval::<NistP521>();
|
||||
verifiable_bad_public_key::<NistP521>();
|
||||
verifiable_server_evaluate::<NistP521>();
|
||||
|
||||
zeroize_verifiable_client::<NistP521>();
|
||||
zeroize_verifiable_server::<NistP521>();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+295
-138
@@ -1,228 +1,385 @@
|
||||
// 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
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// This source code is dual-licensed under either the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
|
||||
//! Handles the serialization of each of the components used
|
||||
//! in the VOPRF protocol
|
||||
//! Handles the serialization of each of the components used in the VOPRF
|
||||
//! protocol
|
||||
|
||||
use generic_array::sequence::Concat;
|
||||
use generic_array::typenum::{Sum, Unsigned};
|
||||
use generic_array::GenericArray;
|
||||
|
||||
use crate::{
|
||||
ciphersuite::CipherSuite,
|
||||
errors::InternalError,
|
||||
group::Group,
|
||||
voprf::{
|
||||
BlindedElement, EvaluationElement, NonVerifiableClient, NonVerifiableServer, Proof,
|
||||
VerifiableClient, VerifiableServer,
|
||||
},
|
||||
BlindedElement, CipherSuite, Error, EvaluationElement, Group, OprfClient, OprfServer,
|
||||
PoprfClient, PoprfServer, Proof, Result, VoprfClient, VoprfServer,
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use generic_array::{typenum::Unsigned, GenericArray};
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
// Serialization and Deserialization for High-Level API //
|
||||
// ==================================================== //
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
impl<CS: CipherSuite> NonVerifiableClient<CS> {
|
||||
/// Length of [`OprfClient`] in bytes for serialization.
|
||||
pub type OprfClientLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
|
||||
|
||||
impl<CS: CipherSuite> OprfClient<CS> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
[
|
||||
CS::Group::scalar_as_bytes(self.blind).to_vec(),
|
||||
self.data.clone(),
|
||||
]
|
||||
.concat()
|
||||
pub fn serialize(&self) -> GenericArray<u8, OprfClientLen<CS>> {
|
||||
CS::Group::serialize_scalar(self.blind)
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
|
||||
let scalar_len = <CS::Group as Group>::ScalarLen::USIZE;
|
||||
if input.len() < scalar_len {
|
||||
return Err(InternalError::SizeError);
|
||||
}
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
||||
let blind = deserialize_scalar::<CS::Group>(&mut input)?;
|
||||
|
||||
let blind = CS::Group::from_scalar_slice(GenericArray::from_slice(&input[..scalar_len]))?;
|
||||
let data = input[scalar_len..].to_vec();
|
||||
|
||||
Ok(Self { blind, data })
|
||||
Ok(Self { blind })
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> VerifiableClient<CS> {
|
||||
/// 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> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
[
|
||||
CS::Group::scalar_as_bytes(self.blind).to_vec(),
|
||||
self.blinded_element.to_arr().to_vec(),
|
||||
self.data.clone(),
|
||||
]
|
||||
.concat()
|
||||
pub fn serialize(&self) -> GenericArray<u8, VoprfClientLen<CS>> {
|
||||
<CS::Group as Group>::serialize_scalar(self.blind)
|
||||
.concat(<CS::Group as Group>::serialize_elem(self.blinded_element))
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
|
||||
let scalar_len = <CS::Group as Group>::ScalarLen::USIZE;
|
||||
let elem_len = <CS::Group as Group>::ElemLen::USIZE;
|
||||
if input.len() < scalar_len + elem_len {
|
||||
return Err(InternalError::SizeError);
|
||||
}
|
||||
|
||||
let blind = CS::Group::from_scalar_slice(GenericArray::from_slice(&input[..scalar_len]))?;
|
||||
let blinded_element = CS::Group::from_element_slice(GenericArray::from_slice(
|
||||
&input[scalar_len..scalar_len + elem_len],
|
||||
))?;
|
||||
let data = input[scalar_len + elem_len..].to_vec();
|
||||
///
|
||||
/// # 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,
|
||||
data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> NonVerifiableServer<CS> {
|
||||
/// 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> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
CS::Group::scalar_as_bytes(self.sk).to_vec()
|
||||
pub fn serialize(&self) -> GenericArray<u8, PoprfClientLen<CS>> {
|
||||
<CS::Group as Group>::serialize_scalar(self.blind)
|
||||
.concat(<CS::Group as Group>::serialize_elem(self.blinded_element))
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
|
||||
let scalar_len = <CS::Group as Group>::ScalarLen::USIZE;
|
||||
if input.len() != scalar_len {
|
||||
return Err(InternalError::SizeError);
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::Deserialization`] if failed to deserialize `input`.
|
||||
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
|
||||
let blind = deserialize_scalar::<CS::Group>(&mut input)?;
|
||||
let blinded_element = deserialize_elem::<CS::Group>(&mut input)?;
|
||||
|
||||
Ok(Self {
|
||||
blind,
|
||||
blinded_element,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let sk = CS::Group::from_scalar_slice(GenericArray::from_slice(input))?;
|
||||
/// Length of [`OprfServer`] in bytes for serialization.
|
||||
pub type OprfServerLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
|
||||
|
||||
impl<CS: CipherSuite> OprfServer<CS> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> GenericArray<u8, OprfServerLen<CS>> {
|
||||
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 })
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> VerifiableServer<CS> {
|
||||
/// 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> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
[
|
||||
CS::Group::scalar_as_bytes(self.sk).to_vec(),
|
||||
self.pk.to_arr().to_vec(),
|
||||
]
|
||||
.concat()
|
||||
pub fn serialize(&self) -> GenericArray<u8, VoprfServerLen<CS>> {
|
||||
CS::Group::serialize_scalar(self.sk).concat(CS::Group::serialize_elem(self.pk))
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
|
||||
let scalar_len = <CS::Group as Group>::ScalarLen::USIZE;
|
||||
let elem_len = <CS::Group as Group>::ElemLen::USIZE;
|
||||
if input.len() != scalar_len + elem_len {
|
||||
return Err(InternalError::SizeError);
|
||||
}
|
||||
|
||||
let sk = CS::Group::from_scalar_slice(GenericArray::from_slice(&input[..scalar_len]))?;
|
||||
let pk = CS::Group::from_element_slice(GenericArray::from_slice(&input[scalar_len..]))?;
|
||||
///
|
||||
/// # 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 })
|
||||
}
|
||||
}
|
||||
|
||||
impl<CS: CipherSuite> Proof<CS> {
|
||||
/// 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> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
[
|
||||
CS::Group::scalar_as_bytes(self.c_scalar),
|
||||
CS::Group::scalar_as_bytes(self.s_scalar),
|
||||
]
|
||||
.concat()
|
||||
pub fn serialize(&self) -> GenericArray<u8, PoprfServerLen<CS>> {
|
||||
CS::Group::serialize_scalar(self.sk).concat(CS::Group::serialize_elem(self.pk))
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
|
||||
let scalar_len = <CS::Group as Group>::ScalarLen::USIZE;
|
||||
if input.len() < scalar_len + scalar_len {
|
||||
return Err(InternalError::SizeError);
|
||||
}
|
||||
Ok(Proof {
|
||||
c_scalar: CS::Group::from_scalar_slice(GenericArray::from_slice(&input[..scalar_len]))?,
|
||||
s_scalar: CS::Group::from_scalar_slice(GenericArray::from_slice(&input[scalar_len..]))?,
|
||||
})
|
||||
///
|
||||
/// # 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> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> GenericArray<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)?;
|
||||
|
||||
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> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
self.value.to_arr().to_vec()
|
||||
pub fn serialize(&self) -> GenericArray<u8, BlindedElementLen<CS>> {
|
||||
CS::Group::serialize_elem(self.0)
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
|
||||
Ok(Self {
|
||||
value: CS::Group::from_element_slice(GenericArray::from_slice(input))?,
|
||||
})
|
||||
///
|
||||
/// # 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> {
|
||||
/// Serialization into bytes
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
self.value.to_arr().to_vec()
|
||||
pub fn serialize(&self) -> GenericArray<u8, EvaluationElementLen<CS>> {
|
||||
CS::Group::serialize_elem(self.0)
|
||||
}
|
||||
|
||||
/// Deserialization from bytes
|
||||
pub fn deserialize(input: &[u8]) -> Result<Self, InternalError> {
|
||||
Ok(Self {
|
||||
value: CS::Group::from_element_slice(GenericArray::from_slice(input))?,
|
||||
})
|
||||
///
|
||||
/// # 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))
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
// Helper Functions //
|
||||
// ================ //
|
||||
//////////////////////
|
||||
|
||||
// Corresponds to the I2OSP() function from RFC8017
|
||||
pub(crate) fn i2osp(input: usize, length: usize) -> Result<alloc::vec::Vec<u8>, InternalError> {
|
||||
let sizeof_usize = core::mem::size_of::<usize>();
|
||||
|
||||
// Check if input >= 256^length
|
||||
if (sizeof_usize as u32 - input.leading_zeros() / 8) > length as u32 {
|
||||
return Err(InternalError::SerializationError);
|
||||
fn deserialize_elem<G: Group>(input: &mut &[u8]) -> Result<G::Elem> {
|
||||
let input = input
|
||||
.take_ext(G::ElemLen::USIZE)
|
||||
.ok_or(Error::Deserialization)?;
|
||||
G::deserialize_elem(input)
|
||||
}
|
||||
|
||||
if length <= sizeof_usize {
|
||||
return Ok((&input.to_be_bytes()[sizeof_usize - length..]).to_vec());
|
||||
fn deserialize_scalar<G: Group>(input: &mut &[u8]) -> Result<G::Scalar> {
|
||||
let input = input
|
||||
.take_ext(G::ScalarLen::USIZE)
|
||||
.ok_or(Error::Deserialization)?;
|
||||
G::deserialize_scalar(input)
|
||||
}
|
||||
|
||||
let mut output = alloc::vec![0u8; length];
|
||||
output.splice(
|
||||
length - sizeof_usize..length,
|
||||
input.to_be_bytes().iter().cloned(),
|
||||
);
|
||||
Ok(output)
|
||||
trait SliceExt {
|
||||
fn take_ext<'a>(self: &mut &'a Self, take: usize) -> Option<&'a Self>;
|
||||
}
|
||||
|
||||
// Computes I2OSP(len(input), max_bytes) || input
|
||||
pub(crate) fn serialize(input: &[u8], max_bytes: usize) -> Result<Vec<u8>, InternalError> {
|
||||
Ok([&i2osp(input.len(), max_bytes)?, input].concat())
|
||||
impl<T> SliceExt for [T] {
|
||||
fn take_ext<'a>(self: &mut &'a Self, take: usize) -> Option<&'a Self> {
|
||||
if take > self.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (front, back) = self.split_at(take);
|
||||
*self = back;
|
||||
Some(front)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
pub(crate) mod serde {
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use generic_array::GenericArray;
|
||||
use serde::de::{Deserializer, Error};
|
||||
use serde::ser::Serializer;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::Group;
|
||||
|
||||
pub(crate) struct Element<G: Group>(PhantomData<G>);
|
||||
|
||||
impl<'de, G: Group> Element<G> {
|
||||
pub(crate) fn deserialize<D>(deserializer: D) -> Result<G::Elem, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
GenericArray::<_, G::ElemLen>::deserialize(deserializer)
|
||||
.and_then(|bytes| G::deserialize_elem(&bytes).map_err(D::Error::custom))
|
||||
}
|
||||
|
||||
pub(crate) fn serialize<S>(self_: &G::Elem, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
G::serialize_elem(*self_).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Scalar<G: Group>(PhantomData<G>);
|
||||
|
||||
impl<'de, G: Group> Scalar<G> {
|
||||
pub(crate) fn deserialize<D>(deserializer: D) -> Result<G::Scalar, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
GenericArray::<_, G::ScalarLen>::deserialize(deserializer)
|
||||
.and_then(|bytes| G::deserialize_scalar(&bytes).map_err(D::Error::custom))
|
||||
}
|
||||
|
||||
pub(crate) fn serialize<S>(self_: &G::Scalar, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
G::serialize_scalar(*self_).serialize(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_tests {
|
||||
use super::*;
|
||||
mod test {
|
||||
use proptest::collection::vec;
|
||||
use proptest::prelude::*;
|
||||
|
||||
// Test the error condition for I2OSP
|
||||
use crate::{
|
||||
BlindedElement, EvaluationElement, OprfClient, OprfServer, PoprfClient, PoprfServer, Proof,
|
||||
VoprfClient, VoprfServer,
|
||||
};
|
||||
|
||||
macro_rules! test_deserialize {
|
||||
($item:ident, $bytes:ident) => {
|
||||
#[cfg(feature = "ristretto255")]
|
||||
{
|
||||
let _ = $item::<crate::Ristretto255>::deserialize(&$bytes[..]);
|
||||
}
|
||||
|
||||
let _ = $item::<p256::NistP256>::deserialize(&$bytes[..]);
|
||||
let _ = $item::<p384::NistP384>::deserialize(&$bytes[..]);
|
||||
let _ = $item::<p521::NistP521>::deserialize(&$bytes[..]);
|
||||
};
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_i2osp_err_check() {
|
||||
assert!(i2osp(0, 1).is_ok());
|
||||
fn test_nocrash_oprf_client(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(OprfClient, bytes);
|
||||
}
|
||||
|
||||
assert!(i2osp(255, 1).is_ok());
|
||||
assert!(i2osp(256, 1).is_err());
|
||||
assert!(i2osp(257, 1).is_err());
|
||||
#[test]
|
||||
fn test_nocrash_voprf_client(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(VoprfClient, bytes);
|
||||
}
|
||||
|
||||
assert!(i2osp(256 * 256 - 1, 2).is_ok());
|
||||
assert!(i2osp(256 * 256, 2).is_err());
|
||||
assert!(i2osp(256 * 256 + 1, 2).is_err());
|
||||
#[test]
|
||||
fn test_nocrash_poprf_client(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(PoprfClient, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_oprf_server(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(OprfServer, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_voprf_server(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(VoprfServer, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_poprf_server(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(PoprfServer, bytes);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_blinded_element(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(BlindedElement, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_evaluation_element(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(EvaluationElement, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nocrash_proof(bytes in vec(any::<u8>(), 0..200)) {
|
||||
test_deserialize!(Proof, bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,922 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
//
|
||||
// This source code is dual-licensed under either the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
|
||||
//! The VOPRF test vectors taken from:
|
||||
//! https://www.rfc-editor.org/rfc/rfc9497#appendix-A
|
||||
|
||||
pub(crate) const VECTORS: &str = r#"
|
||||
A.1. ristretto255-SHA512
|
||||
|
||||
A.1.1. OPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 5ebcea5ee37023ccb9fc2d2019f9d7737be85591ae8652ffa9ef0f4d37063
|
||||
b0e
|
||||
|
||||
A.1.1.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
|
||||
6706
|
||||
BlindedElement = 609a0ae68c15a3cf6903766461307e5c8bb2f95e7e6550e1ffa
|
||||
2dc99e412803c
|
||||
EvaluationElement = 7ec6578ae5120958eb2db1745758ff379e77cb64fe77b0b2
|
||||
d8cc917ea0869c7e
|
||||
Output = 527759c3d9366f277d8c6020418d96bb393ba2afb20ff90df23fb770826
|
||||
4e2f3ab9135e3bd69955851de4b1f9fe8a0973396719b7912ba9ee8aa7d0b5e24bcf
|
||||
6
|
||||
|
||||
A.1.1.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
|
||||
6706
|
||||
BlindedElement = da27ef466870f5f15296299850aa088629945a17d1f5b7f5ff0
|
||||
43f76b3c06418
|
||||
EvaluationElement = b4cbf5a4f1eeda5a63ce7b77c7d23f461db3fcab0dd28e4e
|
||||
17cecb5c90d02c25
|
||||
Output = f4a74c9c592497375e796aa837e907b1a045d34306a749db9f34221f7e7
|
||||
50cb4f2a6413a6bf6fa5e19ba6348eb673934a722a7ede2e7621306d18951e7cf2c7
|
||||
3
|
||||
|
||||
A.1.2. VOPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = e6f73f344b79b379f1a0dd37e07ff62e38d9f71345ce62ae3a9bc60b04ccd
|
||||
909
|
||||
pkSm = c803e2cc6b05fc15064549b5920659ca4a77b2cca6f04f6b357009335476a
|
||||
d4e
|
||||
|
||||
A.1.2.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
|
||||
6706
|
||||
BlindedElement = 863f330cc1a1259ed5a5998a23acfd37fb4351a793a5b3c090b
|
||||
642ddc439b945
|
||||
EvaluationElement = aa8fa048764d5623868679402ff6108d2521884fa138cd7f
|
||||
9c7669a9a014267e
|
||||
Proof = ddef93772692e535d1a53903db24367355cc2cc78de93b3be5a8ffcc6985
|
||||
dd066d4346421d17bf5117a2a1ff0fcb2a759f58a539dfbe857a40bce4cf49ec600d
|
||||
ProofRandomScalar = 222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d98
|
||||
81aa6f61d645fc0e
|
||||
Output = b58cfbe118e0cb94d79b5fd6a6dafb98764dff49c14e1770b566e42402d
|
||||
a1a7da4d8527693914139caee5bd03903af43a491351d23b430948dd50cde10d32b3
|
||||
c
|
||||
|
||||
A.1.2.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
|
||||
6706
|
||||
BlindedElement = cc0b2a350101881d8a4cba4c80241d74fb7dcbfde4a61fde2f9
|
||||
1443c2bf9ef0c
|
||||
EvaluationElement = 60a59a57208d48aca71e9e850d22674b611f752bed48b36f
|
||||
7a91b372bd7ad468
|
||||
Proof = 401a0da6264f8cf45bb2f5264bc31e109155600babb3cd4e5af7d181a2c9
|
||||
dc0a67154fabf031fd936051dec80b0b6ae29c9503493dde7393b722eafdf5a50b02
|
||||
ProofRandomScalar = 222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d98
|
||||
81aa6f61d645fc0e
|
||||
Output = 8a9a2f3c7f085b65933594309041fc1898d42d0858e59f90814ae90571a
|
||||
6df60356f4610bf816f27afdd84f47719e480906d27ecd994985890e5f539e7ea74b
|
||||
6
|
||||
|
||||
A.1.2.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
|
||||
6706,222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0
|
||||
e
|
||||
BlindedElement = 863f330cc1a1259ed5a5998a23acfd37fb4351a793a5b3c090b
|
||||
642ddc439b945,90a0145ea9da29254c3a56be4fe185465ebb3bf2a1801f7124bbba
|
||||
dac751e654
|
||||
EvaluationElement = aa8fa048764d5623868679402ff6108d2521884fa138cd7f
|
||||
9c7669a9a014267e,cc5ac221950a49ceaa73c8db41b82c20372a4c8d63e5dded2db
|
||||
920b7eee36a2a
|
||||
Proof = cc203910175d786927eeb44ea847328047892ddf8590e723c37205cb7460
|
||||
0b0a5ab5337c8eb4ceae0494c2cf89529dcf94572ed267473d567aeed6ab873dee08
|
||||
ProofRandomScalar = 419c4f4f5052c53c45f3da494d2b67b220d02118e0857cdb
|
||||
cf037f9ea84bbe0c
|
||||
Output = b58cfbe118e0cb94d79b5fd6a6dafb98764dff49c14e1770b566e42402d
|
||||
a1a7da4d8527693914139caee5bd03903af43a491351d23b430948dd50cde10d32b3
|
||||
c,8a9a2f3c7f085b65933594309041fc1898d42d0858e59f90814ae90571a6df6035
|
||||
6f4610bf816f27afdd84f47719e480906d27ecd994985890e5f539e7ea74b6
|
||||
|
||||
A.1.3. POPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 145c79c108538421ac164ecbe131942136d5570b16d8bf41a24d4337da981
|
||||
e07
|
||||
pkSm = c647bef38497bc6ec077c22af65b696efa43bff3b4a1975a3e8e0a1c5a79d
|
||||
631
|
||||
|
||||
A.1.3.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
|
||||
6706
|
||||
BlindedElement = c8713aa89241d6989ac142f22dba30596db635c772cbf25021f
|
||||
dd8f3d461f715
|
||||
EvaluationElement = 1a4b860d808ff19624731e67b5eff20ceb2df3c3c03b906f
|
||||
5693e2078450d874
|
||||
Proof = 41ad1a291aa02c80b0915fbfbb0c0afa15a57e2970067a602ddb9e8fd6b7
|
||||
100de32e1ecff943a36f0b10e3dae6bd266cdeb8adf825d86ef27dbc6c0e30c52206
|
||||
ProofRandomScalar = 222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d98
|
||||
81aa6f61d645fc0e
|
||||
Output = ca688351e88afb1d841fde4401c79efebb2eb75e7998fa9737bd5a82a15
|
||||
2406d38bd29f680504e54fd4587eddcf2f37a2617ac2fbd2993f7bdf45442ace7d22
|
||||
1
|
||||
|
||||
A.1.3.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
|
||||
6706
|
||||
BlindedElement = f0f0b209dd4d5f1844dac679acc7761b91a2e704879656cb7c2
|
||||
01e82a99ab07d
|
||||
EvaluationElement = 8c3c9d064c334c6991e99f286ea2301d1bde170b54003fb9
|
||||
c44c6d7bd6fc1540
|
||||
Proof = 4c39992d55ffba38232cdac88fe583af8a85441fefd7d1d4a8d0394cd1de
|
||||
77018bf135c174f20281b3341ab1f453fe72b0293a7398703384bed822bfdeec8908
|
||||
ProofRandomScalar = 222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d98
|
||||
81aa6f61d645fc0e
|
||||
Output = 7c6557b276a137922a0bcfc2aa2b35dd78322bd500235eb6d6b6f91bc5b
|
||||
56a52de2d65612d503236b321f5d0bebcbc52b64b92e426f29c9b8b69f52de98ae50
|
||||
7
|
||||
|
||||
A.1.3.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
|
||||
6706,222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0
|
||||
e
|
||||
BlindedElement = c8713aa89241d6989ac142f22dba30596db635c772cbf25021f
|
||||
dd8f3d461f715,423a01c072e06eb1cce96d23acce06e1ea64a609d7ec9e9023f304
|
||||
9f2d64e50c
|
||||
EvaluationElement = 1a4b860d808ff19624731e67b5eff20ceb2df3c3c03b906f
|
||||
5693e2078450d874,aa1f16e903841036e38075da8a46655c94fc92341887eb5819f
|
||||
46312adfc0504
|
||||
Proof = 43fdb53be399cbd3561186ae480320caa2b9f36cca0e5b160c4a677b8bbf
|
||||
4301b28f12c36aa8e11e5a7ef551da0781e863a6dc8c0b2bf5a149c9e00621f02006
|
||||
ProofRandomScalar = 419c4f4f5052c53c45f3da494d2b67b220d02118e0857cdb
|
||||
cf037f9ea84bbe0c
|
||||
Output = ca688351e88afb1d841fde4401c79efebb2eb75e7998fa9737bd5a82a15
|
||||
2406d38bd29f680504e54fd4587eddcf2f37a2617ac2fbd2993f7bdf45442ace7d22
|
||||
1,7c6557b276a137922a0bcfc2aa2b35dd78322bd500235eb6d6b6f91bc5b56a52de
|
||||
2d65612d503236b321f5d0bebcbc52b64b92e426f29c9b8b69f52de98ae507
|
||||
|
||||
A.2. decaf448-SHAKE256
|
||||
|
||||
A.2.1. OPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = e8b1375371fd11ebeb224f832dcc16d371b4188951c438f751425699ed29e
|
||||
cc80c6c13e558ccd67634fd82eac94aa8d1f0d7fee990695d1e
|
||||
|
||||
A.2.1.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
|
||||
3833a26e9388336361686ff1f83df55046504dfecad8549ba112
|
||||
BlindedElement = e0ae01c4095f08e03b19baf47ffdc19cb7d98e583160522a3c7
|
||||
d6a0b2111cd93a126a46b7b41b730cd7fc943d4e28e590ed33ae475885f6c
|
||||
EvaluationElement = 50ce4e60eed006e22e7027454b5a4b8319eb2bc8ced609eb
|
||||
19eb3ad42fb19e06ba12d382cbe7ae342a0cad6ead0ef8f91f00bb7f0cd9c0a2
|
||||
Output = 37d3f7922d9388a15b561de5829bbf654c4089ede89c0ce0f3f85bcdba0
|
||||
9e382ce0ab3507e021f9e79706a1798ffeac68ebd5cf62e5eb9838c7068351d97ae3
|
||||
7
|
||||
|
||||
A.2.1.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
|
||||
3833a26e9388336361686ff1f83df55046504dfecad8549ba112
|
||||
BlindedElement = 86a88dc5c6331ecfcb1d9aacb50a68213803c462e377577cacc
|
||||
00af28e15f0ddbc2e3d716f2f39ef95f3ec1314a2c64d940a9f295d8f13bb
|
||||
EvaluationElement = 162e9fa6e9d527c3cd734a31bf122a34dbd5bcb7bb23651f
|
||||
1768a7a9274cc116c03b58afa6f0dede3994a60066c76370e7328e7062fd5819
|
||||
Output = a2a652290055cb0f6f8637a249ee45e32ef4667db0b4c80c0a70d2a6416
|
||||
4d01525cfdad5d870a694ec77972b9b6ec5d2596a5223e5336913f945101f0137f55
|
||||
e
|
||||
|
||||
A.2.2. VOPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = e3c01519a076a326a0eb566343e9b21c115fa18e6e85577ddbe890b33104f
|
||||
cc2835ddfb14a928dc3f5d79b936e17c76b99e0bf6a1680930e
|
||||
pkSm = 945fc518c47695cf65217ace04b86ac5e4cbe26ca649d52854bb16c494ce0
|
||||
9069d6add96b20d4b0ae311a87c9a73e3a146b525763ab2f955
|
||||
|
||||
A.2.2.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
|
||||
3833a26e9388336361686ff1f83df55046504dfecad8549ba112
|
||||
BlindedElement = 7261bbc335c664ba788f1b1a1a4cd5190cc30e787ef277665ac
|
||||
1d314f8861e3ec11854ce3ddd42035d9e0f5cddde324c332d8c880abc00eb
|
||||
EvaluationElement = ca1491a526c28d880806cf0fb0122222392cf495657be6e4
|
||||
c9d203bceffa46c86406caf8217859d3fb259077af68e5d41b3699410781f467
|
||||
Proof = f84bbeee47aedf43558dae4b95b3853635a9fc1a9ea7eac9b454c64c66c4
|
||||
f49cd1c72711c7ac2e06c681e16ea693d5500bbd7b56455df52f69e00b76b4126961
|
||||
e1562fdbaaac40b7701065cbeece3febbfe09e00160f81775d36daed99d8a2a10be0
|
||||
759e01b7ee81217203416c9db208
|
||||
ProofRandomScalar = b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0
|
||||
627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b
|
||||
Output = e2ac40b634f36cccd8262b285adff7c9dcc19cd308564a5f4e581d1a853
|
||||
5773b86fa4fc9f2203c370763695c5093aea4a7aedec4488b1340ba3bf663a23098c
|
||||
1
|
||||
|
||||
A.2.2.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
|
||||
3833a26e9388336361686ff1f83df55046504dfecad8549ba112
|
||||
BlindedElement = 88287e553939090b888ddc15913e1807dc4757215555e1c3a79
|
||||
488ef311594729c7fa74c772a732b78440b7d66d0aa35f3bb316f1d93e1b2
|
||||
EvaluationElement = c00978c73e8e4ee1d447ab0d3ad1754055e72cc85c08e3a0
|
||||
db170909a9c61cbff1f1e7015f289e3038b0f341faea5d7780c130106065c231
|
||||
Proof = 7a2831a6b237e11ac1657d440df93bc5ce00f552e6020a99d5c956ffc4d0
|
||||
7b5ade3e82ecdc257fd53d76239e733e0a1313e84ce16cc0d82734806092a693d7e8
|
||||
d3c420c2cb6ccd5d0ca32514fb78e9ad0973ebdcb52eba438fc73948d76339ee7101
|
||||
21d83e2fe6f001cfdf551aff9f36
|
||||
ProofRandomScalar = b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0
|
||||
627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b
|
||||
Output = 862952380e07ec840d9f6e6f909c5a25d16c3dacb586d89a181b4aa7380
|
||||
c959baa8c480fe8e6c64e089d68ea7aeeb5817bd524d7577905b5bab487690048c94
|
||||
1
|
||||
|
||||
A.2.2.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
|
||||
3833a26e9388336361686ff1f83df55046504dfecad8549ba112,b1b748135d405ce
|
||||
48c6973401d9455bb8ccd18b01d0295c0627f67661200dbf9569f73fbb3925daa043
|
||||
a070e5f953d80bb464ea369e5522b
|
||||
BlindedElement = 7261bbc335c664ba788f1b1a1a4cd5190cc30e787ef277665ac
|
||||
1d314f8861e3ec11854ce3ddd42035d9e0f5cddde324c332d8c880abc00eb,2e15f3
|
||||
93c035492a1573627a3606e528c6294c767c8d43b8c691ef70a52cc7dc7d1b53fe45
|
||||
8350a270abb7c231b87ba58266f89164f714d9
|
||||
EvaluationElement = ca1491a526c28d880806cf0fb0122222392cf495657be6e4
|
||||
c9d203bceffa46c86406caf8217859d3fb259077af68e5d41b3699410781f467,8ec
|
||||
68e9871b296e81c55647ce64a04fe75d19932f1400544cd601468c60f998408bbb54
|
||||
6601d4a636e8be279e558d70b95c8d4a4f61892be
|
||||
Proof = 167d922f0a6ffa845eed07f8aa97b6ac746d902ecbeb18f49c009adc0521
|
||||
eab1e4d275b74a2dc266b7a194c854e85e7eb54a9a36376dfc04ec7f3bd55fc9618c
|
||||
3970cb548e064f8a2f06183a5702933dbc3e4c25a73438f2108ee1981c306181003c
|
||||
7ea92fce963ec7b4ba4f270e6d38
|
||||
ProofRandomScalar = 63798726803c9451ba405f00ef3acb633ddf0c420574a2ec
|
||||
6cbf28f840800e355c9fbaac10699686de2724ed22e797a00f3bd93d105a7f23
|
||||
Output = e2ac40b634f36cccd8262b285adff7c9dcc19cd308564a5f4e581d1a853
|
||||
5773b86fa4fc9f2203c370763695c5093aea4a7aedec4488b1340ba3bf663a23098c
|
||||
1,862952380e07ec840d9f6e6f909c5a25d16c3dacb586d89a181b4aa7380c959baa
|
||||
8c480fe8e6c64e089d68ea7aeeb5817bd524d7577905b5bab487690048c941
|
||||
|
||||
A.2.3. POPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 792a10dcbd3ba4a52a054f6f39186623208695301e7adb9634b74709ab22d
|
||||
e402990eb143fd7c67ac66be75e0609705ecea800992aac8e19
|
||||
pkSm = 6c9d12723a5bbcf305522cc04b4a34d9ced2e12831826018ea7b5dcf54526
|
||||
47ad262113059bf0f6e4354319951b9d513c74f29cb0eec38c1
|
||||
|
||||
A.2.3.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
|
||||
3833a26e9388336361686ff1f83df55046504dfecad8549ba112
|
||||
BlindedElement = 161183c13c6cb33b0e4f9b7365f8c5c12d13c72f8b62d276ca0
|
||||
9368d093dce9b42198276b9e9d870ac392dda53efd28d1b7e6e8c060cdc42
|
||||
EvaluationElement = 06ec89dfde25bb2a6f0145ac84b91ac277b35de39ad1d6f4
|
||||
02a8e46414952ce0d9ea1311a4ece283e2b01558c7078b040cfaa40dd63b3e6c
|
||||
Proof = 66caee75bf2460429f620f6ad3e811d524cb8ddd848a435fc5d89af48877
|
||||
abf6506ee341a0b6f67c2d76cd021e5f3d1c9abe5aa9f0dce016da746135fedba2af
|
||||
41ed1d01659bfd6180d96bc1b7f320c0cb6926011ce392ecca748662564892bae665
|
||||
16acaac6ca39aadf6fcca95af406
|
||||
ProofRandomScalar = b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0
|
||||
627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b
|
||||
Output = 4423f6dcc1740688ea201de57d76824d59cd6b859e1f9884b7eebc49b0b
|
||||
971358cf9cb075df1536a8ea31bcf55c3e31c2ba9cfa8efe54448d17091daeb9924e
|
||||
d
|
||||
|
||||
A.2.3.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
|
||||
3833a26e9388336361686ff1f83df55046504dfecad8549ba112
|
||||
BlindedElement = 12082b6a381c6c51e85d00f2a3d828cdeab3f5cb19a10b9c014
|
||||
c33826764ab7e7cfb8b4ff6f411bddb2d64e62a472af1cd816e5b712790c6
|
||||
EvaluationElement = f2919b7eedc05ab807c221fce2b12c4ae9e19e6909c47845
|
||||
64b690d1972d2994ca623f273afc67444d84ea40cbc58fcdab7945f321a52848
|
||||
Proof = a295677c54d1bc4286330907fc2490a7de163da26f9ce03a462a452fea42
|
||||
2b19ade296ba031359b3b6841e48455d20519ad01b4ac4f0b92e76d3cf16fbef0a3f
|
||||
72791a8401ef2d7081d361e502e96b2c60608b9fa566f43d4611c2f161d83aabef7f
|
||||
8017332b26ed1daaf80440772022
|
||||
ProofRandomScalar = b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0
|
||||
627f67661200dbf9569f73fbb3925daa043a070e5f953d80bb464ea369e5522b
|
||||
Output = 8691905500510843902c44bdd9730ab9dc3925aa58ff9dd42765a2baf63
|
||||
3126de0c3adb93bef5652f38e5827b6396e87643960163a560fc4ac9738c8de4e4a8
|
||||
d
|
||||
|
||||
A.2.3.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
|
||||
3833a26e9388336361686ff1f83df55046504dfecad8549ba112,b1b748135d405ce
|
||||
48c6973401d9455bb8ccd18b01d0295c0627f67661200dbf9569f73fbb3925daa043
|
||||
a070e5f953d80bb464ea369e5522b
|
||||
BlindedElement = 161183c13c6cb33b0e4f9b7365f8c5c12d13c72f8b62d276ca0
|
||||
9368d093dce9b42198276b9e9d870ac392dda53efd28d1b7e6e8c060cdc42,fc8847
|
||||
d43fb4cea4e408f585661a8f2867533fa91d22155d3127a22f18d3b007add480f7d3
|
||||
00bca93fa47fe87ae06a57b7d0f0d4c30b12f0
|
||||
EvaluationElement = 06ec89dfde25bb2a6f0145ac84b91ac277b35de39ad1d6f4
|
||||
02a8e46414952ce0d9ea1311a4ece283e2b01558c7078b040cfaa40dd63b3e6c,2e7
|
||||
4c626d07de49b1c8c21d87120fd78105f485e36816af9bde3e3efbeef76815326062
|
||||
fd333925b66c5ce5a20f100bf01770c16609f990a
|
||||
Proof = fd94db736f97ea4efe9d0d4ad2933072697a6bbeb32834057b23edf7c700
|
||||
9f011dfa72157f05d2a507c2bbf0b54cad99ab99de05921c021fda7d70e65bcecdb0
|
||||
5f9a30154127ace983c74d10fd910b554c5e95f6bd1565fd1f3dbbe3c523ece5c72d
|
||||
57a559b7be1368c4786db4a3c910
|
||||
ProofRandomScalar = 63798726803c9451ba405f00ef3acb633ddf0c420574a2ec
|
||||
6cbf28f840800e355c9fbaac10699686de2724ed22e797a00f3bd93d105a7f23
|
||||
Output = 4423f6dcc1740688ea201de57d76824d59cd6b859e1f9884b7eebc49b0b
|
||||
971358cf9cb075df1536a8ea31bcf55c3e31c2ba9cfa8efe54448d17091daeb9924e
|
||||
d,8691905500510843902c44bdd9730ab9dc3925aa58ff9dd42765a2baf633126de0
|
||||
c3adb93bef5652f38e5827b6396e87643960163a560fc4ac9738c8de4e4a8d
|
||||
|
||||
A.3. P256-SHA256
|
||||
|
||||
A.3.1. OPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 159749d750713afe245d2d39ccfaae8381c53ce92d098a9375ee70739c7ac
|
||||
0bf
|
||||
|
||||
A.3.1.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 03723a1e5c09b8b9c18d1dcbca29e8007e95f14f4732d9346d4
|
||||
90ffc195110368d
|
||||
EvaluationElement = 030de02ffec47a1fd53efcdd1c6faf5bdc270912b8749e78
|
||||
3c7ca75bb412958832
|
||||
Output = a0b34de5fa4c5b6da07e72af73cc507cceeb48981b97b7285fc375345fe
|
||||
495dd
|
||||
|
||||
A.3.1.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 03cc1df781f1c2240a64d1c297b3f3d16262ef5d4cf10273488
|
||||
2675c26231b0838
|
||||
EvaluationElement = 03a0395fe3828f2476ffcd1f4fe540e5a8489322d398be3c
|
||||
4e5a869db7fcb7c52c
|
||||
Output = c748ca6dd327f0ce85f4ae3a8cd6d4d5390bbb804c9e12dcf94f853fece
|
||||
3dcce
|
||||
|
||||
A.3.2. VOPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = ca5d94c8807817669a51b196c34c1b7f8442fde4334a7121ae4736364312f
|
||||
ca6
|
||||
pkSm = 03e17e70604bcabe198882c0a1f27a92441e774224ed9c702e51dd17038b1
|
||||
02462
|
||||
|
||||
A.3.2.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 02dd05901038bb31a6fae01828fd8d0e49e35a486b5c5d4b499
|
||||
4013648c01277da
|
||||
EvaluationElement = 0209f33cab60cf8fe69239b0afbcfcd261af4c1c5632624f
|
||||
2e9ba29b90ae83e4a2
|
||||
Proof = e7c2b3c5c954c035949f1f74e6bce2ed539a3be267d1481e9ddb178533df
|
||||
4c2664f69d065c604a4fd953e100b856ad83804eb3845189babfa5a702090d6fc5fa
|
||||
ProofRandomScalar = f9db001266677f62c095021db018cd8cbb55941d4073698c
|
||||
e45c405d1348b7b1
|
||||
Output = 0412e8f78b02c415ab3a288e228978376f99927767ff37c5718d420010a
|
||||
645a1
|
||||
|
||||
A.3.2.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 03cd0f033e791c4d79dfa9c6ed750f2ac009ec46cd4195ca6fd
|
||||
3800d1e9b887dbd
|
||||
EvaluationElement = 030d2985865c693bf7af47ba4d3a3813176576383d19aff0
|
||||
03ef7b0784a0d83cf1
|
||||
Proof = 2787d729c57e3d9512d3aa9e8708ad226bc48e0f1750b0767aaff73482c4
|
||||
4b8d2873d74ec88aebd3504961acea16790a05c542d9fbff4fe269a77510db00abab
|
||||
ProofRandomScalar = f9db001266677f62c095021db018cd8cbb55941d4073698c
|
||||
e45c405d1348b7b1
|
||||
Output = 771e10dcd6bcd3664e23b8f2a710cfaaa8357747c4a8cbba03133967b5c
|
||||
24f18
|
||||
|
||||
A.3.2.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364,f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b
|
||||
1
|
||||
BlindedElement = 02dd05901038bb31a6fae01828fd8d0e49e35a486b5c5d4b499
|
||||
4013648c01277da,03462e9ae64cae5b83ba98a6b360d942266389ac369b923eb3d5
|
||||
57213b1922f8ab
|
||||
EvaluationElement = 0209f33cab60cf8fe69239b0afbcfcd261af4c1c5632624f
|
||||
2e9ba29b90ae83e4a2,02bb24f4d838414aef052a8f044a6771230ca69c0a5677540
|
||||
fff738dd31bb69771
|
||||
Proof = bdcc351707d02a72ce49511c7db990566d29d6153ad6f8982fad2b435d6c
|
||||
e4d60da1e6b3fa740811bde34dd4fe0aa1b5fe6600d0440c9ddee95ea7fad7a60cf2
|
||||
ProofRandomScalar = 350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba
|
||||
51943c8026877963
|
||||
Output = 0412e8f78b02c415ab3a288e228978376f99927767ff37c5718d420010a
|
||||
645a1,771e10dcd6bcd3664e23b8f2a710cfaaa8357747c4a8cbba03133967b5c24f
|
||||
18
|
||||
|
||||
A.3.3. POPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 6ad2173efa689ef2c27772566ad7ff6e2d59b3b196f00219451fb2c89ee4d
|
||||
ae2
|
||||
pkSm = 030d7ff077fddeec965db14b794f0cc1ba9019b04a2f4fcc1fa525dedf72e
|
||||
2a3e3
|
||||
|
||||
A.3.3.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 031563e127099a8f61ed51eeede05d747a8da2be329b40ba1f0
|
||||
db0b2bd9dd4e2c0
|
||||
EvaluationElement = 02c5e5300c2d9e6ba7f3f4ad60500ad93a0157e6288eb04b
|
||||
67e125db024a2c74d2
|
||||
Proof = f8a33690b87736c854eadfcaab58a59b8d9c03b569110b6f31f8bf7577f3
|
||||
fbb85a8a0c38468ccde1ba942be501654adb106167c8eb178703ccb42bccffb9231a
|
||||
ProofRandomScalar = f9db001266677f62c095021db018cd8cbb55941d4073698c
|
||||
e45c405d1348b7b1
|
||||
Output = 193a92520bd8fd1f37accb918040a57108daa110dc4f659abe212636d24
|
||||
5c592
|
||||
|
||||
A.3.3.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 021a440ace8ca667f261c10ac7686adc66a12be31e3520fca31
|
||||
7643a1eee9dcd4d
|
||||
EvaluationElement = 0208ca109cbae44f4774fc0bdd2783efdcb868cb4523d521
|
||||
96f700210e777c5de3
|
||||
Proof = 043a8fb7fc7fd31e35770cabda4753c5bf0ecc1e88c68d7d35a62bf2631e
|
||||
875af4613641be2d1875c31d1319d191c4bbc0d04875f4fd03c31d3d17dd8e069b69
|
||||
ProofRandomScalar = f9db001266677f62c095021db018cd8cbb55941d4073698c
|
||||
e45c405d1348b7b1
|
||||
Output = 1e6d164cfd835d88a31401623549bf6b9b306628ef03a7962921d62bc5f
|
||||
fce8c
|
||||
|
||||
A.3.3.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364,f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b
|
||||
1
|
||||
BlindedElement = 031563e127099a8f61ed51eeede05d747a8da2be329b40ba1f0
|
||||
db0b2bd9dd4e2c0,03ca4ff41c12fadd7a0bc92cf856732b21df652e01a3abdf0fa8
|
||||
847da053db213c
|
||||
EvaluationElement = 02c5e5300c2d9e6ba7f3f4ad60500ad93a0157e6288eb04b
|
||||
67e125db024a2c74d2,02f0b6bcd467343a8d8555a99dc2eed0215c71898c5edb77a
|
||||
3d97ddd0dbad478e8
|
||||
Proof = 8fbd85a32c13aba79db4b42e762c00687d6dbf9c8cb97b2a225645ccb00d
|
||||
9d7580b383c885cdfd07df448d55e06f50f6173405eee5506c0ed0851ff718d13e68
|
||||
ProofRandomScalar = 350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba
|
||||
51943c8026877963
|
||||
Output = 193a92520bd8fd1f37accb918040a57108daa110dc4f659abe212636d24
|
||||
5c592,1e6d164cfd835d88a31401623549bf6b9b306628ef03a7962921d62bc5ffce
|
||||
8c
|
||||
|
||||
A.4. P384-SHA384
|
||||
|
||||
A.4.1. OPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = dfe7ddc41a4646901184f2b432616c8ba6d452f9bcd0c4f75a5150ef2b2ed
|
||||
02ef40b8b92f60ae591bcabd72a6518f188
|
||||
|
||||
A.4.1.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
|
||||
889d89dbfa691d1cde91517fa222ed7ad364
|
||||
BlindedElement = 02a36bc90e6db34096346eaf8b7bc40ee1113582155ad379700
|
||||
3ce614c835a874343701d3f2debbd80d97cbe45de6e5f1f
|
||||
EvaluationElement = 03af2a4fc94770d7a7bf3187ca9cc4faf3732049eded2442
|
||||
ee50fbddda58b70ae2999366f72498cdbc43e6f2fc184afe30
|
||||
Output = ed84ad3f31a552f0456e58935fcc0a3039db42e7f356dcb32aa6d487b6b
|
||||
815a07d5813641fb1398c03ddab5763874357
|
||||
|
||||
A.4.1.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
|
||||
889d89dbfa691d1cde91517fa222ed7ad364
|
||||
BlindedElement = 02def6f418e3484f67a124a2ce1bfb19de7a4af568ede6a1ebb
|
||||
2733882510ddd43d05f2b1ab5187936a55e50a847a8b900
|
||||
EvaluationElement = 034e9b9a2960b536f2ef47d8608b21597ba400d5abfa1825
|
||||
fd21c36b75f927f396bf3716c96129d1fa4a77fa1d479c8d7b
|
||||
Output = dd4f29da869ab9355d60617b60da0991e22aaab243a3460601e48b07585
|
||||
9d1c526d36597326f1b985778f781a1682e75
|
||||
|
||||
A.4.2. VOPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 051646b9e6e7a71ae27c1e1d0b87b4381db6d3595eeeb1adb41579adbf992
|
||||
f4278f9016eafc944edaa2b43183581779d
|
||||
pkSm = 031d689686c611991b55f1a1d8f4305ccd6cb719446f660a30db61b7aa87b
|
||||
46acf59b7c0d4a9077b3da21c25dd482229a0
|
||||
|
||||
A.4.2.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
|
||||
889d89dbfa691d1cde91517fa222ed7ad364
|
||||
BlindedElement = 02d338c05cbecb82de13d6700f09cb61190543a7b7e2c6cd4fc
|
||||
a56887e564ea82653b27fdad383995ea6d02cf26d0e24d9
|
||||
EvaluationElement = 02a7bba589b3e8672aa19e8fd258de2e6aae20101c8d7612
|
||||
46de97a6b5ee9cf105febce4327a326255a3c604f63f600ef6
|
||||
Proof = bfc6cf3859127f5fe25548859856d6b7fa1c7459f0ba5712a806fc091a30
|
||||
00c42d8ba34ff45f32a52e40533efd2a03bc87f3bf4f9f58028297ccb9ccb18ae718
|
||||
2bcd1ef239df77e3be65ef147f3acf8bc9cbfc5524b702263414f043e3b7ca2e
|
||||
ProofRandomScalar = 803d955f0e073a04aa5d92b3fb739f56f9db001266677f62
|
||||
c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
|
||||
Output = 3333230886b562ffb8329a8be08fea8025755372817ec969d114d1203d0
|
||||
26b4a622beab60220bf19078bca35a529b35c
|
||||
|
||||
A.4.2.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
|
||||
889d89dbfa691d1cde91517fa222ed7ad364
|
||||
BlindedElement = 02f27469e059886f221be5f2cca03d2bdc61e55221721c3b3e5
|
||||
6fc012e36d31ae5f8dc058109591556a6dbd3a8c69c433b
|
||||
EvaluationElement = 03f16f903947035400e96b7f531a38d4a07ac89a80f89d86
|
||||
a1bf089c525a92c7f4733729ca30c56ce78b1ab4f7d92db8b4
|
||||
Proof = d005d6daaad7571414c1e0c75f7e57f2113ca9f4604e84bc90f9be52da89
|
||||
6fff3bee496dcde2a578ae9df315032585f801fb21c6080ac05672b291e575a40295
|
||||
b306d967717b28e08fcc8ad1cab47845d16af73b3e643ddcc191208e71c64630
|
||||
ProofRandomScalar = 803d955f0e073a04aa5d92b3fb739f56f9db001266677f62
|
||||
c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
|
||||
Output = b91c70ea3d4d62ba922eb8a7d03809a441e1c3c7af915cbc2226f485213
|
||||
e895942cd0f8580e6d99f82221e66c40d274f
|
||||
|
||||
A.4.2.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
|
||||
889d89dbfa691d1cde91517fa222ed7ad364,803d955f0e073a04aa5d92b3fb739f5
|
||||
6f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
|
||||
BlindedElement = 02d338c05cbecb82de13d6700f09cb61190543a7b7e2c6cd4fc
|
||||
a56887e564ea82653b27fdad383995ea6d02cf26d0e24d9,02fa02470d7f151018b4
|
||||
1e82223c32fad824de6ad4b5ce9f8e9f98083c9a726de9a1fc39d7a0cb6f4f188dd9
|
||||
cea01474cd
|
||||
EvaluationElement = 02a7bba589b3e8672aa19e8fd258de2e6aae20101c8d7612
|
||||
46de97a6b5ee9cf105febce4327a326255a3c604f63f600ef6,028e9e115625ff4c2
|
||||
f07bf87ce3fd73fc77994a7a0c1df03d2a630a3d845930e2e63a165b114d98fe34e6
|
||||
1b68d23c0b50a
|
||||
Proof = 6d8dcbd2fc95550a02211fb78afd013933f307d21e7d855b0b1ed0af7807
|
||||
6d8137ad8b0a1bfa05676d325249c1dbb9a52bd81b1c2b7b0efc77cf7b278e1c947f
|
||||
6283f1d4c513053fc0ad19e026fb0c30654b53d9cea4b87b037271b5d2e2d0ea
|
||||
ProofRandomScalar = a097e722ed2427de86966910acba9f5c350e8040f828bf6c
|
||||
eca27405420cdf3d63cb3aef005f40ba51943c8026877963
|
||||
Output = 3333230886b562ffb8329a8be08fea8025755372817ec969d114d1203d0
|
||||
26b4a622beab60220bf19078bca35a529b35c,b91c70ea3d4d62ba922eb8a7d03809
|
||||
a441e1c3c7af915cbc2226f485213e895942cd0f8580e6d99f82221e66c40d274f
|
||||
|
||||
A.4.3. POPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 5b2690d6954b8fbb159f19935d64133f12770c00b68422559c65431942d72
|
||||
1ff79d47d7a75906c30b7818ec0f38b7fb2
|
||||
pkSm = 02f00f0f1de81e5d6cf18140d4926ffdc9b1898c48dc49657ae36eb1e45de
|
||||
b8b951aaf1f10c82d2eaa6d02aafa3f10d2b6
|
||||
|
||||
A.4.3.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
|
||||
889d89dbfa691d1cde91517fa222ed7ad364
|
||||
BlindedElement = 03859b36b95e6564faa85cd3801175eda2949707f6aa0640ad0
|
||||
93cbf8ad2f58e762f08b56b2a1b42a64953aaf49cbf1ae3
|
||||
EvaluationElement = 0220710e2e00306453f5b4f574cb6a512453f35c45080d09
|
||||
373e190c19ce5b185914fbf36582d7e0754bb7c8b683205b91
|
||||
Proof = 82a17ef41c8b57f1e3122311b4d5cd39a63df0f67443ef18d961f9b659c1
|
||||
601ced8d3c64b294f604319ca80230380d437a49c7af0d620e22116669c008ebb767
|
||||
d90283d573b49cdb49e3725889620924c2c4b047a2a6225a3ba27e640ebddd33
|
||||
ProofRandomScalar = 803d955f0e073a04aa5d92b3fb739f56f9db001266677f62
|
||||
c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
|
||||
Output = 0188653cfec38119a6c7dd7948b0f0720460b4310e40824e048bf82a165
|
||||
27303ed449a08caf84272c3bbc972ede797df
|
||||
|
||||
A.4.3.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
|
||||
889d89dbfa691d1cde91517fa222ed7ad364
|
||||
BlindedElement = 03f7efcb4aaf000263369d8a0621cb96b81b3206e99876de2a0
|
||||
0699ed4c45acf3969cd6e2319215395955d3f8d8cc1c712
|
||||
EvaluationElement = 034993c818369927e74b77c400376fd1ae29b6ac6c6ddb77
|
||||
6cf10e4fbc487826531b3cf0b7c8ca4d92c7af90c9def85ce6
|
||||
Proof = 693471b5dff0cd6a5c00ea34d7bf127b2795164e3bdb5f39a1e5edfbd13e
|
||||
443bc516061cd5b8449a473c2ceeccada9f3e5b57302e3d7bc5e28d38d6e3a3056e1
|
||||
e73b6cc030f5180f8a1ffa45aa923ee66d2ad0a07b500f2acc7fb99b5506465c
|
||||
ProofRandomScalar = 803d955f0e073a04aa5d92b3fb739f56f9db001266677f62
|
||||
c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
|
||||
Output = ff2a527a21cc43b251a567382677f078c6e356336aec069dea8ba369953
|
||||
43ca3b33bb5d6cf15be4d31a7e6d75b30d3f5
|
||||
|
||||
A.4.3.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
|
||||
889d89dbfa691d1cde91517fa222ed7ad364,803d955f0e073a04aa5d92b3fb739f5
|
||||
6f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
|
||||
BlindedElement = 03859b36b95e6564faa85cd3801175eda2949707f6aa0640ad0
|
||||
93cbf8ad2f58e762f08b56b2a1b42a64953aaf49cbf1ae3,021a65d618d645f1a20b
|
||||
c33b06deaa7e73d6d634c8a56a3d02b53a732b69a5c53c5a207ea33d5afdcde9a22d
|
||||
59726bce51
|
||||
EvaluationElement = 0220710e2e00306453f5b4f574cb6a512453f35c45080d09
|
||||
373e190c19ce5b185914fbf36582d7e0754bb7c8b683205b91,02017657b315ec65e
|
||||
f861505e596c8645d94685dd7602cdd092a8f1c1c0194a5d0485fe47d071d972ab51
|
||||
4370174cc23f5
|
||||
Proof = 4a0b2fe96d5b2a046a0447fe079b77859ef11a39a3520d6ff7c626aad9b4
|
||||
73b724fb0cf188974ec961710a62162a83e97e0baa9eeada73397032d928b3e97b1e
|
||||
a92ad9458208302be3681b8ba78bcc17745bac00f84e0fdc98a6a8cba009c080
|
||||
ProofRandomScalar = a097e722ed2427de86966910acba9f5c350e8040f828bf6c
|
||||
eca27405420cdf3d63cb3aef005f40ba51943c8026877963
|
||||
Output = 0188653cfec38119a6c7dd7948b0f0720460b4310e40824e048bf82a165
|
||||
27303ed449a08caf84272c3bbc972ede797df,ff2a527a21cc43b251a567382677f0
|
||||
78c6e356336aec069dea8ba36995343ca3b33bb5d6cf15be4d31a7e6d75b30d3f5
|
||||
|
||||
A.5. P521-SHA512
|
||||
|
||||
A.5.1. OPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 0153441b8faedb0340439036d6aed06d1217b34c42f17f8db4c5cc610a4a9
|
||||
55d698a688831b16d0dc7713a1aa3611ec60703bffc7dc9c84e3ed673b3dbe1d5fcc
|
||||
ea6
|
||||
|
||||
A.5.1.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
|
||||
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 0300e78bf846b0e1e1a3c320e353d758583cd876df56100a3a1
|
||||
e62bacba470fa6e0991be1be80b721c50c5fd0c672ba764457acc18c6200704e9294
|
||||
fbf28859d916351
|
||||
EvaluationElement = 030166371cf827cb2fb9b581f97907121a16e2dc5d8b10ce
|
||||
9f0ede7f7d76a0d047657735e8ad07bcda824907b3e5479bd72cdef6b839b967ba5c
|
||||
58b118b84d26f2ba07
|
||||
Output = 26232de6fff83f812adadadb6cc05d7bbeee5dca043dbb16b03488abb99
|
||||
81d0a1ef4351fad52dbd7e759649af393348f7b9717566c19a6b8856284d69375c80
|
||||
9
|
||||
|
||||
A.5.1.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
|
||||
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 0300c28e57e74361d87e0c1874e5f7cc1cc796d61f9cad50427
|
||||
cf54655cdb455613368d42b27f94bf66f59f53c816db3e95e68e1b113443d66a99b3
|
||||
693bab88afb556b
|
||||
EvaluationElement = 0301ad453607e12d0cc11a3359332a40c3a254eaa1afc642
|
||||
96528d55bed07ba322e72e22cf3bcb50570fd913cb54f7f09c17aff8787af75f6a7f
|
||||
af5640cbb2d9620a6e
|
||||
Output = ad1f76ef939042175e007738906ac0336bbd1d51e287ebaa66901abdd32
|
||||
4ea3ffa40bfc5a68e7939c2845e0fd37a5a6e76dadb9907c6cc8579629757fd4d04b
|
||||
a
|
||||
|
||||
A.5.2. VOPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 015c7fc1b4a0b1390925bae915bd9f3d72009d44d9241b962428aad5d13f2
|
||||
2803311e7102632a39addc61ea440810222715c9d2f61f03ea424ec9ab1fe5e31cf9
|
||||
238
|
||||
pkSm = 0301505d646f6e4c9102451eb39730c4ba1c4087618641edbdba4a60896b0
|
||||
7fd0c9414ce553cbf25b81dfcca50a8f6724ab7a2bc4d0cf736967a287bb6084cc06
|
||||
78ac0
|
||||
|
||||
A.5.2.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
|
||||
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 0301d6e4fb545e043ddb6aee5d5ceeee1b44102615ab04430c2
|
||||
7dd0f56988dedcb1df32ef384f160e0e76e718605f14f3f582f9357553d153b99679
|
||||
5b4b3628a4f6380
|
||||
EvaluationElement = 03013fdeaf887f3d3d283a79e696a54b66ff0edcb559265e
|
||||
204a958acf840e0930cc147e2a6835148d8199eebc26c03e9394c9762a1c991dde40
|
||||
bca0f8ca003eefb045
|
||||
Proof = 0077fcc8ec6d059d7759b0a61f871e7c1dadc65333502e09a51994328f79
|
||||
e5bda3357b9a4f410a1760a3612c2f8f27cb7cb032951c047cc66da60da583df7b24
|
||||
7edd0188e5eb99c71799af1d80d643af16ffa1545acd9e9233fbb370455b10eb257e
|
||||
a12a1667c1b4ee5b0ab7c93d50ae89602006960f083ca9adc4f6276c0ad60440393c
|
||||
ProofRandomScalar = 015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e07
|
||||
3a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698c
|
||||
e45c405d1348b7b1
|
||||
Output = 5e003d9b2fb540b3d4bab5fedd154912246da1ee5e557afd8f56415faa1
|
||||
a0fadff6517da802ee254437e4f60907b4cda146e7ba19e249eef7be405549f62954
|
||||
b
|
||||
|
||||
A.5.2.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
|
||||
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 03005b05e656cb609ce5ff5faf063bb746d662d67bbd07c0626
|
||||
38396f52f0392180cf2365cabb0ece8e19048961d35eeae5d5fa872328dce98df076
|
||||
ee154dd191c615e
|
||||
EvaluationElement = 0301b19fcf482b1fff04754e282292ed736c5f0aa080d4f4
|
||||
2663cd3a416c6596f03129e8e096d8671fe5b0d19838312c511d2ce08d431e43e3ef
|
||||
06199d8cab7426238d
|
||||
Proof = 01ec9fece444caa6a57032e8963df0e945286f88fbdf233fb5101f0924f7
|
||||
ea89c47023f5f72f240e61991fd33a299b5b38c45a5e2dd1a67b072e59dfe86708a3
|
||||
59c701e38d383c60cf6969463bcf13251bedad47b7941f52e409a3591398e2792441
|
||||
0b18a301c0e19f527cad504fa08388050ac634e1b05c5216d337742f2754e1fc502f
|
||||
ProofRandomScalar = 015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e07
|
||||
3a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698c
|
||||
e45c405d1348b7b1
|
||||
Output = fa15eebba81ecf40954f7135cb76f69ef22c6bae394d1a4362f9b03066b
|
||||
54b6604d39f2e53369ca6762a3d9787e230e832aa85955af40ecb8deebb009a8cf47
|
||||
4
|
||||
|
||||
A.5.2.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
|
||||
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364,015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb7
|
||||
39f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b
|
||||
1
|
||||
BlindedElement = 0301d6e4fb545e043ddb6aee5d5ceeee1b44102615ab04430c2
|
||||
7dd0f56988dedcb1df32ef384f160e0e76e718605f14f3f582f9357553d153b99679
|
||||
5b4b3628a4f6380,0301403b597538b939b450c93586ba275f9711ba07e42364bac1
|
||||
d5769c6824a8b55be6f9a536df46d952b11ab2188363b3d6737635d9543d4dba14a6
|
||||
e19421b9245bf5
|
||||
EvaluationElement = 03013fdeaf887f3d3d283a79e696a54b66ff0edcb559265e
|
||||
204a958acf840e0930cc147e2a6835148d8199eebc26c03e9394c9762a1c991dde40
|
||||
bca0f8ca003eefb045,03001f96424497e38c46c904978c2fa1636c5c3dd2e634a85
|
||||
d8a7265977c5dce1f02c7e6c118479f0751767b91a39cce6561998258591b5d7c1bb
|
||||
02445a9e08e4f3e8d
|
||||
Proof = 00b4d215c8405e57c7a4b53398caf55f1f1623aaeb22408ddb9ea2913090
|
||||
9b3f95dbb1ff366e81e86e918f9f2fd8b80dbb344cd498c9499d112905e585417e00
|
||||
68c600fe5dea18b389ef6c4cc062935607b8ccbbb9a84fba3143868a3e8a58efa0bf
|
||||
6ca642804d09dc06e980f64837811227c4267b217f1099a4e28b0854f4e5ee659796
|
||||
ProofRandomScalar = 01ec21c7bb69b0734cb48dfd68433dd93b0fa097e722ed24
|
||||
27de86966910acba9f5c350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba
|
||||
51943c8026877963
|
||||
Output = 5e003d9b2fb540b3d4bab5fedd154912246da1ee5e557afd8f56415faa1
|
||||
a0fadff6517da802ee254437e4f60907b4cda146e7ba19e249eef7be405549f62954
|
||||
b,fa15eebba81ecf40954f7135cb76f69ef22c6bae394d1a4362f9b03066b54b6604
|
||||
d39f2e53369ca6762a3d9787e230e832aa85955af40ecb8deebb009a8cf474
|
||||
|
||||
A.5.3. POPRF Mode
|
||||
|
||||
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
KeyInfo = 74657374206b6579
|
||||
skSm = 014893130030ce69cf714f536498a02ff6b396888f9bb507985c32928c442
|
||||
7d6d39de10ef509aca4240e8569e3a88debc0d392e3361bcd934cb9bdd59e339dff7
|
||||
b27
|
||||
pkSm = 0301de8ceb9ffe9237b1bba87c320ea0bebcfc3447fe6f278065c6c69886d
|
||||
692d1126b79b6844f829940ace9b52a5e26882cf7cbc9e57503d4cca3cd834584729
|
||||
f812a
|
||||
|
||||
A.5.3.1. Test Vector 1, Batch Size 1
|
||||
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
|
||||
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 020095cff9d7ecf65bdfee4ea92d6e748d60b02de34ad98094f
|
||||
82e25d33a8bf50138ccc2cc633556f1a97d7ea9438cbb394df612f041c485a515849
|
||||
d5ebb2238f2f0e2
|
||||
EvaluationElement = 0301408e9c5be3ffcc1c16e5ae8f8aa68446223b0804b119
|
||||
62e856af5a6d1c65ebbb5db7278c21db4e8cc06d89a35b6804fb1738a295b691638a
|
||||
f77aa1327253f26d01
|
||||
Proof = 0106a89a61eee9dd2417d2849a8e2167bc5f56e3aed5a3ff23e22511fa1b
|
||||
37a29ed44d1bbfd6907d99cfbc558a56aec709282415a864a281e49dc53792a4a638
|
||||
a0660034306d64be12a94dcea5a6d664cf76681911c8b9a84d49bf12d4893307ec14
|
||||
436bd05f791f82446c0de4be6c582d373627b51886f76c4788256e3da7ec8fa18a86
|
||||
ProofRandomScalar = 015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e07
|
||||
3a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698c
|
||||
e45c405d1348b7b1
|
||||
Output = 808ae5b87662eaaf0b39151dd85991b94c96ef214cb14a68bf5c1439548
|
||||
82d330da8953a80eea20788e552bc8bbbfff3100e89f9d6e341197b122c46a208733
|
||||
b
|
||||
|
||||
A.5.3.2. Test Vector 2, Batch Size 1
|
||||
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
|
||||
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364
|
||||
BlindedElement = 030112ea89cf9cf589496189eafc5f9eb13c9f9e170d6ecde7c
|
||||
5b940541cb1a9c5cfeec908b67efe16b81ca00d0ce216e34b3d5f46a658d3fd8573d
|
||||
671bdb6515ed508
|
||||
EvaluationElement = 0200ebc49df1e6fa61f412e6c391e6f074400ecdd2f56c4a
|
||||
8c03fe0f91d9b551f40d4b5258fd891952e8c9b28003bcfa365122e54a5714c8949d
|
||||
5d202767b31b4bf1f6
|
||||
Proof = 0082162c71a7765005cae202d4bd14b84dae63c29067e886b82506992bd9
|
||||
94a1c3aac0c1c5309222fe1af8287b6443ed6df5c2e0b0991faddd3564c73c7597ae
|
||||
cd9a003b1f1e3c65f28e58ab4e767cfb4adbcaf512441645f4c2aed8bf67d132d966
|
||||
006d35fa71a34145414bf3572c1de1a46c266a344dd9e22e7fb1e90ffba1caf556d9
|
||||
ProofRandomScalar = 015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e07
|
||||
3a04aa5d92b3fb739f56f9db001266677f62c095021db018cd8cbb55941d4073698c
|
||||
e45c405d1348b7b1
|
||||
Output = 27032e24b1a52a82ab7f4646f3c5df0f070f499db98b9c5df33972bd5af
|
||||
5762c3638afae7912a6c1acdb1ae2ab2fa670bd5486c645a0e55412e08d33a4a0d6e
|
||||
3
|
||||
|
||||
A.5.3.3. Test Vector 3, Batch Size 2
|
||||
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
|
||||
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
|
||||
d364,015e80ae32363b32cb76ad4b95a5a34e46bb803d955f0e073a04aa5d92b3fb7
|
||||
39f56f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b
|
||||
1
|
||||
BlindedElement = 020095cff9d7ecf65bdfee4ea92d6e748d60b02de34ad98094f
|
||||
82e25d33a8bf50138ccc2cc633556f1a97d7ea9438cbb394df612f041c485a515849
|
||||
d5ebb2238f2f0e2,0201a328cf9f3fdeb86b6db242dd4cbb436b3a488b70b72d2fbb
|
||||
d1e5f50d7b0878b157d6f278c6a95c488f3ad52d6898a421658a82fe7ceb000b01ae
|
||||
dea7967522d525
|
||||
EvaluationElement = 0301408e9c5be3ffcc1c16e5ae8f8aa68446223b0804b119
|
||||
62e856af5a6d1c65ebbb5db7278c21db4e8cc06d89a35b6804fb1738a295b691638a
|
||||
f77aa1327253f26d01,020062ab51ac3aa829e0f5b7ae50688bcf5f63a18a83a6e0d
|
||||
a538666b8d50c7ea2b4ef31f4ac669302318dbebe46660acdda695da30c22cee7ca2
|
||||
1f6984a720504502e
|
||||
Proof = 00731738844f739bca0cca9d1c8bea204bed4fd00285785738b985763741
|
||||
de5cdfa275152d52b6a2fdf7792ef3779f39ba34581e56d62f78ecad5b7f8083f384
|
||||
961501cd4b43713253c022692669cf076b1d382ecd8293c1de69ea569737f37a2477
|
||||
2ab73517983c1e3db5818754ba1f008076267b8058b6481949ae346cdc17a8455fe2
|
||||
ProofRandomScalar = 01ec21c7bb69b0734cb48dfd68433dd93b0fa097e722ed24
|
||||
27de86966910acba9f5c350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba
|
||||
51943c8026877963
|
||||
Output = 808ae5b87662eaaf0b39151dd85991b94c96ef214cb14a68bf5c1439548
|
||||
82d330da8953a80eea20788e552bc8bbbfff3100e89f9d6e341197b122c46a208733
|
||||
b,27032e24b1a52a82ab7f4646f3c5df0f070f499db98b9c5df33972bd5af5762c36
|
||||
38afae7912a6c1acdb1ae2ab2fa670bd5486c645a0e55412e08d33a4a0d6e3
|
||||
"#;
|
||||
+10
-16
@@ -1,27 +1,27 @@
|
||||
// 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
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// This source code is dual-licensed under either the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::cmp::min;
|
||||
use rand::{CryptoRng, Error, RngCore};
|
||||
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
/// A simple implementation of `RngCore` for testing purposes.
|
||||
///
|
||||
/// This generates a cyclic sequence (i.e. cycles over an initial buffer)
|
||||
///
|
||||
///
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CycleRng {
|
||||
v: Vec<u8>,
|
||||
}
|
||||
|
||||
impl CycleRng {
|
||||
/// Create a `CycleRng`, yielding a sequence starting with
|
||||
/// `initial` and looping thereafter
|
||||
/// Create a `CycleRng`, yielding a sequence starting with `initial` and
|
||||
/// looping thereafter
|
||||
pub fn new(initial: Vec<u8>) -> Self {
|
||||
CycleRng { v: initial }
|
||||
}
|
||||
@@ -51,15 +51,9 @@ impl RngCore for CycleRng {
|
||||
#[inline]
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
let len = min(self.v.len(), dest.len());
|
||||
(&mut dest[..len]).copy_from_slice(&self.v[..len]);
|
||||
dest[..len].copy_from_slice(&self.v[..len]);
|
||||
rotate_left(&mut self.v, len);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
|
||||
self.fill_bytes(dest);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// This is meant for testing only
|
||||
|
||||
+7
-21
@@ -1,26 +1,12 @@
|
||||
// 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
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// This source code is dual-licensed under either the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
|
||||
mod cfrg_vectors;
|
||||
mod mock_rng;
|
||||
mod parser;
|
||||
mod voprf_test_vectors;
|
||||
mod voprf_vectors;
|
||||
|
||||
/// Ciphersuite definitions for tests
|
||||
pub(crate) struct Ristretto255Sha512;
|
||||
impl crate::CipherSuite for Ristretto255Sha512 {
|
||||
type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
type Hash = sha2::Sha512;
|
||||
}
|
||||
|
||||
#[cfg(feature = "p256")]
|
||||
pub(crate) struct P256Sha256;
|
||||
#[cfg(feature = "p256")]
|
||||
impl crate::CipherSuite for P256Sha256 {
|
||||
type Group = p256_::ProjectivePoint;
|
||||
type Hash = sha2::Sha256;
|
||||
}
|
||||
mod test_cfrg_vectors;
|
||||
|
||||
+17
-14
@@ -1,18 +1,21 @@
|
||||
// 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
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// This source code is dual-licensed under either the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
|
||||
use alloc::string::String;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{format, vec};
|
||||
|
||||
pub(crate) fn rfc_to_json(input: &str) -> String {
|
||||
format!("{{\n{}\n}}", parse_ciphersuites(input))
|
||||
}
|
||||
|
||||
fn parse_ciphersuites(input: &str) -> String {
|
||||
let re = regex::Regex::new(r"## OPRF\((?P<ciphersuite>.+?)\)").unwrap();
|
||||
let re = regex::Regex::new(r"\nA\.\d\. (?P<ciphersuite>.+?)\n\n").unwrap();
|
||||
let mut ciphersuites = vec![];
|
||||
|
||||
let chunks: Vec<&str> = re.split(input).collect();
|
||||
@@ -20,7 +23,7 @@ fn parse_ciphersuites(input: &str) -> String {
|
||||
for caps in re.captures_iter(input) {
|
||||
let ciphersuite = format!(
|
||||
"\"{}\": {{ {} }}",
|
||||
caps["ciphersuite"].to_string(),
|
||||
&caps["ciphersuite"],
|
||||
parse_modes(chunks[count])
|
||||
);
|
||||
ciphersuites.push(ciphersuite);
|
||||
@@ -31,7 +34,7 @@ fn parse_ciphersuites(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\. (?P<mode>.*?) Mode").unwrap();
|
||||
let mut modes = vec![];
|
||||
|
||||
let chunks: Vec<&str> = re.split(input).collect();
|
||||
@@ -39,7 +42,7 @@ fn parse_modes(input: &str) -> String {
|
||||
for caps in re.captures_iter(input) {
|
||||
let mode = format!(
|
||||
"\"{}\": [\n {} \n]",
|
||||
caps["mode"].to_string(),
|
||||
&caps["mode"],
|
||||
parse_vectors(chunks[count])
|
||||
);
|
||||
modes.push(mode);
|
||||
@@ -50,7 +53,7 @@ fn parse_modes(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\. Test Vector.*+\n").unwrap();
|
||||
let mut vectors = vec![];
|
||||
|
||||
let chunks: Vec<&str> = re.split(input).collect();
|
||||
@@ -85,7 +88,7 @@ fn parse_params(input: &str) -> String {
|
||||
// If line contains =, then
|
||||
if line.contains('=') {
|
||||
// Clear out any existing string and flush to params
|
||||
if param.len() > 0 {
|
||||
if !param.is_empty() {
|
||||
param += "\"";
|
||||
params.push(param);
|
||||
}
|
||||
@@ -94,14 +97,14 @@ fn parse_params(input: &str) -> String {
|
||||
let key = iter.next().unwrap().split_whitespace().next().unwrap();
|
||||
let val = iter.next().unwrap().split_whitespace().next().unwrap();
|
||||
|
||||
param = format!(" \"{}\": \"{}", key, val);
|
||||
param = format!(" \"{key}\": \"{val}");
|
||||
} else {
|
||||
let s = line.trim().to_string();
|
||||
if s.contains("~") || s.contains("#") {
|
||||
if s.contains('~') || s.contains('#') {
|
||||
// Ignore comment lines
|
||||
continue;
|
||||
}
|
||||
if s.len() > 0 {
|
||||
if !s.is_empty() {
|
||||
param += &s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,528 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
//
|
||||
// This source code is dual-licensed under either the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree. You may select, at your option, one of the above-listed
|
||||
// licenses.
|
||||
|
||||
use alloc::string::String;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::tests::mock_rng::CycleRng;
|
||||
use crate::tests::parser::*;
|
||||
use crate::{
|
||||
BlindedElement, CipherSuite, EvaluationElement, Group, OprfClient, OprfServer, PoprfClient,
|
||||
PoprfServer, PoprfServerBatchEvaluateFinishResult, PoprfServerBatchEvaluatePrepareResult,
|
||||
Proof, Result, VoprfClient, VoprfServer, VoprfServerBatchEvaluateFinishResult,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct VOPRFTestVectorParameters {
|
||||
seed: Vec<u8>,
|
||||
sksm: Vec<u8>,
|
||||
pksm: Vec<u8>,
|
||||
input: Vec<Vec<u8>>,
|
||||
info: Vec<u8>,
|
||||
key_info: Vec<u8>,
|
||||
blind: Vec<Vec<u8>>,
|
||||
blinded_element: Vec<Vec<u8>>,
|
||||
evaluation_element: Vec<Vec<u8>>,
|
||||
proof: Vec<u8>,
|
||||
proof_random_scalar: Vec<u8>,
|
||||
output: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
fn populate_test_vectors(values: &Value) -> VOPRFTestVectorParameters {
|
||||
VOPRFTestVectorParameters {
|
||||
seed: decode(values, "Seed"),
|
||||
sksm: decode(values, "skSm"),
|
||||
pksm: decode(values, "pkSm"),
|
||||
input: decode_vec(values, "Input"),
|
||||
info: decode(values, "Info"),
|
||||
key_info: decode(values, "KeyInfo"),
|
||||
blind: decode_vec(values, "Blind"),
|
||||
blinded_element: decode_vec(values, "BlindedElement"),
|
||||
evaluation_element: decode_vec(values, "EvaluationElement"),
|
||||
proof: decode(values, "Proof"),
|
||||
proof_random_scalar: decode(values, "ProofRandomScalar"),
|
||||
output: decode_vec(values, "Output"),
|
||||
}
|
||||
}
|
||||
|
||||
fn decode(values: &Value, key: &str) -> Vec<u8> {
|
||||
values[key]
|
||||
.as_str()
|
||||
.and_then(|s| hex::decode(s).ok())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn decode_vec(values: &Value, key: &str) -> Vec<Vec<u8>> {
|
||||
let s = values[key].as_str().unwrap();
|
||||
let res = match s.contains(',') {
|
||||
true => Some(s.split(',').map(|x| hex::decode(x).unwrap()).collect()),
|
||||
false => Some(vec![hex::decode(s).unwrap()]),
|
||||
};
|
||||
res.unwrap()
|
||||
}
|
||||
|
||||
macro_rules! json_to_test_vectors {
|
||||
( $v:ident, $cs:expr, $mode:expr ) => {
|
||||
$v[$cs][$mode]
|
||||
.as_array()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(populate_test_vectors)
|
||||
.collect::<Vec<VOPRFTestVectorParameters>>()
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vectors() -> Result<()> {
|
||||
use p256::NistP256;
|
||||
use p384::NistP384;
|
||||
use p521::NistP521;
|
||||
|
||||
let rfc: Value = serde_json::from_str(rfc_to_json(super::cfrg_vectors::VECTORS).as_str())
|
||||
.expect("Could not parse json");
|
||||
|
||||
#[cfg(feature = "ristretto255")]
|
||||
{
|
||||
use crate::Ristretto255;
|
||||
|
||||
let ristretto_oprf_tvs = json_to_test_vectors!(
|
||||
rfc,
|
||||
String::from("ristretto255-SHA512"),
|
||||
String::from("OPRF")
|
||||
);
|
||||
assert_ne!(ristretto_oprf_tvs.len(), 0);
|
||||
test_oprf_seed_to_key::<Ristretto255>(&ristretto_oprf_tvs)?;
|
||||
test_oprf_blind::<Ristretto255>(&ristretto_oprf_tvs)?;
|
||||
test_oprf_blind_evaluate::<Ristretto255>(&ristretto_oprf_tvs)?;
|
||||
test_oprf_finalize::<Ristretto255>(&ristretto_oprf_tvs)?;
|
||||
test_oprf_evaluate::<Ristretto255>(&ristretto_oprf_tvs)?;
|
||||
|
||||
let ristretto_voprf_tvs = json_to_test_vectors!(
|
||||
rfc,
|
||||
String::from("ristretto255-SHA512"),
|
||||
String::from("VOPRF")
|
||||
);
|
||||
assert_ne!(ristretto_voprf_tvs.len(), 0);
|
||||
test_voprf_seed_to_key::<Ristretto255>(&ristretto_voprf_tvs)?;
|
||||
test_voprf_blind::<Ristretto255>(&ristretto_voprf_tvs)?;
|
||||
test_voprf_blind_evaluate::<Ristretto255>(&ristretto_voprf_tvs)?;
|
||||
test_voprf_finalize::<Ristretto255>(&ristretto_voprf_tvs)?;
|
||||
test_voprf_evaluate::<Ristretto255>(&ristretto_voprf_tvs)?;
|
||||
|
||||
let ristretto_poprf_tvs = json_to_test_vectors!(
|
||||
rfc,
|
||||
String::from("ristretto255-SHA512"),
|
||||
String::from("POPRF")
|
||||
);
|
||||
assert_ne!(ristretto_poprf_tvs.len(), 0);
|
||||
test_poprf_seed_to_key::<Ristretto255>(&ristretto_poprf_tvs)?;
|
||||
test_poprf_blind::<Ristretto255>(&ristretto_poprf_tvs)?;
|
||||
test_poprf_blind_evaluate::<Ristretto255>(&ristretto_poprf_tvs)?;
|
||||
test_poprf_finalize::<Ristretto255>(&ristretto_poprf_tvs)?;
|
||||
test_poprf_evaluate::<Ristretto255>(&ristretto_poprf_tvs)?;
|
||||
}
|
||||
|
||||
let p256_oprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P256-SHA256"), String::from("OPRF"));
|
||||
assert_ne!(p256_oprf_tvs.len(), 0);
|
||||
test_oprf_seed_to_key::<NistP256>(&p256_oprf_tvs)?;
|
||||
test_oprf_blind::<NistP256>(&p256_oprf_tvs)?;
|
||||
test_oprf_blind_evaluate::<NistP256>(&p256_oprf_tvs)?;
|
||||
test_oprf_finalize::<NistP256>(&p256_oprf_tvs)?;
|
||||
test_oprf_evaluate::<NistP256>(&p256_oprf_tvs)?;
|
||||
|
||||
let p256_voprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P256-SHA256"), String::from("VOPRF"));
|
||||
assert_ne!(p256_voprf_tvs.len(), 0);
|
||||
test_voprf_seed_to_key::<NistP256>(&p256_voprf_tvs)?;
|
||||
test_voprf_blind::<NistP256>(&p256_voprf_tvs)?;
|
||||
test_voprf_blind_evaluate::<NistP256>(&p256_voprf_tvs)?;
|
||||
test_voprf_finalize::<NistP256>(&p256_voprf_tvs)?;
|
||||
test_voprf_evaluate::<NistP256>(&p256_voprf_tvs)?;
|
||||
|
||||
let p256_poprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P256-SHA256"), String::from("POPRF"));
|
||||
assert_ne!(p256_poprf_tvs.len(), 0);
|
||||
test_poprf_seed_to_key::<NistP256>(&p256_poprf_tvs)?;
|
||||
test_poprf_blind::<NistP256>(&p256_poprf_tvs)?;
|
||||
test_poprf_blind_evaluate::<NistP256>(&p256_poprf_tvs)?;
|
||||
test_poprf_finalize::<NistP256>(&p256_poprf_tvs)?;
|
||||
test_poprf_evaluate::<NistP256>(&p256_poprf_tvs)?;
|
||||
|
||||
let p384_oprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P384-SHA384"), String::from("OPRF"));
|
||||
assert_ne!(p384_oprf_tvs.len(), 0);
|
||||
test_oprf_seed_to_key::<NistP384>(&p384_oprf_tvs)?;
|
||||
test_oprf_blind::<NistP384>(&p384_oprf_tvs)?;
|
||||
test_oprf_blind_evaluate::<NistP384>(&p384_oprf_tvs)?;
|
||||
test_oprf_finalize::<NistP384>(&p384_oprf_tvs)?;
|
||||
test_oprf_evaluate::<NistP384>(&p384_oprf_tvs)?;
|
||||
|
||||
let p384_voprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P384-SHA384"), String::from("VOPRF"));
|
||||
assert_ne!(p384_voprf_tvs.len(), 0);
|
||||
test_voprf_seed_to_key::<NistP384>(&p384_voprf_tvs)?;
|
||||
test_voprf_blind::<NistP384>(&p384_voprf_tvs)?;
|
||||
test_voprf_blind_evaluate::<NistP384>(&p384_voprf_tvs)?;
|
||||
test_voprf_finalize::<NistP384>(&p384_voprf_tvs)?;
|
||||
test_voprf_evaluate::<NistP384>(&p384_voprf_tvs)?;
|
||||
|
||||
let p384_poprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P384-SHA384"), String::from("POPRF"));
|
||||
assert_ne!(p384_poprf_tvs.len(), 0);
|
||||
test_poprf_seed_to_key::<NistP384>(&p384_poprf_tvs)?;
|
||||
test_poprf_blind::<NistP384>(&p384_poprf_tvs)?;
|
||||
test_poprf_blind_evaluate::<NistP384>(&p384_poprf_tvs)?;
|
||||
test_poprf_finalize::<NistP384>(&p384_poprf_tvs)?;
|
||||
test_poprf_evaluate::<NistP384>(&p384_poprf_tvs)?;
|
||||
|
||||
let p521_oprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P521-SHA512"), String::from("OPRF"));
|
||||
assert_ne!(p521_oprf_tvs.len(), 0);
|
||||
test_oprf_seed_to_key::<NistP521>(&p521_oprf_tvs)?;
|
||||
test_oprf_blind::<NistP521>(&p521_oprf_tvs)?;
|
||||
test_oprf_blind_evaluate::<NistP521>(&p521_oprf_tvs)?;
|
||||
test_oprf_finalize::<NistP521>(&p521_oprf_tvs)?;
|
||||
test_oprf_evaluate::<NistP521>(&p521_oprf_tvs)?;
|
||||
|
||||
let p521_voprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P521-SHA512"), String::from("VOPRF"));
|
||||
assert_ne!(p521_voprf_tvs.len(), 0);
|
||||
test_voprf_seed_to_key::<NistP521>(&p521_voprf_tvs)?;
|
||||
test_voprf_blind::<NistP521>(&p521_voprf_tvs)?;
|
||||
test_voprf_blind_evaluate::<NistP521>(&p521_voprf_tvs)?;
|
||||
test_voprf_finalize::<NistP521>(&p521_voprf_tvs)?;
|
||||
test_voprf_evaluate::<NistP521>(&p521_voprf_tvs)?;
|
||||
|
||||
let p521_poprf_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P521-SHA512"), String::from("POPRF"));
|
||||
assert_ne!(p521_poprf_tvs.len(), 0);
|
||||
test_poprf_seed_to_key::<NistP521>(&p521_poprf_tvs)?;
|
||||
test_poprf_blind::<NistP521>(&p521_poprf_tvs)?;
|
||||
test_poprf_blind_evaluate::<NistP521>(&p521_poprf_tvs)?;
|
||||
test_poprf_finalize::<NistP521>(&p521_poprf_tvs)?;
|
||||
test_poprf_evaluate::<NistP521>(&p521_poprf_tvs)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_oprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
let server = OprfServer::<CS>::new_from_seed(¶meters.seed, ¶meters.key_info)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.sksm,
|
||||
&CS::Group::serialize_scalar(server.get_private_key()).to_vec()
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_voprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
let server = VoprfServer::<CS>::new_from_seed(¶meters.seed, ¶meters.key_info)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.sksm,
|
||||
&CS::Group::serialize_scalar(server.get_private_key()).to_vec()
|
||||
);
|
||||
assert_eq!(
|
||||
¶meters.pksm,
|
||||
CS::Group::serialize_elem(server.get_public_key()).as_slice()
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_poprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
let server = PoprfServer::<CS>::new_from_seed(¶meters.seed, ¶meters.key_info)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.sksm,
|
||||
&CS::Group::serialize_scalar(server.get_private_key()).to_vec()
|
||||
);
|
||||
assert_eq!(
|
||||
¶meters.pksm,
|
||||
CS::Group::serialize_elem(server.get_public_key()).as_slice()
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests input -> blind, blinded_element
|
||||
fn test_oprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||
let client_result =
|
||||
OprfClient::<CS>::deterministic_blind_unchecked(¶meters.input[i], blind)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.blind[i],
|
||||
&CS::Group::serialize_scalar(client_result.state.blind).to_vec()
|
||||
);
|
||||
assert_eq!(
|
||||
parameters.blinded_element[i].as_slice(),
|
||||
client_result.message.serialize().as_slice(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests input -> blind, blinded_element
|
||||
fn test_voprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||
let client_blind_result =
|
||||
VoprfClient::<CS>::deterministic_blind_unchecked(¶meters.input[i], blind)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.blind[i],
|
||||
&CS::Group::serialize_scalar(client_blind_result.state.get_blind()).to_vec()
|
||||
);
|
||||
assert_eq!(
|
||||
parameters.blinded_element[i].as_slice(),
|
||||
client_blind_result.message.serialize().as_slice(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests input -> blind, blinded_element
|
||||
fn test_poprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||
let client_blind_result =
|
||||
PoprfClient::<CS>::deterministic_blind_unchecked(¶meters.input[i], blind)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.blind[i],
|
||||
&CS::Group::serialize_scalar(client_blind_result.state.get_blind()).to_vec()
|
||||
);
|
||||
assert_eq!(
|
||||
parameters.blinded_element[i].as_slice(),
|
||||
client_blind_result.message.serialize().as_slice(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests sksm, blinded_element -> evaluation_element
|
||||
fn test_oprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let server = OprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
let message = server.blind_evaluate(&BlindedElement::deserialize(
|
||||
¶meters.blinded_element[i],
|
||||
)?);
|
||||
|
||||
assert_eq!(
|
||||
¶meters.evaluation_element[i],
|
||||
&message.serialize().as_slice()
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_voprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
|
||||
let server = VoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
|
||||
let mut blinded_elements = vec![];
|
||||
for blinded_element_bytes in ¶meters.blinded_element {
|
||||
blinded_elements.push(BlindedElement::deserialize(blinded_element_bytes)?);
|
||||
}
|
||||
|
||||
let prepared_evaluation_elements =
|
||||
server.batch_blind_evaluate_prepare(blinded_elements.iter());
|
||||
let prepared_elements: Vec<_> = prepared_evaluation_elements.collect();
|
||||
let VoprfServerBatchEvaluateFinishResult { messages, proof } = server
|
||||
.batch_blind_evaluate_finish(&mut rng, blinded_elements.iter(), &prepared_elements)?;
|
||||
let messages: Vec<_> = messages.collect();
|
||||
|
||||
for (parameter, message) in parameters.evaluation_element.iter().zip(messages) {
|
||||
assert_eq!(¶meter, &message.serialize().as_slice());
|
||||
}
|
||||
|
||||
assert_eq!(¶meters.proof, &proof.serialize().as_slice());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_poprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
|
||||
let server = PoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
|
||||
let mut blinded_elements = vec![];
|
||||
for blinded_element_bytes in ¶meters.blinded_element {
|
||||
blinded_elements.push(BlindedElement::deserialize(blinded_element_bytes)?);
|
||||
}
|
||||
|
||||
let PoprfServerBatchEvaluatePrepareResult {
|
||||
prepared_evaluation_elements,
|
||||
prepared_tweak,
|
||||
} = server.batch_blind_evaluate_prepare(blinded_elements.iter(), Some(¶meters.info))?;
|
||||
let prepared_evaluation_elements: Vec<_> = prepared_evaluation_elements.collect();
|
||||
let PoprfServerBatchEvaluateFinishResult { messages, proof } =
|
||||
PoprfServer::batch_blind_evaluate_finish::<_, _, Vec<_>>(
|
||||
&mut rng,
|
||||
blinded_elements.iter(),
|
||||
&prepared_evaluation_elements,
|
||||
&prepared_tweak,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let messages: Vec<_> = messages.collect();
|
||||
|
||||
for (parameter, message) in parameters.evaluation_element.iter().zip(messages) {
|
||||
assert_eq!(¶meter, &message.serialize().as_slice());
|
||||
}
|
||||
|
||||
assert_eq!(¶meters.proof, &proof.serialize().as_slice());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests input, blind, evaluation_element -> output
|
||||
fn test_oprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let client =
|
||||
OprfClient::<CS>::from_blind(CS::Group::deserialize_scalar(¶meters.blind[i])?);
|
||||
|
||||
let client_finalize_result = client.finalize(
|
||||
¶meters.input[i],
|
||||
&EvaluationElement::deserialize(¶meters.evaluation_element[i])?,
|
||||
)?;
|
||||
|
||||
assert_eq!(¶meters.output[i], &client_finalize_result.to_vec());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_voprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
let mut clients = vec![];
|
||||
for i in 0..parameters.input.len() {
|
||||
let client = VoprfClient::<CS>::from_blind_and_element(
|
||||
CS::Group::deserialize_scalar(¶meters.blind[i])?,
|
||||
CS::Group::deserialize_elem(¶meters.blinded_element[i])?,
|
||||
);
|
||||
clients.push(client.clone());
|
||||
}
|
||||
|
||||
let messages: Vec<_> = parameters
|
||||
.evaluation_element
|
||||
.iter()
|
||||
.map(|x| EvaluationElement::deserialize(x).unwrap())
|
||||
.collect();
|
||||
|
||||
let batch_result = VoprfClient::batch_finalize(
|
||||
¶meters.input,
|
||||
&clients,
|
||||
&messages,
|
||||
&Proof::deserialize(¶meters.proof)?,
|
||||
CS::Group::deserialize_elem(¶meters.pksm)?,
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
parameters.output,
|
||||
batch_result
|
||||
.map(|arr| arr.map(|message| message.to_vec()))
|
||||
.collect::<Result<Vec<_>>>()?
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_poprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
let mut clients = vec![];
|
||||
for i in 0..parameters.input.len() {
|
||||
let blind = CS::Group::deserialize_scalar(¶meters.blind[i])?;
|
||||
let client_blind_result =
|
||||
PoprfClient::<CS>::deterministic_blind_unchecked(¶meters.input[i], blind)?;
|
||||
let client = client_blind_result.state;
|
||||
clients.push(client.clone());
|
||||
}
|
||||
|
||||
let messages: Vec<_> = parameters
|
||||
.evaluation_element
|
||||
.iter()
|
||||
.map(|x| EvaluationElement::deserialize(x).unwrap())
|
||||
.collect();
|
||||
|
||||
let batch_result = PoprfClient::batch_finalize(
|
||||
parameters.input.iter().map(|input| input.as_slice()),
|
||||
&clients,
|
||||
&messages,
|
||||
&Proof::deserialize(¶meters.proof)?,
|
||||
CS::Group::deserialize_elem(¶meters.pksm)?,
|
||||
Some(¶meters.info),
|
||||
)?;
|
||||
|
||||
let result: Vec<Vec<u8>> = batch_result.map(|arr| arr.unwrap().to_vec()).collect();
|
||||
|
||||
assert_eq!(parameters.output, result);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests input, sksm -> output
|
||||
fn test_oprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let server = OprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
|
||||
let server_evaluate_result = server.evaluate(¶meters.input[i])?;
|
||||
|
||||
assert_eq!(¶meters.output[i], &server_evaluate_result.to_vec());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_voprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let server = VoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
|
||||
let server_evaluate_result = server.evaluate(¶meters.input[i])?;
|
||||
|
||||
assert_eq!(¶meters.output[i], &server_evaluate_result.to_vec());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_poprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let server = PoprfServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
|
||||
let server_evaluate_result =
|
||||
server.evaluate(¶meters.input[i], Some(¶meters.info))?;
|
||||
|
||||
assert_eq!(¶meters.output[i], &server_evaluate_result.to_vec());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,335 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
use crate::{
|
||||
ciphersuite::CipherSuite,
|
||||
errors::InternalError,
|
||||
group::Group,
|
||||
tests::{mock_rng::CycleRng, parser::*},
|
||||
voprf::{
|
||||
BatchFinalizeInput, BlindedElement, EvaluationElement, Metadata, NonVerifiableClient,
|
||||
NonVerifiableServer, Proof, VerifiableClient, VerifiableServer,
|
||||
},
|
||||
};
|
||||
use alloc::string::ToString;
|
||||
use alloc::vec::Vec;
|
||||
use generic_array::GenericArray;
|
||||
use json::JsonValue;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct VOPRFTestVectorParameters {
|
||||
seed: Vec<u8>,
|
||||
sksm: Vec<u8>,
|
||||
pksm: Vec<u8>,
|
||||
input: Vec<Vec<u8>>,
|
||||
info: Vec<u8>,
|
||||
blind: Vec<Vec<u8>>,
|
||||
blinded_element: Vec<Vec<u8>>,
|
||||
evaluation_element: Vec<Vec<u8>>,
|
||||
proof: Vec<u8>,
|
||||
proof_random_scalar: Vec<u8>,
|
||||
output: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
fn populate_test_vectors(values: &JsonValue) -> VOPRFTestVectorParameters {
|
||||
VOPRFTestVectorParameters {
|
||||
seed: decode(values, "seed"),
|
||||
sksm: decode(values, "skSm"),
|
||||
pksm: decode(values, "pkSm"),
|
||||
input: decode_vec(&values, "Input"),
|
||||
info: decode(values, "Info"),
|
||||
blind: decode_vec(&values, "Blind"),
|
||||
blinded_element: decode_vec(&values, "BlindedElement"),
|
||||
evaluation_element: decode_vec(&values, "EvaluationElement"),
|
||||
proof: decode(values, "Proof"),
|
||||
proof_random_scalar: decode(values, "ProofRandomScalar"),
|
||||
output: decode_vec(&values, "Output"),
|
||||
}
|
||||
}
|
||||
|
||||
fn decode(values: &JsonValue, key: &str) -> Vec<u8> {
|
||||
values[key]
|
||||
.as_str()
|
||||
.and_then(|s| hex::decode(&s.to_string()).ok())
|
||||
.unwrap_or(vec![])
|
||||
}
|
||||
|
||||
fn decode_vec(values: &JsonValue, key: &str) -> Vec<Vec<u8>> {
|
||||
let s = values[key].as_str().unwrap();
|
||||
let res = match s.contains(',') {
|
||||
true => Some(
|
||||
s.split(',')
|
||||
.map(|x| hex::decode(&x.to_string()).unwrap())
|
||||
.collect(),
|
||||
),
|
||||
false => Some(vec![hex::decode(&s.to_string()).unwrap()]),
|
||||
};
|
||||
res.unwrap()
|
||||
}
|
||||
|
||||
macro_rules! json_to_test_vectors {
|
||||
( $v:ident, $cs:expr, $mode:expr ) => {
|
||||
$v[$cs][$mode]
|
||||
.members()
|
||||
.map(|x| populate_test_vectors(&x))
|
||||
.collect::<Vec<VOPRFTestVectorParameters>>()
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vectors() -> Result<(), InternalError> {
|
||||
let rfc = json::parse(rfc_to_json(super::voprf_vectors::VECTORS).as_str())
|
||||
.expect("Could not parse json");
|
||||
|
||||
use crate::tests::Ristretto255Sha512;
|
||||
|
||||
let ristretto_base_tvs = json_to_test_vectors!(
|
||||
rfc,
|
||||
String::from("ristretto255, SHA-512"),
|
||||
String::from("Base")
|
||||
);
|
||||
|
||||
let ristretto_verifiable_tvs = json_to_test_vectors!(
|
||||
rfc,
|
||||
String::from("ristretto255, SHA-512"),
|
||||
String::from("Verifiable")
|
||||
);
|
||||
|
||||
test_base_seed_to_key::<Ristretto255Sha512>(&ristretto_base_tvs)?;
|
||||
test_base_blind::<Ristretto255Sha512>(&ristretto_base_tvs)?;
|
||||
test_base_evaluate::<Ristretto255Sha512>(&ristretto_base_tvs)?;
|
||||
test_base_finalize::<Ristretto255Sha512>(&ristretto_base_tvs)?;
|
||||
|
||||
test_verifiable_seed_to_key::<Ristretto255Sha512>(&ristretto_verifiable_tvs)?;
|
||||
test_verifiable_blind::<Ristretto255Sha512>(&ristretto_verifiable_tvs)?;
|
||||
test_verifiable_evaluate::<Ristretto255Sha512>(&ristretto_verifiable_tvs)?;
|
||||
test_verifiable_finalize::<Ristretto255Sha512>(&ristretto_verifiable_tvs)?;
|
||||
|
||||
#[cfg(feature = "p256")]
|
||||
{
|
||||
use crate::tests::P256Sha256;
|
||||
|
||||
let p256_base_tvs =
|
||||
json_to_test_vectors!(rfc, String::from("P-256, SHA-256"), String::from("Base"));
|
||||
|
||||
let p256_verifiable_tvs = json_to_test_vectors!(
|
||||
rfc,
|
||||
String::from("P-256, SHA-256"),
|
||||
String::from("Verifiable")
|
||||
);
|
||||
|
||||
test_base_seed_to_key::<P256Sha256>(&p256_base_tvs)?;
|
||||
test_base_blind::<P256Sha256>(&p256_base_tvs)?;
|
||||
test_base_evaluate::<P256Sha256>(&p256_base_tvs)?;
|
||||
test_base_finalize::<P256Sha256>(&p256_base_tvs)?;
|
||||
|
||||
test_verifiable_seed_to_key::<P256Sha256>(&p256_verifiable_tvs)?;
|
||||
test_verifiable_blind::<P256Sha256>(&p256_verifiable_tvs)?;
|
||||
test_verifiable_evaluate::<P256Sha256>(&p256_verifiable_tvs)?;
|
||||
test_verifiable_finalize::<P256Sha256>(&p256_verifiable_tvs)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_base_seed_to_key<CS: CipherSuite>(
|
||||
tvs: &[VOPRFTestVectorParameters],
|
||||
) -> Result<(), InternalError> {
|
||||
for parameters in tvs {
|
||||
let server = NonVerifiableServer::<CS>::new_from_seed(¶meters.seed)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.sksm,
|
||||
&CS::Group::scalar_as_bytes(server.get_private_key()).to_vec()
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_verifiable_seed_to_key<CS: CipherSuite>(
|
||||
tvs: &[VOPRFTestVectorParameters],
|
||||
) -> Result<(), InternalError> {
|
||||
for parameters in tvs {
|
||||
let server = VerifiableServer::<CS>::new_from_seed(¶meters.seed)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.sksm,
|
||||
&CS::Group::scalar_as_bytes(server.get_private_key()).to_vec()
|
||||
);
|
||||
assert_eq!(¶meters.pksm, &server.get_public_key().to_arr().to_vec());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests input -> blind, blinded_element
|
||||
fn test_base_blind<CS: CipherSuite>(
|
||||
tvs: &[VOPRFTestVectorParameters],
|
||||
) -> Result<(), InternalError> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let mut rng = CycleRng::new(parameters.blind[i].to_vec());
|
||||
let client_result = NonVerifiableClient::<CS>::blind(¶meters.input[i], &mut rng)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.blind[i],
|
||||
&CS::Group::scalar_as_bytes(client_result.state.get_blind()).to_vec()
|
||||
);
|
||||
assert_eq!(
|
||||
¶meters.blinded_element[i],
|
||||
&client_result.message.serialize()
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests input -> blind, blinded_element
|
||||
fn test_verifiable_blind<CS: CipherSuite>(
|
||||
tvs: &[VOPRFTestVectorParameters],
|
||||
) -> Result<(), InternalError> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let mut rng = CycleRng::new(parameters.blind[i].to_vec());
|
||||
let client_blind_result =
|
||||
VerifiableClient::<CS>::blind(¶meters.input[i], &mut rng)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.blind[i],
|
||||
&CS::Group::scalar_as_bytes(client_blind_result.state.get_blind()).to_vec()
|
||||
);
|
||||
assert_eq!(
|
||||
¶meters.blinded_element[i],
|
||||
&client_blind_result.message.serialize()
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests sksm, blinded_element -> evaluation_element
|
||||
fn test_base_evaluate<CS: CipherSuite>(
|
||||
tvs: &[VOPRFTestVectorParameters],
|
||||
) -> Result<(), InternalError> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let server = NonVerifiableServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
let server_result = server.evaluate(
|
||||
BlindedElement::deserialize(¶meters.blinded_element[i])?,
|
||||
&Metadata(parameters.info.clone()),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.evaluation_element[i],
|
||||
&server_result.message.serialize()
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_verifiable_evaluate<CS: CipherSuite>(
|
||||
tvs: &[VOPRFTestVectorParameters],
|
||||
) -> Result<(), InternalError> {
|
||||
for parameters in tvs {
|
||||
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
|
||||
let server = VerifiableServer::<CS>::new_with_key(¶meters.sksm)?;
|
||||
|
||||
let mut blinded_elements = vec![];
|
||||
for blinded_element_bytes in ¶meters.blinded_element {
|
||||
blinded_elements.push(BlindedElement::deserialize(&blinded_element_bytes)?);
|
||||
}
|
||||
|
||||
let batch_evaluate_result = server.batch_evaluate(
|
||||
&mut rng,
|
||||
&blinded_elements,
|
||||
&Metadata(parameters.info.clone()),
|
||||
)?;
|
||||
|
||||
for i in 0..parameters.evaluation_element.len() {
|
||||
assert_eq!(
|
||||
¶meters.evaluation_element[i],
|
||||
&batch_evaluate_result.messages[i].serialize(),
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(¶meters.proof, &batch_evaluate_result.proof.serialize());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tests input, blind, evaluation_element -> output
|
||||
fn test_base_finalize<CS: CipherSuite>(
|
||||
tvs: &[VOPRFTestVectorParameters],
|
||||
) -> Result<(), InternalError> {
|
||||
for parameters in tvs {
|
||||
for i in 0..parameters.input.len() {
|
||||
let client = NonVerifiableClient::<CS>::from_data_and_blind(
|
||||
¶meters.input[i],
|
||||
&<CS::Group as Group>::from_scalar_slice(&GenericArray::clone_from_slice(
|
||||
¶meters.blind[i],
|
||||
))?,
|
||||
);
|
||||
|
||||
let client_finalize_result = client.finalize(
|
||||
EvaluationElement::deserialize(¶meters.evaluation_element[i])?,
|
||||
&Metadata(parameters.info.clone()),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
¶meters.output[i],
|
||||
&client_finalize_result.output.to_vec()
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_verifiable_finalize<CS: CipherSuite>(
|
||||
tvs: &[VOPRFTestVectorParameters],
|
||||
) -> Result<(), InternalError> {
|
||||
for parameters in tvs {
|
||||
let mut clients = vec![];
|
||||
for i in 0..parameters.input.len() {
|
||||
let client = VerifiableClient::<CS>::from_data_and_blind(
|
||||
¶meters.input[i],
|
||||
&<CS::Group as Group>::from_scalar_slice(&GenericArray::clone_from_slice(
|
||||
¶meters.blind[i],
|
||||
))?,
|
||||
&<CS::Group as Group>::from_element_slice(&GenericArray::clone_from_slice(
|
||||
¶meters.blinded_element[i],
|
||||
))?,
|
||||
);
|
||||
clients.push(client.clone());
|
||||
}
|
||||
|
||||
let batch_finalize_input = BatchFinalizeInput::new(
|
||||
clients,
|
||||
parameters
|
||||
.evaluation_element
|
||||
.iter()
|
||||
.map(|x| EvaluationElement::deserialize(x).unwrap())
|
||||
.collect(),
|
||||
);
|
||||
|
||||
let batch_result = VerifiableClient::batch_finalize(
|
||||
batch_finalize_input,
|
||||
Proof::deserialize(¶meters.proof)?,
|
||||
CS::Group::from_element_slice(GenericArray::from_slice(¶meters.pksm))?,
|
||||
&Metadata(parameters.info.clone()),
|
||||
)?;
|
||||
|
||||
assert_eq!(
|
||||
parameters.output,
|
||||
batch_result
|
||||
.outputs
|
||||
.iter()
|
||||
.map(|arr| arr.to_vec())
|
||||
.collect::<Vec<Vec<u8>>>()
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,642 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under both the MIT license found in the
|
||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||
// of this source tree.
|
||||
|
||||
//! The VOPRF test vectors taken from:
|
||||
//! https://github.com/cfrg/draft-irtf-cfrg-voprf/blob/master/draft-irtf-cfrg-voprf.md
|
||||
|
||||
pub(crate) static VECTORS: &str = r#"
|
||||
## OPRF(ristretto255, SHA-512)
|
||||
|
||||
### Base Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
skSm = caeff69352df4905a9121a4997704ca8cee1524a110819eb87deba1a39ec1
|
||||
701
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = c604c785ada70d77a5256ae21767de8c3304115237d262134f5e46e512cf
|
||||
8e03
|
||||
BlindedElement = fc20e03aff3a9de9b37e8d35886ade11ec7d85c2a1fb5bb0b16
|
||||
86c64e07ac467
|
||||
EvaluationElement = 922e4c04b9f3b3e795d322a306c0ab9d96b667df9b949c05
|
||||
2c8c75435a9dbf2f
|
||||
Output = 9e857d0e8523b8eb9e995d455ae6ae19f75d85ac8b5df62c50616fb5aa0
|
||||
ced3da5646698089c36dead28f9ad8e489fc0ee1c8e168725c38ed50f3783a5c520c
|
||||
e
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 5ed895206bfc53316d307b23e46ecc6623afb3086da74189a416012be037
|
||||
e50b
|
||||
BlindedElement = 483d4f39de5ff77fa0f9a0ad2334dd5bf87f2cda868539d21de
|
||||
67ce49e7d1536
|
||||
EvaluationElement = 6eef6ee53c6fb17c77ae47e78bdca2e1094f98785e7b9a14
|
||||
f09be20797dad656
|
||||
Output = b090b2ff80028771c14fecf2f37c1b14e46deec59c83d3b943c51d315bd
|
||||
3bf7d32c399ed0c4ce6003339ab9ed4ad168bfb595e43530c9d73ff02ab0f1263d93
|
||||
b
|
||||
~~~
|
||||
|
||||
### Verifiable Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
skSm = ac37d5850510299406ea8eb8fa226a7bfc2467a4b070d6c7bf667948b9600
|
||||
b00
|
||||
pkSm = 0c0254e22063cae3e1bae02fb6fa20882664a117c0278eda6bda3372c0dd9
|
||||
860
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = ed8366feb6b1d05d1f46acb727061e43aadfafe9c10e5a64e7518d63e326
|
||||
3503
|
||||
BlindedElement = 3a0a53f2c57e5ee0d89e394087f8e5f95b24159db01c31933a0
|
||||
7f0e6414c954d
|
||||
EvaluationElement = f8a50ed35a477b0cde91d926e1bc5ae59b97d5bd0dda51a7
|
||||
28b0f036ec557d79
|
||||
Proof = 7a5375eb1dbad259431f5c294e816a1c1483c279748da1a75d91f8a81438
|
||||
ea08355d4087d4d848b46878dcc8fb5849ac7a09133382c2c6129564a7f7b4b7bf01
|
||||
ProofRandomScalar = 019cbd1d7420292528f8cdd62f339fdabb602f04a95dac9d
|
||||
bcec831b8c681a09
|
||||
Output = 4b2ff4c984985829c3cd9d90c255cdc0d6b61c4c0aafa9215769d51cf7d
|
||||
eb01472ba945928a8305e010f12b7dcc75a9dc2460439e6297d57dc2ce7ca0abaae1
|
||||
a
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = e6d0f1d89ad552e383d6c6f4e8598cc3037d6e274d22da3089e7afbd4171
|
||||
ea02
|
||||
BlindedElement = a86dd4544d0f3ea973926054230767dff16016215f2d73f26d3
|
||||
f86a81f38cf1a
|
||||
EvaluationElement = 9e47810f1de1b57ebe163a95c170ec165a2063f872155c37
|
||||
6d94e8de2157af70
|
||||
Proof = 61075125d851d5164b0aa1a4d5ddeebaf097266450ac6019579af5f7abd1
|
||||
90088eb0f6f1e7f9d8bfddbc21ae3c25a065e6c4e797d15f345ed4fb9ee468d24c0a
|
||||
ProofRandomScalar = 74ae06fd50d5f26c2519bd7b184f45dd3ef2cb50197d42df
|
||||
9d013f7d6c312a0b
|
||||
Output = fe1fb7fa49c37dc7cd31d64859b4a2e6ae0cef294f2764e6f12f7d809f2
|
||||
18047d1fde147cf69807b8971fb2c316eb572be2b5bf491813bfec0a20668d6d07b0
|
||||
b
|
||||
~~~
|
||||
|
||||
#### Test Vector 3, Batch Size 2
|
||||
|
||||
~~~
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 80513e77795feeec6d2c450589b0e1b178febd5c193a9fcba0d27f0a06e0
|
||||
d50f,533c2e6d91c934f919ac218973be55ba0d7b234160a0d4cf3bddafbda99e2e0
|
||||
c
|
||||
BlindedElement = c24645d6378a4a86ec4682a8d86f368b1e7db870fd709a45102
|
||||
492bcdc17e904,0e5ec78f839a8b6e86999bc180602690a4daae57bf5d7f827f3d40
|
||||
2f56cc6c51
|
||||
EvaluationElement = 3afe48eab00493eb1b073e95f57a456cde9aefe463dd1e6d
|
||||
0144bf6e99ce411c,daaf9421318fd2c7fcdf369cb348748cf4dd177cce30ee4d13c
|
||||
eb1644b85b653
|
||||
Proof = 601381ecbe127ada04c057b8b1fc21d912f71e49252780dd0d0ac768b233
|
||||
ce035f9b489a994c1d14b92d603ebcffee4f5cfadc953f69bb62648c6e662613ae00
|
||||
ProofRandomScalar = 3af5aec325791592eee4a8860522f8444c8e71ac33af5186
|
||||
a9706137886dce08
|
||||
Output = 4b2ff4c984985829c3cd9d90c255cdc0d6b61c4c0aafa9215769d51cf7d
|
||||
eb01472ba945928a8305e010f12b7dcc75a9dc2460439e6297d57dc2ce7ca0abaae1
|
||||
a,fe1fb7fa49c37dc7cd31d64859b4a2e6ae0cef294f2764e6f12f7d809f218047d1
|
||||
fde147cf69807b8971fb2c316eb572be2b5bf491813bfec0a20668d6d07b0b
|
||||
~~~
|
||||
|
||||
## OPRF(decaf448, SHAKE-256)
|
||||
|
||||
### Base Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3
|
||||
skSm = 78f2622804104209f7e015370ff98f4a3cbf311e6784e9f4944f8a252dc08
|
||||
e916d9ab1a60dc905f0e56631903ecd4ae6e15291776d61460b
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = d1080372f0fcf8c5eace50914e7127f576725f215cc7c111673c635ce668
|
||||
bbbb9b50601ad89b358ab8c23ed0b6c9d040365ec9d060868714
|
||||
BlindedElement = 984e0a443ee194090737df4afb402253f216b77650c91d252b6
|
||||
638e1179723d51a4154b88eae396f1320f5df3c4b17f779516c456e364bd1
|
||||
EvaluationElement = de477252a5ff3c7d51ce159cb8ccf1865d8c7d3402824163
|
||||
8d80971f13a59d87b2b1036341b98089555ab088278391794c49bbb052fdbcff
|
||||
Output = df8f910c3b84d1f3ca6afd1992768608a20f2ad7b770e9d89d303c88ba1
|
||||
5bb7d991f2f7ffd5b5b51fa3bcf8fa06779609497f6c0ae4e9cb2dcd48c68b4ac6b9
|
||||
4
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = aed1ffa44fd8f0ed16373606a3cf7df589cca86d8ea1abbf5768771dbef3
|
||||
d401c74ae55ba1e28b9565e1e4018eb261a14134a4ce60c1c718
|
||||
BlindedElement = 4aa751f84b2634b73efa364b03e60b92b84f457576e6b369eea
|
||||
b76140e3859d10d2e98174f13f5a2c70670529ccf093d5f1aaf355b4f830b
|
||||
EvaluationElement = 085ea1cb452a2fb15b3a0d0e1c86899c7ea49fe2e4856ef4
|
||||
f95bc2542eec610fc09b0fe7d7ed7389d86af6a646695b7ad46527dc2a936aa4
|
||||
Output = b57516a737879ece1110ad5d051ac0a6c54e1dcd989c907721ecebab5b4
|
||||
5877cc693c3c05d0bd416c5a9ceba36de41a0a31679c146fe4c110c64b056eba1720
|
||||
b
|
||||
~~~
|
||||
|
||||
### Verifiable Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3
|
||||
skSm = 9eb722f7fee9f61f24ad31bc42309f73648cf4393929e8f5f333fe10c6975
|
||||
c827a1eba4e03ae2fa8735db2f63f6c98c7af6010e64c81f535
|
||||
pkSm = b6e2751176d57836fe1dfbdbbdc78a1b5c5a52f831226c9d8dfdf5daf8f46
|
||||
6e310e80978e9b81c387f5bc85cc7ef5567f4dd3ba7674579a2
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 4c936db1779a621b6c71475ac3111fd5703a59b713929f36dfd1e892a7fe
|
||||
814479c93d8b4b6e11d1f6fe5351e51457b665fa7b76074e531f
|
||||
BlindedElement = d0b8e2eecad2816d45c1f8a072fe6db77d18f4b26f0889c98e2
|
||||
ef856ac5df82090c1fbeac9c8e732f192b66c3b4c3f1e446ab8910c86be2f
|
||||
EvaluationElement = d29849d8ad1e651328e8119003debd9ecd54cc786a5eb8ae
|
||||
ea56487ffc09120e98792f9475605488d16623b8e3cfa5af1ec27e76bc841b75
|
||||
Proof = 8b3b8f0c9eb22527e419f5a03d4d3f34cf725837424a38c5b4f88c7759f7
|
||||
a54bade57b7930bfeff051be9bfeaabc8976ed407398e0ce462a062e068a8d57bc1c
|
||||
411bc42fe714626cfb92ad854a56636c2b83f2b5215c2ff531b22e4d37031523db20
|
||||
3556959e275b46b84303ed23fc37
|
||||
ProofRandomScalar = 1b3f5a55b2f18f8c53d4ecf2e1c27e1028f1c345bb504486
|
||||
4aa9dd8439d7520a7ba6183d50ef08bdf6c781aa465660c93e8195a8d231b62f
|
||||
Output = 1ff5c5c2c081c76006b52c45f79728882dc48962036ea7d4d5097b04e93
|
||||
9ae81118a7fe5f0a66a6131bef18b9cd998150f10c62619ec4c2d223ea57dc67f153
|
||||
d
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 15b3355179392f40c3d5a15f0d5ffc354e340454ec779f575e4573a3886a
|
||||
b5e57e4da2985cea9e32f6d95539ce2c7189e1bd7462e8c5483a
|
||||
BlindedElement = 0e7ddd85c8bc5382e908241c6151afe23a41e0396759b5e38a9
|
||||
affd996cd822bca242a499793555fc15f07bffdaaa93b42568b307fbdca0f
|
||||
EvaluationElement = 4c81e29e8a9502fa02e00cb09cf40d9b98988ac9b4bce7cc
|
||||
a0656caeb0926b59c7000d7fe6c5dd814f831864547d2360d223a50077bd04fe
|
||||
Proof = 74fc8fbf2e669dc5d25898ea8ce45d1d3eb97edb4b7c3cee39865a3c66da
|
||||
6b7bad4ad3e77794d6f5e82fa8a645b9b973a8612bfcd1194302f700ee3433e876d8
|
||||
3f96bb70f19ff292605ad4c9466fd71dbc2ed22ade0130574e5ee343ef45d42e834a
|
||||
11a19fd6f5b1b5ef910bcccf731b
|
||||
ProofRandomScalar = 2f2e9955be83a4b25743ebd3618d4fad8b7288477da50bed
|
||||
9befa58af639ddd950fec34205f8a4f166fadcb8fa71a3ffdd2e98f4c8ef5e26
|
||||
Output = 2753e222528f1ee5fcc6ad4bf1ca953e5d3b47c1dfae85710f46a0a030c
|
||||
07f59055e9b05dacb729a7ce41cd2ed782f8a76a1b3f74b40196aed0b6938b89c60f
|
||||
9
|
||||
~~~
|
||||
|
||||
#### Test Vector 3, Batch Size 2
|
||||
|
||||
~~~
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 614bb578f29cc677ea9e7aea3e4839413997e020f9377b63c13584156a09
|
||||
a46dd2a425c41eac0e313a47e99d05df72c6e1d58e6592577a0d,4c115060bca87db
|
||||
7d73e00cbb8559f84cb7a221b235b0950a0ab553f03f10e1386abe954011b7da62bb
|
||||
6599418ef90b5d4ea98cc28aff517
|
||||
BlindedElement = 5e481a4d7eaa5bab831f53f9a6311851dafd4318c6462eed4f6
|
||||
15004afdb082da2f99670b0963985faac21c30eea19aacfc441412edb4c0b,8e043b
|
||||
9b7afeafa07e39d9b8b88957ff07d69124b1a2b841e18c9ffb52ebf0c25144eb2501
|
||||
a1d7983a44604f33a36e925eebc9bec65d9c54
|
||||
EvaluationElement = 8a0d34fdb0b55121421546ff952c7bd3cbe469926ff9ad4f
|
||||
aeba243823955529eeae4f1a7a64cd055ec01baa041a99dfbe1a67ca4d59f93d,5e8
|
||||
6e0b41cc88186ee0003baa46535e71acd98453b298976b92be2cca2646e88620f55d
|
||||
f6bf4754456dfd8d84f6889c17b5ff93052325a1a
|
||||
Proof = 1ff624a102b99771c76a9414e9b3f33127897d971bc84a922e464805e4a9
|
||||
f27b889922030adebbbd58e0ab618ade9c84bfe8aa226176f11f432958ea1e6f6926
|
||||
3aef51db9efb23ee504d233c17e9077c0373401da167637a1df4eafd9c2537c9f89c
|
||||
103f9e635931fe2042419dd9bd37
|
||||
ProofRandomScalar = a614f1894bcf6a1c7cef33909b794fe6e69a642b20f4c911
|
||||
8febffaf6b6a31471fe7794aa77ced123f07e56cc27de60b0ab106c0b8eab127
|
||||
Output = 1ff5c5c2c081c76006b52c45f79728882dc48962036ea7d4d5097b04e93
|
||||
9ae81118a7fe5f0a66a6131bef18b9cd998150f10c62619ec4c2d223ea57dc67f153
|
||||
d,2753e222528f1ee5fcc6ad4bf1ca953e5d3b47c1dfae85710f46a0a030c07f5905
|
||||
5e9b05dacb729a7ce41cd2ed782f8a76a1b3f74b40196aed0b6938b89c60f9
|
||||
~~~
|
||||
|
||||
## OPRF(P-256, SHA-256)
|
||||
|
||||
### Base Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
skSm = a1b2355828f2c76de6749af9d093bd9fe0f2cada3ec653cd9a6d3126a7a78
|
||||
27b
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 5d9e7f6efd3093c32ecceabd57fb03cf760c926d2a7bfa265babf29ec98a
|
||||
f0d0
|
||||
BlindedElement = 03e3c379698da853d9844098fa0ac676970d5ec24167b598714
|
||||
cd2ee188604ddd2
|
||||
EvaluationElement = 030d8d882120e8fa67ef978a9abac506acd5ec731b8e8d6f
|
||||
15035e29241dd2ced2
|
||||
Output = ab653a4f3b357177b125e1c6d0bd2c0bc409b7ed5f48c99537fbd7fd11e
|
||||
f8133
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 825155ab61f17605af2ae2e935c78d857c9407bcd45128d57d338f1671b5
|
||||
fcbe
|
||||
BlindedElement = 030b40be181ffbb3c3ae4a4911287c43261f5e4034781def69c
|
||||
51608f372a02102
|
||||
EvaluationElement = 03991df04e3e526d457065b6eafc855aa2fc4528c22d2b51
|
||||
6a3c71227b1b488f44
|
||||
Output = eca4df985f7c49b091c3ce4217be1f26cdc6a148b681ed1f1638d09dfd2
|
||||
13e6e
|
||||
~~~
|
||||
|
||||
### Verifiable Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
skSm = 4e7804245a743c59d624457677294e04a8bc4bdcd94f0d3bd54f568067489
|
||||
d34
|
||||
pkSm = 03b51a0af95c819b09ee80c2056cf0ab0551a5355266d3a0aaff90c3fe915
|
||||
ed892
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = cee64d86fd20ab4caa264a26c0e3d42fb773b3173ba76f9588c9b14779bd
|
||||
8d91
|
||||
BlindedElement = 0222f5dba2da1ec7bd1086d0e04894ef1da1c11163daf376b2b
|
||||
c76cc51edb16815
|
||||
EvaluationElement = 02f2767135f75f69b257675b38f2bcd50338a655a5092166
|
||||
3c8942ca61ea7d3c29
|
||||
Proof = ffa082fc9f9a287e7edc50e3ad879ee13aebd24b69124792bdf047c643f7
|
||||
0af2b50907b2fa188b90aff3b25e1d9abb02e9e2c8bfdc525c61ca008428940fca64
|
||||
ProofRandomScalar = 70a5204b2b606f5a28328916e1e5ea5a17862d7a261fdd6d
|
||||
959759758d5e34ac
|
||||
Output = c74d46cc93e578f7048bc6b852cd9bc1d9ebb90c586308f9202b9deedc8
|
||||
94448
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 5c4b401063eff0bf242b4cd534a79bacfc2e715b2db1e7a3ad4ff8af1b24
|
||||
daa2
|
||||
BlindedElement = 02f84403d1ceb40a3668349f7c349f806d2c858785853324c66
|
||||
7505018d13ee160
|
||||
EvaluationElement = 0216d7d342ef50113244b444dfedaec78810959e40fef0a6
|
||||
922658d44accb1e9c1
|
||||
Proof = f496e58818c25ffb386f22ceb57a83da1200612b67aaa07608b3375c25b2
|
||||
97e03e67d1f6094a8012725dc63a0c2f4f870173b97a3daa03588f777655a087fbbf
|
||||
ProofRandomScalar = 3b9217801b5d51cef66d9fdbd94a53533e7c5057e09e2200
|
||||
65ea8c257c0dd606
|
||||
Output = 90a9f5ff4208a5505d1b7ed65eb233bb61b4c999ffa0d8cd1d98fb717b9
|
||||
2fe28
|
||||
~~~
|
||||
|
||||
#### Test Vector 3, Batch Size 2
|
||||
|
||||
~~~
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = f0c7822ba317fb5e86028c44b92bd3aedcf6744d388ca013ef33edd36930
|
||||
4eda,3b9631be9f8b274d9aaf671bfb6a775229bf435021b89c683259773bc686956
|
||||
b
|
||||
BlindedElement = 02a840214a74345570dcadfc927e726901b257b447234fac509
|
||||
0a1830295ca736c,039a5a8152abb0154b4d79a90486e358ea325980f0bf590524c4
|
||||
460f700454238f
|
||||
EvaluationElement = 025991aac0b0c79bb1185c0b1e64964656634dfcd755cdf5
|
||||
da9ee52be0b5d5f742,03319e3baba8fa7f60dab49ef0ba68b7a85bccb5d4968643e
|
||||
2f029b6c0826911d1
|
||||
Proof = 51b5ed453168480a2e95863cda1f4d28ad5bc91e8c9c75d788569aea1679
|
||||
794a642087db120a2b3ce839f57041801f37cd4a6c05b69b327b877810293f7b09a8
|
||||
ProofRandomScalar = 8306b863276ae74049615162a416d507a6532c99c1ea3f03
|
||||
d05f6e78dc1edabe
|
||||
Output = c74d46cc93e578f7048bc6b852cd9bc1d9ebb90c586308f9202b9deedc8
|
||||
94448,90a9f5ff4208a5505d1b7ed65eb233bb61b4c999ffa0d8cd1d98fb717b92fe
|
||||
28
|
||||
~~~
|
||||
|
||||
## OPRF(P-384, SHA-512)
|
||||
|
||||
### Base Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3
|
||||
skSm = ef1b52c12cdf43dc260bf5425a30cde7d708ec34b38dcfbdc2946d7baf525
|
||||
361e797f6a98f1ebd80f64865f21cde1c6d
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 359073c015b92d15450f7fb395bf52c6ea98384c491fe4e4d423b59de7b0
|
||||
df382902c13bdc9993d3717bda68fc080b99
|
||||
BlindedElement = 02fa3115c21ffcacc09ca470729b725781f84333e217cfeec2b
|
||||
8ba6a54ce492ede7ead3714c5b177427ef853effb1b5c24
|
||||
EvaluationElement = 033a4bdea2693686e4ce467c8a5cdfc41b86ad20aaaa9bc1
|
||||
6e75b59dbd41dab0bc9af0041e551ece3b4c9fb2315d8d1fa9
|
||||
Output = a5a0ef3fb964a36097662d1258ef0f93b224ddd81a356c37d5dd05a885a
|
||||
0b6722b90c1f5181637fece7ed180ba053da23bf35cef7a87dcba75562cb7a264001
|
||||
8
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 21ece4f9b6ffd01ce82082545413bd9bb5e8f3c63b86ae88d9ce0530b01c
|
||||
b1c23382c7ec9bdd6e75898e4877d8e2bc17
|
||||
BlindedElement = 025fddc89a832089a59120df742acb34dba82b26afcae977961
|
||||
57df238b5905c494a23c56b1f485cbbff78d31df7fa1492
|
||||
EvaluationElement = 02f8b59813663e7965c219c113c560482cbea7ca4c412a0c
|
||||
f3fd855ee7d543ae926d29ace85296f195f988be284b2347f6
|
||||
Output = f2a0b355cae4ae2c717d0b48e39c0ee356db3ca446fddf85cddb74f397e
|
||||
b85046da62d0d85d55d19d39dd9b68fcc39379ec6d3b93ba33909fcc96361d225cdd
|
||||
e
|
||||
~~~
|
||||
|
||||
### Verifiable Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3
|
||||
skSm = 67ee1c9e67566d87bfcca9e5dac4bfdb8bdd727c031133fac2aa9ba6c41e6
|
||||
1e5f8fd401b5d76c7d54b15b15932797479
|
||||
pkSm = 029b51b2ce9c499f2056e65e0f41d60960f9c4795c0cf94af273ce840c20b
|
||||
e4cdf87690b6b121b37d399b49afcc2ec9ac3
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 102f6338df84c9602bfa9e7d690b1f7a173d07e6d54a419db4a6308f8b09
|
||||
589e4283efb9cd1ee4061c6bf884e60a8774
|
||||
BlindedElement = 02a1f41323e91a6ac9fbbb5b8e4c7c58a4c5bcbaa4195557182
|
||||
cd59e826dc847f1e077de1d402ac92eafe322461fc0d582
|
||||
EvaluationElement = 03af3164f8721a57931f92884b43c58ff0ed1be249f7e1c9
|
||||
3033a5909f0ffc59ed3fea9452ec5c9cfb865b8bd2e65cd209
|
||||
Proof = 44108ca9b342f4d7e31a250aa9f41afb0de840e113dbb6bb82b5e6735aef
|
||||
18a20867a63628be6e109d2d687e1faa8888270f1173bc6f916e21142096d23d1719
|
||||
4edf844074922c287a50182f87bbb5fc3a966c8851dd6799ec5cfe59c7063c7f
|
||||
ProofRandomScalar = 90f67cafc0ffaa7a1e1d1ced3c477fea691e696032c8709c
|
||||
86cbcda2b184ad0029d29abeabede9788d11782429bff297
|
||||
Output = 065094c66d66b6541aa1e09d99e2fdaac727356e9cd1c18275b7127be51
|
||||
eb1ce7f37ad5924f7425d60828c2d1acc69bef40d11423bba8f9e34478e04c437fbe
|
||||
0
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 8aec1d0c3d16afd032da7ba961449a56cec6fb918e932b06d5778ac7f67b
|
||||
ecfb3e3869237f74106241777f230582e84a
|
||||
BlindedElement = 02b3465d70f76de3eaf6ecb8080490288f741c622c06d023bd1
|
||||
80a55a2e3e4eaad08533651f9d278a3f59cec8277780303
|
||||
EvaluationElement = 03a53e01901893585437cd48a1eea1188fc8e9275a80cf43
|
||||
370a451c476dae3b84ca8c7bf44fcac2fa3eeab933b25da0c3
|
||||
Proof = 5ebc467e78ae29f7d741221df0ee67285df72ec482fdc8e5bde7e588b12f
|
||||
cba86f4f116c23ee6b32c0f38f2daac67e869e53e7e0494cc883e4984daf10a55819
|
||||
bbb5ce7e9005f143b3dda88d8a35649269a4658a98c81c814097d15a3dcf4dbe
|
||||
ProofRandomScalar = bb1876a7f7165ac7ec79bfd5213ea2e374252f29a6e19915
|
||||
f81b0c7dcea93ce6580e089ede31c1b6b5b33494581b4868
|
||||
Output = 5f557169680da50500b5333a26bb2ba79256c0ecc351051d32cac540920
|
||||
267a40b246deb286c9ecb0025dede808465f85d6a5e75aca61088533b306d8646c92
|
||||
c
|
||||
~~~
|
||||
|
||||
#### Test Vector 3, Batch Size 2
|
||||
|
||||
~~~
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 41fabd4722d92472d858051ce9ad1a533176a862c697b2c392aff2aeb77e
|
||||
b20c2ae6ba52fe31e13e03bf1d9f39878b23,51171628f1d28bb7402ca4aea6465e2
|
||||
67b7f977a1fb71593281099ef2625644aee0b6c5f5e6e01a2b052b3bd4caf539b
|
||||
BlindedElement = 02d715dfce1a0724071fa8e530d79f7b234a31739a64166e0fe
|
||||
21fa6fa0fe19e1ab5e468becca899f31e365c47f3efb2ef,028dfd0c7a38b4cb8477
|
||||
cae34f041344fb44fc9e55bfa3cf55ab7b4764b74accc7b49c0ff09a524598033dad
|
||||
1152fb3a1c
|
||||
EvaluationElement = 03f9a8c81c108201888eb86348c6f80691d99425272972b5
|
||||
bf41d3038af0eeb04d60edd9ea288625a7166a8c17cea0083f,02abb31980533dbf7
|
||||
eb5fee0a8969089b3e16585a2cd41a34067592a2021b1b4ea3d1cef3e7c87a6f284c
|
||||
0e45546c92d98
|
||||
Proof = f0f7bd2723c3460d5c5ab03092c6861fb34253470ef430dac9aeac6ce489
|
||||
84b28d91178061cba02e3e911c4aa97229d519755db385ddd08064fdf8405897d1de
|
||||
a472688934088505e89dcff91081fec1d2e37c1d4c5a9dddbdd358aa89f63b46
|
||||
ProofRandomScalar = 1b538ff23749be19e92df82df1acd3f606cc9faa9dc7ab25
|
||||
1997738a3a232f352c2059c25684e6ccea420f8d0c793fa0
|
||||
Output = 065094c66d66b6541aa1e09d99e2fdaac727356e9cd1c18275b7127be51
|
||||
eb1ce7f37ad5924f7425d60828c2d1acc69bef40d11423bba8f9e34478e04c437fbe
|
||||
0,5f557169680da50500b5333a26bb2ba79256c0ecc351051d32cac540920267a40b
|
||||
246deb286c9ecb0025dede808465f85d6a5e75aca61088533b306d8646c92c
|
||||
~~~
|
||||
|
||||
## OPRF(P-521, SHA-512)
|
||||
|
||||
### Base Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
skSm = 016ee706f30ce7e15e4ffa3114c7d59a7b6f302d531ca60419be39d1cd43e
|
||||
e13b1fc8398b7f63a900cdc49c6e99f65a74403db2fa739927a2ee288cff857d9d84
|
||||
ecf
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 01b983705fcc9a39607288b935b0797ac6b3c4b2e848823ac9ae16b3a3b5
|
||||
816be03432370deb7c3c17d9fc7cb4e0ce646e04e42d638e0fa7a434ed340772a8b5
|
||||
d626
|
||||
BlindedElement = 0301f0a8c68e58f5571bd39fe3b0b2aa055a8c34e3d68ba0d2e
|
||||
d177db0bc7575d477ed8f557596feb5ac568fe738eee8cff7dcb56dc78f52bf381c0
|
||||
912e0e84b5a3f5b
|
||||
EvaluationElement = 0200d7b1131aa9f8c365de7bd7903738f61bdecfaada375a
|
||||
ba3905bdaad1301c7cd537f69abff04140ccca29a4c46cb4a036160e55a9621210b3
|
||||
71d84646b0199571fa
|
||||
Output = 61eea8fedfa9338dd22fac279f1f3f9e96693919c59ea3918c7a441115e
|
||||
6bdecb1d05b5da55d4024858c92d3911a81d4eca362123b2911e5dc58591bf7be29c
|
||||
7
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 01a03b1096b0316bc8567c89bd70267d35c8ddcb2be2cdc867089a2eb5cf
|
||||
471b1e6eb4b043b9644c8539857abe3a2022e9c9fd6a1695bbabe8add48bcd149ff3
|
||||
b841
|
||||
BlindedElement = 030099c35342a43221c6e03debfb17bad71b62e04c9242aa6e9
|
||||
f2f915163ef4f5b8b7fe1740a4d636c36bd5c73ca39c69992dc7f6dff8f232125efc
|
||||
22af4df8352fea2
|
||||
EvaluationElement = 0300ceeba6751486eecc479ab2259e3a57c13b0710f61c82
|
||||
87acad60624974b76ea242dbcae3a9daad1bdc9c49012c8d8b384d510980cc1ef8fa
|
||||
8d10502748ce63d93f
|
||||
Output = 6682273a5199b2454a706cac557008e2264580ac39b6995e1f47130b985
|
||||
d1015de7713d3bdb121212a68de2ece73bf72e41738a01c23428753c44e3dd39b5de
|
||||
3
|
||||
~~~
|
||||
|
||||
### Verifiable Mode
|
||||
|
||||
~~~
|
||||
seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
|
||||
3a3
|
||||
skSm = 0017674057e06c5e3e8a331f2dc3558540701c9cd0f4c19126d5972af6a01
|
||||
447b312d05a06dab3e9e07c891d749444c27ede0897ad42aea03b887eb5db93e3f29
|
||||
a86
|
||||
pkSm = 0201ee4e2eaa74728f577f4bb282c5440cd454fdee1d79b15a36d34b5e5a1
|
||||
25e3ccc0f99e32cc0a6a15b5652a0c8a424860c6753f685d0e1e150ceba24ca3386f
|
||||
29216
|
||||
~~~
|
||||
|
||||
#### Test Vector 1, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 00
|
||||
Info = 7465737420696e666f
|
||||
Blind = 00bbb82117c88bbd91b8954e16c0b9ceed3ce992b198be1ebfba9ba970db
|
||||
d75beefbfc6d056b7f7ba1ef79f4facbf2d912c26ce2ecc5bb8d66419b379952e96b
|
||||
d6f5
|
||||
BlindedElement = 0200357f949a0a0bdfeb682734dbdeb778f3845045617b21436
|
||||
27753332e2e75458ab183b12635c75e19afaf56981e7755803026842db1b22fa42c8
|
||||
61413d07ff86545
|
||||
EvaluationElement = 0201d636bac3f77c1091b337daae32259a3eacd57e3c0fb1
|
||||
444fe5ce22af6acdcef4a46a2b5e169aa8d0e26ec2a3621c15dd366ba1978dae761c
|
||||
1ef3dac63c60cbee88
|
||||
Proof = 011ebe27ebc79e5679b643c6b3a51333499c7abee86c092181c0a8e7e539
|
||||
e0ba30b1c128666708c753696ace2aa789c4975b0b80d6241a1dafe85c39a7338d1e
|
||||
20d00131c8a81b5f64209f8fe53e8c6a00789a893f20596198e2521275e05d925298
|
||||
08e9f54030fc8be2ce78c6df0d29e6fd7d8e623e0ccc7b19b194493dacd2a4eb3a32
|
||||
ProofRandomScalar = 00ce4f0d824939827888f4c28773466f3c0a05741260040b
|
||||
c9f302a4fea13f1d8f2f6b92a02a32d5eb06f81de7960470f06169bee12cf47965b7
|
||||
2a59946ca3879670
|
||||
Output = c51295e2a03ba59f1538734316e0d70dd81f95daba2f7b5ac4906c56ce8
|
||||
79d6cef8f583433c981a182a52dd568811b073f65fc1124941f344cc9dd3b3880f29
|
||||
5
|
||||
~~~
|
||||
|
||||
#### Test Vector 2, Batch Size 1
|
||||
|
||||
~~~
|
||||
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 009055c99bf9591cb0eab2a72d044c05ca2cc2ef9b609a38546f74b6d688
|
||||
f70cf205f782fa11a0d61b2f5a8a2a1143368327f3077c68a1545e9aafbba6a90dc0
|
||||
d40a
|
||||
BlindedElement = 030185e431f056e75ba7fac49da70790031daa333d16f05e1de
|
||||
471e24afe0ed985c770ce77bd1bebec527e9a76feecc6afd92c5fd00481ba7fb843d
|
||||
2aab52337cb716e
|
||||
EvaluationElement = 02000859e1abc2ed28086b854ec5ae72311244fdeedf81d7
|
||||
69af6a6f2c83f00fa48df1f1a0c0b6fac84cc654b7757ac042107a6b3043e483bb3b
|
||||
74de5d6c301b20e8f6
|
||||
Proof = 01629dd5af14c7414801d879b1018ce06bcc5c5d0a64ca422b76aaa531c8
|
||||
ecca630919fb4b51fa60fdc215f73e67e8d617d55ca6a227343d434d5e0f487567f8
|
||||
5bfa016959443267bb7d9a5c5e5b1c4d20026394b4edaca7dfbc1aa3b3c2020cf995
|
||||
79cf276c0e84f0cb5a820226fa3b81d42de2db39d8412642e70428e485a61ee9d760
|
||||
ProofRandomScalar = 00b5dfc19eb96faba6382ec845097904db87240b9dd47b1e
|
||||
487ec625f11a7ba2cc3de74c5078a81806f74dd65065273c5bd886c7f87ff8c5f39f
|
||||
90320718eff747e3
|
||||
Output = 7462f460340a52f7b7609c5e1c5e2d5334d43da7631cb549bb65163a05d
|
||||
1b2e936669e52e66c92da4b2e24fff3c118c62787577c01d2885567b476c13011057
|
||||
1
|
||||
~~~
|
||||
|
||||
#### Test Vector 3, Batch Size 2
|
||||
|
||||
~~~
|
||||
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
|
||||
Info = 7465737420696e666f
|
||||
Blind = 01c6cf092d80c7cf2cb55388d899515238094c800bdd9c65f71780ba85f5
|
||||
ae9b4703e17e559ca3ccd1944f9a70536c175f11a827452672b60d4e9f89eba28104
|
||||
6e29,00cba1ba1a337759061965a423d9d3d6e1e1006dc8984ad28a4c93ecfc36fc2
|
||||
171046b3c4284855cfa2434ed98db9e68a597db2c14728fade716a6a82d600444b26
|
||||
e
|
||||
BlindedElement = 0301978860af75cd69acbc93e8c9fc530e5d2b2208da42c65bf
|
||||
e079f0f6e0b3fc6080556c10739271d2a8fe578409d4fa9b19ef0484d9c15451c4e7
|
||||
0501e31da7608cb,0200e30565c3d7e02c822762f25db4c872811adb2cbfbad92b04
|
||||
291bc8c476d0546d1c5ecf5c58ff06b8d19aad8eca9e5f1a80ff8e981ebc490b0cfb
|
||||
d5d499b47bad8e
|
||||
EvaluationElement = 0300abdee910f144c3be460e724c11626e1f9986f72e2c43
|
||||
3a9c4dad2ef6fcb9249c9a5036334ba88b0892462b6f8ad419c38cc259b0c774a9bd
|
||||
0c4d545d0914413ea2,02019696f91dcc178bbe6b97f822cdc4052f9b94852ff6023
|
||||
f6068848f867df40e54a5f1525e7fafa383e82fe36bf3c74427b51903032d0f89876
|
||||
05bf24ee003f37693
|
||||
Proof = 008fa896b69c1efc4e9c6bdfd0b149444532d5ba3bfd957cf7cd71c374d3
|
||||
a1cca25f17b60616164377b0734243bc878e17d3ecab36b3e3565b5c6218dae92d40
|
||||
c0be018707381f6f4b0153044737030b5d9851c15609532da8932c1fa1f4901dba05
|
||||
a4118d25142344f9ea1465c907eb13d908a45d8b98265eac48819a04cae859b0643a
|
||||
ProofRandomScalar = 00d47b0d4ca4c64825ba085de242042b84d9ebe3b2e9de07
|
||||
678ff96713dfe16f40f2c662a56ed2db95e1e7bf2dea02bd1fa76e953a630772f68b
|
||||
53baade9962d1646
|
||||
Output = c51295e2a03ba59f1538734316e0d70dd81f95daba2f7b5ac4906c56ce8
|
||||
79d6cef8f583433c981a182a52dd568811b073f65fc1124941f344cc9dd3b3880f29
|
||||
5,7462f460340a52f7b7609c5e1c5e2d5334d43da7631cb549bb65163a05d1b2e936
|
||||
669e52e66c92da4b2e24fff3c118c62787577c01d2885567b476c130110571
|
||||
~~~
|
||||
"#;
|
||||
+634
-832
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
[formatting]
|
||||
allowed_blank_lines = 1
|
||||
reorder_keys = true
|
||||
Reference in New Issue
Block a user